Update to 0.24.7.
authorjbj <devnull@localhost>
Fri, 29 Oct 2004 13:34:35 +0000 (13:34 +0000)
committerjbj <devnull@localhost>
Fri, 29 Oct 2004 13:34:35 +0000 (13:34 +0000)
CVS patchset: 7522
CVS date: 2004/10/29 13:34:35

253 files changed:
neon/.cvsignore
neon/.release.sh
neon/.version
neon/BUGS
neon/ChangeLog
neon/Makefile.in
neon/NEWS
neon/THANKS
neon/TODO
neon/autogen.sh
neon/configure.in
neon/doc/biblio.xml [new file with mode: 0644]
neon/doc/date.xml [new file with mode: 0644]
neon/doc/fdl.sgml [new file with mode: 0644]
neon/doc/feat.xml [new file with mode: 0644]
neon/doc/html.xsl
neon/doc/html/apas01.html [new file with mode: 0644]
neon/doc/html/apas02.html [new file with mode: 0644]
neon/doc/html/apas03.html [new file with mode: 0644]
neon/doc/html/apas04.html [new file with mode: 0644]
neon/doc/html/apas05.html [new file with mode: 0644]
neon/doc/html/apas06.html [new file with mode: 0644]
neon/doc/html/apas07.html [new file with mode: 0644]
neon/doc/html/apas08.html [new file with mode: 0644]
neon/doc/html/apas09.html [new file with mode: 0644]
neon/doc/html/apas10.html [new file with mode: 0644]
neon/doc/html/apas11.html [new file with mode: 0644]
neon/doc/html/apas12.html [new file with mode: 0644]
neon/doc/html/api.html [new file with mode: 0644]
neon/doc/html/biblio.html [new file with mode: 0644]
neon/doc/html/compliance.html [new file with mode: 0644]
neon/doc/html/features.html [new file with mode: 0644]
neon/doc/html/gfdl.html [new file with mode: 0644]
neon/doc/html/index.html [new file with mode: 0644]
neon/doc/html/intro.html [new file with mode: 0644]
neon/doc/html/ref.html [new file with mode: 0644]
neon/doc/html/refalloc.html [new file with mode: 0644]
neon/doc/html/refauth.html [new file with mode: 0644]
neon/doc/html/refbuf.html [new file with mode: 0644]
neon/doc/html/refbufapp.html [new file with mode: 0644]
neon/doc/html/refbufcr.html [new file with mode: 0644]
neon/doc/html/refbufdest.html [new file with mode: 0644]
neon/doc/html/refbufutil.html [new file with mode: 0644]
neon/doc/html/refcert.html [new file with mode: 0644]
neon/doc/html/refclicert.html [new file with mode: 0644]
neon/doc/html/refconfig.html [new file with mode: 0644]
neon/doc/html/referr.html [new file with mode: 0644]
neon/doc/html/refgetst.html [new file with mode: 0644]
neon/doc/html/refiaddr.html [new file with mode: 0644]
neon/doc/html/refneon.html [new file with mode: 0644]
neon/doc/html/refopts.html [new file with mode: 0644]
neon/doc/html/refreq.html [new file with mode: 0644]
neon/doc/html/refreqbody.html [new file with mode: 0644]
neon/doc/html/refreqhdr.html [new file with mode: 0644]
neon/doc/html/refresolve.html [new file with mode: 0644]
neon/doc/html/refsess.html [new file with mode: 0644]
neon/doc/html/refshave.html [new file with mode: 0644]
neon/doc/html/refsockinit.html [new file with mode: 0644]
neon/doc/html/refsslca.html [new file with mode: 0644]
neon/doc/html/refsslcert2.html [new file with mode: 0644]
neon/doc/html/refsslcertio.html [new file with mode: 0644]
neon/doc/html/refssldname.html [new file with mode: 0644]
neon/doc/html/refsslvfy.html [new file with mode: 0644]
neon/doc/html/refstatus.html [new file with mode: 0644]
neon/doc/html/reftok.html [new file with mode: 0644]
neon/doc/html/refvers.html [new file with mode: 0644]
neon/doc/html/using.html [new file with mode: 0644]
neon/doc/html/xml.html [new file with mode: 0644]
neon/doc/man/ne_add_request_header.3 [new file with mode: 0644]
neon/doc/man/ne_addr_destroy.3 [new file with mode: 0644]
neon/doc/man/ne_addr_error.3 [new file with mode: 0644]
neon/doc/man/ne_addr_first.3 [new file with mode: 0644]
neon/doc/man/ne_addr_next.3 [new file with mode: 0644]
neon/doc/man/ne_addr_resolve.3 [new file with mode: 0644]
neon/doc/man/ne_addr_result.3 [new file with mode: 0644]
neon/doc/man/ne_buffer.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_altered.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_append.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_clear.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_concat.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_create.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_destroy.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_finish.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_grow.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_ncreate.3 [new file with mode: 0644]
neon/doc/man/ne_buffer_zappend.3 [new file with mode: 0644]
neon/doc/man/ne_calloc.3 [new file with mode: 0644]
neon/doc/man/ne_close_connection.3 [new file with mode: 0644]
neon/doc/man/ne_forget_auth.3 [new file with mode: 0644]
neon/doc/man/ne_get_error.3 [new file with mode: 0644]
neon/doc/man/ne_get_status.3 [new file with mode: 0644]
neon/doc/man/ne_iaddr_cmp.3 [new file with mode: 0644]
neon/doc/man/ne_iaddr_free.3 [new file with mode: 0644]
neon/doc/man/ne_iaddr_make.3 [new file with mode: 0644]
neon/doc/man/ne_iaddr_print.3 [new file with mode: 0644]
neon/doc/man/ne_malloc.3 [new file with mode: 0644]
neon/doc/man/ne_oom_callback.3 [new file with mode: 0644]
neon/doc/man/ne_print_request_header.3 [new file with mode: 0644]
neon/doc/man/ne_qtoken.3 [new file with mode: 0644]
neon/doc/man/ne_realloc.3 [new file with mode: 0644]
neon/doc/man/ne_request_create.3 [new file with mode: 0644]
neon/doc/man/ne_request_destroy.3 [new file with mode: 0644]
neon/doc/man/ne_request_dispatch.3 [new file with mode: 0644]
neon/doc/man/ne_session_create.3 [new file with mode: 0644]
neon/doc/man/ne_session_destroy.3 [new file with mode: 0644]
neon/doc/man/ne_session_proxy.3 [new file with mode: 0644]
neon/doc/man/ne_set_error.3 [new file with mode: 0644]
neon/doc/man/ne_set_expect100.3 [new file with mode: 0644]
neon/doc/man/ne_set_persist.3 [new file with mode: 0644]
neon/doc/man/ne_set_proxy_auth.3 [new file with mode: 0644]
neon/doc/man/ne_set_read_timeout.3 [new file with mode: 0644]
neon/doc/man/ne_set_request_body_buffer.3 [new file with mode: 0644]
neon/doc/man/ne_set_request_body_fd.3 [new file with mode: 0644]
neon/doc/man/ne_set_server_auth.3 [new file with mode: 0644]
neon/doc/man/ne_set_useragent.3 [new file with mode: 0644]
neon/doc/man/ne_shave.3 [new file with mode: 0644]
neon/doc/man/ne_sock_init.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_cmp.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_export.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_free.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_identity.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_import.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_issuer.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_read.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_signedby.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_subject.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_cert_write.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_clicert_decrypt.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_clicert_encrypted.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_clicert_free.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_clicert_name.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_clicert_owner.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_clicert_read.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_dname_cmp.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_readable_dname.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_set_verify.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_trust_cert.3 [new file with mode: 0644]
neon/doc/man/ne_ssl_trust_default_ca.3 [new file with mode: 0644]
neon/doc/man/ne_status.3 [new file with mode: 0644]
neon/doc/man/ne_strdup.3 [new file with mode: 0644]
neon/doc/man/ne_strndup.3 [new file with mode: 0644]
neon/doc/man/ne_token.3 [new file with mode: 0644]
neon/doc/man/ne_version_match.3 [new file with mode: 0644]
neon/doc/man/ne_version_string.3 [new file with mode: 0644]
neon/doc/man/neon-config.1 [new file with mode: 0644]
neon/doc/man/neon.3 [new file with mode: 0644]
neon/doc/manual.css
neon/doc/manual.xml
neon/doc/ref/buf.xml
neon/doc/ref/clicert.xml [new file with mode: 0644]
neon/doc/ref/config.xml [new file with mode: 0644]
neon/doc/ref/getst.xml
neon/doc/ref/iaddr.xml [new file with mode: 0644]
neon/doc/ref/neon.xml
neon/doc/ref/req.xml
neon/doc/ref/resolve.xml
neon/doc/ref/sess.xml
neon/doc/ref/shave.xml
neon/doc/ref/sslca.xml [deleted file]
neon/doc/ref/sslcert.xml
neon/doc/ref/sslcert2.xml [new file with mode: 0644]
neon/doc/ref/sslcertio.xml [new file with mode: 0644]
neon/doc/ref/ssldname.xml
neon/doc/ref/ssltrust.xml [new file with mode: 0644]
neon/doc/ref/sslvfy.xml
neon/doc/ref/status.xml
neon/doc/ref/vers.xml
neon/doc/ssl.xml [new file with mode: 0644]
neon/doc/using.xml [new file with mode: 0644]
neon/doc/version.xml [new file with mode: 0644]
neon/doc/xml.xml [new file with mode: 0644]
neon/macros/ChangeLog
neon/macros/neon-test.m4
neon/macros/neon-xml-parser.m4
neon/macros/neon.m4
neon/neon-config.in
neon/neon.mak
neon/neon.pc.in [new file with mode: 0644]
neon/src/.cvsignore
neon/src/ChangeLog
neon/src/Makefile.in
neon/src/memleak.h [new file with mode: 0644]
neon/src/ne_207.c
neon/src/ne_207.h
neon/src/ne_alloc.c
neon/src/ne_alloc.h
neon/src/ne_auth.c
neon/src/ne_auth.h
neon/src/ne_basic.c
neon/src/ne_compress.c
neon/src/ne_cookies.c
neon/src/ne_dates.c
neon/src/ne_defs.h
neon/src/ne_i18n.c
neon/src/ne_locks.c
neon/src/ne_md5.c
neon/src/ne_md5.h
neon/src/ne_openssl.c [new file with mode: 0644]
neon/src/ne_private.h
neon/src/ne_privssl.h [new file with mode: 0644]
neon/src/ne_props.c
neon/src/ne_props.h
neon/src/ne_redirect.c
neon/src/ne_redirect.h
neon/src/ne_request.c
neon/src/ne_request.h
neon/src/ne_session.c
neon/src/ne_session.h
neon/src/ne_socket.c
neon/src/ne_socket.h
neon/src/ne_ssl.h [new file with mode: 0644]
neon/src/ne_string.c
neon/src/ne_string.h
neon/src/ne_stubssl.c [new file with mode: 0644]
neon/src/ne_uri.c
neon/src/ne_utils.c
neon/src/ne_utils.h
neon/src/ne_xml.c
neon/src/ne_xml.h
neon/src/sslcerts.c [deleted file]
neon/test/.cvsignore
neon/test/ChangeLog
neon/test/Makefile.in
neon/test/acl.c
neon/test/auth.c
neon/test/basic.c
neon/test/common/.cvsignore [new file with mode: 0644]
neon/test/common/ChangeLog
neon/test/common/child.c
neon/test/common/tests.c
neon/test/common/tests.h
neon/test/compress.c
neon/test/cookies.c
neon/test/lock.c
neon/test/makekeys.sh
neon/test/openssl.conf
neon/test/props.c
neon/test/redirect.c
neon/test/regress.c [deleted file]
neon/test/request.c
neon/test/resolve.c
neon/test/run.sh
neon/test/session.c
neon/test/skeleton.c
neon/test/socket.c
neon/test/ssl.c
neon/test/string-tests.c
neon/test/stubs.c
neon/test/uri-tests.c
neon/test/util-tests.c
neon/test/utils.c
neon/test/utils.h
neon/test/xml.c

index ba4ab4f..0522756 100644 (file)
@@ -3,7 +3,20 @@ config.h
 configure
 config.status
 config.log
+conftest.c
 Makefile
 aclocal.m4
-nget
-config.cache
+*.cache
+libtool
+neon-config
+reconf*
+confdefs.h
+ltmain.sh
+ltconfig
+config.sub
+config.guess
+*.out
+*.log
+.version
+config.hw
+neon.pc
index 78cdcc5..a278217 100755 (executable)
@@ -1,9 +1,18 @@
 #!/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"`
+
+set -ex
+
+major=`echo $1 | awk -F. '{print $1;}'`
+minor=`echo $1 | awk -F. '{print $2;}'`
+rel=`echo $1 | awk -F. '{print $3;}'`
 version=$1
 
+# check that release version matches build version!
+grep ^NEON_VERSION_MAJOR=$major\$ macros/neon.m4 || exit 1
+grep ^NEON_VERSION_MINOR=$minor\$ macros/neon.m4 || exit 1
+grep ^NEON_VERSION_RELEASE=$rel\$ macros/neon.m4 || exit 1
+grep '^NEON_VERSION_TAG=$' macros/neon.m4 || exit 1
+
 for f in config.hw; do
 in=$f.in
 out=$f
@@ -12,3 +21,15 @@ sed -e "s/@VERSION@/$version/g" \
     -e "s/@MINOR@/$minor/g" \
     -e "s/@RELEASE@/$release/g" < $in > $out
 done
+
+echo $1 > .version
+
+# for the documentation:
+date +"%e %B %Y" | tr -d '\n' > doc/date.xml
+echo -n $1 > doc/version.xml
+
+# Try to create a valid Makefile
+tmp=`mktemp /tmp/neon-XXXXXX`
+sed -e "s/@SET_MAKE@//g" -e "s|@SHELL@|/bin/sh|g" < Makefile.in > $tmp
+make -f $tmp docs
+rm -f $tmp
index 379191a..c9731ff 100644 (file)
@@ -1 +1 @@
-0.23.7
+0.24.7
index 5a4e3e2..2ae9ca5 100644 (file)
--- a/neon/BUGS
+++ b/neon/BUGS
@@ -1,7 +1,28 @@
 
-Known problems/bugs in neon                                        -*- text -*-
----------------------------      Id: BUGS,v 1.2 2000/07/27 20:59:19 joe Exp 
+Known problems/bugs in neon                                      -*- text -*-
+---------------------------
 
-1.  Each new SSL request is opening a new connection to the server.
-    VERY slow.
+* 2818 requires that a on rejection of the SSL server cert, a "bad certificate"
+ message should be sent - this is not being done currently (and can probably
+ only be done with OpenSSL by actually doing cert verification in the verify
+ callback)
 
+* ne_lock_discover does not handle multiple (shared) locks on
+  a single resource.
+
+* SSL session caching issues; only cache for clean shutdowns, and
+ only cache on shutdown, since the SSL_SESSION may change during
+ an ne_session.
+
+* what is passed as 'path' to req create hook: auth needs Request-URI;
+  how does that interact with proxies?  also they will be passed NULL
+  for a CONNECT request, or "*" possibly as well.
+
+* expect100 support is broken.
+
+* It would be nice to fail with a friendly error message if a client
+cert is requested by the srever but one is not provided.  Currently,
+returning -1 from the provide_client_cert function would allow that
+(as it forces the SSL handshake to fail), but that would prevent
+opportunistic use of client certificates, of the "SSLVerifyClient
+optional" variety.
index 3481156..bb1a691 100644 (file)
@@ -1,3 +1,672 @@
+Mon May 17 21:25:44 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak: Fix handling of paths with spaces, and simplify (Jon
+       Foster <jon@jon-foster.co.uk>).
+
+Thu May 13 11:42:07 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Don't rely on echo -n in ne_version.
+
+Fri Mar 26 12:07:04 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (distclean): Clean more.
+
+Sat Oct 25 10:37:59 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Use AC_LIBTOOL_TAGS if available.
+
+Mon Sep 22 20:56:21 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Respect @datadir@ from configure (Max Bowsher).
+
+Sun Sep 14 11:13:36 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Run NEON_TEST before LIBNEON_SOURCE_CHECKS, to
+       help prevent time_t format string detection failing due to changed
+       CFLAGS.
+
+Fri Jun 20 17:51:05 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in, neon-config.in: Don't pass user-supplied CPPFLAGS
+       through to neon-config, it is no longer necessary.
+
+Sun Apr  6 19:51:31 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * doc/manual.xml: Include clicert reference docs.
+
+       * doc/ref/clicert.xml: New file.
+
+Sun Apr  6 19:24:47 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (XMLTO): New variable.
+       (docs-man, docs-pdf, docs-ps, docs-html): Use $(XMLTO) variable.
+
+Wed Mar 26 20:09:12 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (DIST_HEADERS): Add ne_tls.h
+
+       * neon.mak: Build ne_openssl.obj for SSL build, ne_stubssl.obj for
+       non-SSL build.
+
+Sun Mar  9 10:38:36 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Set ALLOW_INSTALL=yes early to allow later
+       overrides.
+
+       * Makefile.in (install-yes): Remove dependence on subdirs.
+       (install-lib): Depend on subdirs; don't install neon.pc.
+       (install-config): Install neon.pc here. 
+
+Sat Mar  1 21:50:17 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (cover): New target.
+
+Sat Mar  1 20:39:32 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Add --enable-memleak argument; if enabled, have
+       config.h include memleak.h, add top_srcdir/src to include path,
+       and substitute ALLOW_INSTALL as 'memleak'.  Otherwise, substitute
+       ALLOW_INSTALL as 'yes'.
+
+       * Makefile.in (install): Use install-@ALLOW_INSTALL@.
+       (install-yes): Renamed from install.
+       (install-memleak): Prevent installation with non-standard ABI.
+
+Fri Dec 27 15:15:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.pc.in: New file.
+
+       * configure.in: Generate neon.pc.
+       
+       * Makefile.in (install-lib): Install neon.pc
+
+Tue Nov 19 11:24:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Use NEON_TEST before NEON_XML_PARSER to prevent
+       problems if NEON_XML_PARSER adds anything to CPPFLAGS which breaks
+       gcc -Werror (e.g. -I/usr/local/include).
+
+Sat Sep 21 12:29:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-config.in: Add support for `--la-file' argument, to print
+       location of libtool .la file.
+
+Sat Sep 14 12:46:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Add AC_COPYRIGHT, use AC_MSG_NOTICE for
+       post-configure message, update for modern use of AC_CONFIG_FILES
+       and AC_OUTPUT.
+
+Thu Aug 29 23:49:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Further Win32 updates from Blair Zajac:
+       
+       * config.hw.in: Define HAVE_ERRNO_H; fix non-SSL build.
+
+       * neon.mak: Fix to build DAV sources if EXPAT_FLAGS is set but not
+       EXPAT_SRC.
+
+Sun Aug 25 23:37:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Win32 updates from Blair Zajac <blair@orcaware.com>:
+       
+       * neon.mak: Fix expansion of $(LIB32_OBJS) variable that was
+       causing nmake failures.  Allow non-DAV build if EXPAT_SRC is not
+       specified.
+
+       * config.hw.in: Remove hard-coded defines.
+
+       * INSTALL.win32: Overhaul.
+
+Sat Aug 10 10:42:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Substitue OPENSSL into Makefiles as an absolute
+       path.
+
+Fri Aug  9 20:45:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Update to use modern three-argument AC_INIT and
+       AC_CONFIG_SRCDIR.  Pick up version string from .version; thanks to
+       Greg Stein for they `esyscmd' trick.  Move NEON_WITH_LIBS earlier.
+       Use AC_HELP_STRING for --disable-webdav.
+
+Fri Aug  9 20:41:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * config.hw.in, neon.mak, INSTALL.win32: Win32 build update from
+       Gerald Richter: add optional NEON_NODAV and OPENSSL_STATIC flags,
+       update locations of includes and libraries.
+
+Sun Jun 30 11:08:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak: Enable WIN32_LEAN_AND_MEAN etc, to prevent X509_NAME
+       definition conflicting with OpenSSL headers in recent versions of
+       Platform SDK (Branko Ãˆibej).
+
+Thu Jun 13 20:35:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Call NEON_WITH_LIBS for --with-libs option.
+
+Tue Jun  4 13:27:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak: Always build ne_compress.obj; patch from Branko Ãˆibej.
+
+Tue Jun  4 09:38:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (man1dir, man3dir): Use $(mandir).  (Rodney Dawes)
+
+Mon Jun  3 20:47:14 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (install-html, install-man): Fix for VPATH builds.
+
+Sat May 25 15:01:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Only enable SSL tests if an `openssl' binary is
+       found in $PATH.
+
+Thu May 23 20:31:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Require a release version of autoconf.
+
+Sat May 18 14:43:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (docs-man): Use customisation layer.
+
+Sat Apr 13 22:34:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in, neon-config.in: Pass LDFLAGS and CPPFLAGS setting
+       given to configure through to neon-config --libs/--cflags output.
+
+Mon Feb 25 20:53:28 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak (OPENSSL_FLAGS): Define NEON_SSL not ENABLE_SSL
+       (Branko).
+
+Mon Feb 25 20:46:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * config.hw.in: Add NE_FMT_OFF_T (Dan Berlin, Branko Ãˆibej).
+
+Sun Feb 10 20:35:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (docs-html): Use XSL customisation layer for the
+       HTML output.
+
+Wed Feb  6 00:42:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Require autoconf 2.52d for AC_C_BIGENDIAN which
+       supports cross-compiling.
+
+Sat Jan 26 11:19:39 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Define TESTS and HELPERS appropriately for whether
+       DAV, SSL, zlib are supported.
+
+Sat Jan 26 11:03:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak: Add OpenSSL, zlib support (Branko Ãˆibej).
+
+Sat Jan 26 00:15:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.dsp, neon.dsw: Removed per advice from Branko.
+
+Thu Jan 24 21:02:02 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (check): Run check target in test subdir.
+       (subdirs): Remove redundant subshell.
+       (distclean): Clean harder.
+
+Thu Jan 24 20:46:28 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak: Remove sslcerts.c from build.
+
+Sun Jan 20 12:51:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Create test/common for vpath build.
+
+Tue Jan  8 21:35:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak: Rewritten by Branko Ãˆibej <brane@xbc.nu>.
+
+       * config.hw.in: Update from Branko Ãˆibej.
+
+Thu Jan  3 08:48:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (neon-config): Add target.
+
+Thu Jan  3 08:47:06 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-config.in (libs): Don't print -L/usr/lib or -L/lib.
+
+Thu Jan  3 08:43:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Make neon-config executable in the build tree.
+
+Mon Dec 17 22:54:00 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (install-lib, install-headers, install-config):
+       Support DESTDIR; patch from Pawel Golaszewski <ues@ds.pg.gda.pl>.
+
+Sat Oct 27 12:23:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-config.in: Add "--support FEATURE" option; the script exits
+       with success if given FEATURE is supported.
+
+Sat Oct  6 13:11:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Require autoconf 2.50. Use AC_HELP_STRING.
+
+Sun Sep 30 23:44:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in, neon-config.in: Clean up handling of cflags/libs
+       exporting.
+
+Sat Sep 29 12:45:25 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Add Makefile re-generation target, have clean
+       recurse into test too, have check depend on subdirs, distribute
+       ne_compress.h.
+
+Tue Sep 25 07:34:32 2001  Mo DeJong  <supermo@bayarea.net>
+
+       * configure.in: Move check for signal.h into
+       LIBNEON_SOURCE_CHECKS.
+
+Mon Sep 24 20:28:26 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * autogen.sh: Bomb out on errors (set -e).
+
+Mon Sep 24 20:20:08 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * autogen.sh: Clean up, use libtoolize.
+
+       * ltmain.sh: Removed from CVS.
+
+Mon Sep 24 20:17:18 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Use three-argument AC_DEFINE for _GNU_SOURCE (Mo
+       DeJong).
+
+Tue Sep 11 23:20:23 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * config.hw.in: Define USE_DAV_LOCKS.
+
+Mon Aug 13 21:07:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.mak: Add support for SSL (Peter Boos).
+
+Sat Jun 30 12:22:17 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Add NEONLIBS to LIBS, so that the depedencies are
+       added to the link line.  This means they are picked up as
+       references in the .so, and also listed in the libtool .la file.
+
+Tue Jun 12 13:02:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * config.hw.in: Renamed from config.hw, and have version
+       substituted in when tarball is rolled. Adjust for XML parser
+       changes. (Gerald Richter)
+
+Sun Jun 10 16:41:46 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Check for pipe() for tests, use NEON_DEBUG.
+
+Fri Jun  8 22:57:24 2001  Joe Orton  <joe@manyfish.co.uk>
+       
+       * config.hw: Fixes from Gerald Richter <richter@ecos.de>.
+
+       * neon.mak, INSTALL.win32: New files from Gerald Richter.
+
+Thu May 31 00:00:32 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Disable shared library build by default.  Use
+       NE_DEBUGGING rather than DEBUGGING.  Check for 'usleep' for tests.
+
+Sun Apr 29 16:41:17 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Produce test/Makefile.
+
+Wed Mar 14 22:51:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (DIST_HEADERS): Add http_auth.h
+
+Wed Mar 14 22:45:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Build bundled directory regardless of
+       --disable-webdav.
+
+Sun Feb 25 17:00:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Rename NEON_IS_BUNDLED to NEON_BUILD_BUNDLED.
+
+Sun Feb 25 16:53:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Set NEON_IS_BUNDLED to "yes".
+
+Sat Feb 24 00:09:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Add --disable-webdav flag to disable WebDAV
+       support.  Set NEON_NEED_XML_PARSER=yes if DAV is not disabled.
+
+Sun Jan 28 23:10:39 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Remove neon_config.h from DIST_HEADERS.
+
+Sun Jan 28 10:41:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * configure.in: Don't produce neon_config.h.
+
+Tue Jan 23 23:16:25 2001  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Moved version defns into NEON_VERSIONS macros.
+       Produce src/neon_config.h.
+
+Tue Jan 16 20:16:47 2001  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Bumped version to 0.10.1.
+
+Mon Jan 15 22:59:47 2001  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Bumped version to 0.10.0.
+
+Sun Jan 14 23:55:47 2001  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in (distclean): Don't remove neon_config.h.  (install):
+       Depend on subdirs.
+
+Wed Jan 10 22:46:53 2001  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Use NEON_LINK_FLAGS to pass through -version-info
+       and interface version flags.  Add -I${top_builddir} to CFLAGS.
+       Remove NEONOBJS declaration, let NEON_LIBTOOL_BUILD do it.
+
+Wed Dec 20 00:11:56 2000  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Bumped version to 0.9.1, interface version to
+       9:1:0.
+
+Tue Dec 19 22:15:45 2000  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in: Build using recursive make into src directory.  Add
+       VPATH support. (Mo DeJong <mdejong@cygnus.com>)
+
+Tue Dec 19 22:13:40 2000  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Build using new src/Makefile.in.
+
+Sun Dec 17 19:53:36 2000  Joe Orton  <joe@light.plus.com>
+
+       * config.sub, config.guess, ltconfig, ltmain.sh: Update to libtool
+       1.3.5 versions.
+
+Sun Dec 17 18:43:00 2000  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in: Remove all traces of example programs.  Fix
+       uritest.
+
+Thu Dec 14 21:47:48 2000  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Bumped version to 0.8.0, interface version to
+       8:0:0.
+
+Thu Dec 14 20:57:49 2000  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Removed configuration of examples.
+
+Wed Dec 13 20:14:53 2000  Joe Orton  <joe@light.plus.com>
+
+       * config.hw: Added for Windows.
+
+Sun Nov 26 09:52:29 2000  Joe Orton  <joe@light.plus.com>
+
+       * example/: Removed directory (now in separate neon-examples
+       package).
+
+Tue Sep 12 10:33:50 2000  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in: Remove sitecopy bits which somehow got left in
+       there.
+
+Tue Sep 12 00:41:49 2000  Joe Orton  <joe@light.plus.com>
+
+       * configure.in: Bumped version to 0.7.3, interface version to
+       7:1:1.
+
+Tue Sep 12 00:39:49 2000  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in (distclean): New target.
+
+Thu Sep  7 00:14:15 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped version to 0.7.2, interface version to
+       7:0:1.
+
+Thu Sep  7 00:10:54 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in: Added 'all' target (patch by Greg Stein
+       <gstein@lyra.org>).
+
+Sun Sep  3 10:32:09 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in (SHELL): Added definition.  Changed xalloc.h to
+       ne_alloc.h in DIST_HEADERS. Thanks to Eric Mumpower
+       <nocturne@arepa.com>.
+
+Tue Aug 15 21:53:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped version to 0.7.1, interface version to
+       6:1:1.
+
+Mon Aug 14 09:28:47 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped version to 0.7.0, interface version to
+       6:0:0.
+
+Sun Aug 13 15:59:58 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped version to 0.6.1, interface version to
+       5:1:1.
+
+Sat Aug 12 17:10:09 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped version to 0.6.0, interface version to
+       5:0:1.
+
+Sat Aug 12 17:08:54 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Add --with-extra-includes and --with-extra-libs
+       configure parameters
+
+Sat Aug 12 17:07:22 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (redirect_notify): New function.  (main): Support
+       automatic redirects.
+
+Sat Aug 12 16:53:50 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in: Build http_redirect.lo.
+
+Sat Aug 12 14:43:28 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (pretty_progress_bar): Use 'off_t' not size_t
+       arguments.
+
+Sat Aug 12 02:11:05 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped interface version to 4:1:0, version to
+       0.5.1.
+
+Fri Aug 11 17:18:19 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped interface version to 4:0:0, version to
+       0.5.0.
+
+Fri Jul 28 13:35:06 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped interface version to 3:2:1, version to
+       0.4.2.
+
+Fri Jul 28 11:26:18 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped interface version to 3:1:1, version to
+       0.4.1.
+
+Fri Jul 28 11:25:05 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c: Include nsocket.h not socket.h.
+
+Fri Jul 28 10:31:50 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped interface version to 3:0:1.  Bumped version
+       to 0.4.0.
+
+Thu Jul 27 22:01:11 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in: Install nsocket.h not socket.h.
+
+Thu Jul 27 21:59:45 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Tell neon-config that header files are in
+       $(includedir)/neon.  Use NEON_WARNINGS macro for compiler
+       warnings.
+
+Thu Jul 20 19:20:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in: Install headers into $(includedir)/neon rather
+       libneon.  Add dav_locks.h, xalloc.h, neon_md5.h, neon_i18n.h to
+       list of headers installed.
+
+Mon Jul 17 09:11:46 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped version to 0.3.1, interface version to
+       2:0:1.
+
+Sun Jul 16 18:47:47 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped version to 0.3.0.
+
+Sun Jul 16 17:17:51 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in (nbrowse, nserver, debug-nserver, debug-nbrowse):
+       Added targets.
+
+Sun Jul 16 17:15:08 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (main): Updated for new SSL interface.
+
+Sun Jul 16 16:51:16 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Added --enable-gnome-examples switch.
+
+Sun Jun 18 12:56:00 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nserver.c: New file.
+
+Sun Jun 18 12:54:43 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nbrowse.glade: Added Glade project file.
+
+Sun Jun 18 12:51:56 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nbrowse/main.c, example/nbrowse/interface.c,
+       example/nbrowse/callbacks.c, example/nbrowse/support.c: Added
+       files from Glade.
+
+Thu May 25 01:04:11 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Bumped NEON_INTERFACE_VERSION to 1:0:0.  With
+       --enable-warnings, only include -Wstrict-prototypes if we're not
+       building with SSL support.
+
+Thu May 25 01:01:01 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in: Pass --quiet to libtool to make it a bit less
+       chatty.  (again, debug-nget): New target.
+
+Tue May 23 17:14:43 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (main): Use http_session_create/destroy. Quit if
+       http_set_secure fails.
+
+Tue May 23 15:35:16 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (parse_args, conn_notify): New functions.
+       (main): Call neon_debug_init, call sock_init.  Register
+       conn_notify. If scheme is "https", use a secure connection.
+
+Tue May 23 14:14:05 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (parse_args): New function.  (main): 
+
+Sun May 21 23:54:48 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-config.in: Use @NEON_LIBDIR@ not -L@libdir@ in --libs
+       output.
+
+Sun May 21 23:53:41 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Call NEON_SSL macro, add LDFLAGS to NEON_LIBDIR to
+       pick up OpenSSL library location.
+
+Sat May 20 21:47:54 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c: Include neon_config.h for NEON_VERSION
+       declaration.
+
+Sat May 20 21:47:29 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Added --enable-warnings parameter.
+
+Sat May 20 21:46:54 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in (libneon.la): Fixed passing interface version.
+
+Sun May 14 00:40:38 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in (NEON_VERSION): Bumped to 0.2.0.
+
+Sun May 14 00:28:33 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in (install-config): New goal.
+
+Sun May 14 00:26:00 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Added --enable-debugging argument.  Added
+       NEON_LIBDIR, NEON_INCLUDEDIR, NEON_LIBS, NEON_CFLAGS for
+       neon-config.  Produce neon-config from neon-config.in.
+
+       * neon-config.in: New file, modified from libxml.
+
+Sat May 13 23:16:46 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Builed static and shared libraries by default.
+
+Sat May 13 16:32:15 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * config.sub, config.guess, ltmain.sh, ltconfig: Updated from
+       libtool-1.3.4.
+
+Sat May 13 13:45:56 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c: Don't include neon.h.
+
+Sat May 13 13:44:22 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in: Use libtool.  (install-examples): New goal.
+
+       * configure.in: Define NEON_VERSION.
+
+Thu May 11 14:14:00 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (main): Set user-agent.
+
+Thu May 11 14:10:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * ltconfig, ltmain.sh, config.sub, config.guess: Added libtool
+       support files.
+
+       * configure.in: Call AC_PROG_LIBTOOL and AC_DISABLE_STATIC.
+
+       * .cvsignore: Added libtool.
+
 Wed May 10 19:17:24 2000  Joe Orton  <joe@orton.demon.co.uk>
 
        * configure.in: Print configuration message, check for ranlib.
index 3389afb..ae8a535 100644 (file)
+# Copyright (C) 2001-2004 Joe Orton <joe@manyfish.co.uk>
+# Copyright (C) 1994, 1995-8, 1999, 2000 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
 
-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@
+# This program 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.
+
+# libtool bits mostly stolen from libxml and libtool/demo
+
+SHELL = @SHELL@
 
 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
+mandir = @mandir@
+man1dir = $(mandir)/man1
+man3dir = $(mandir)/man3
+datadir = @datadir@
+docdir = $(datadir)/doc/neon-@NEON_VERSION@
 includedir = @includedir@
-neonincludes = $(includedir)/libneon
+neonincludes = $(includedir)/neon
+pkgconfigdir = $(libdir)/pkgconfig
 
 top_srcdir = @top_srcdir@
-# Where does top_builddir come from?
-top_builddir = @top_srcdir@
+top_builddir = .
 srcdir = @srcdir@
-# intl stuff
-localedir = $(datadir)/locale
-gnulocaledir = $(prefix)/share/locale
-gettextsrcdir = $(prefix)/share/gettext
-aliaspath = $(localedir):.
+VPATH = @srcdir@
+
 @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
+LDFLAGS = -L. @LDFLAGS@
+LIBS = @LIBS@
+CC = @CC@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL = @INSTALL@
+transform = @program_transform_name@
+
+LIBTOOL = @LIBTOOL@
+
+XMLTO = xmlto
 
-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
+# The headers to distribute - making up the public interface of neon
+DIST_HEADERS = ne_request.h ne_session.h ne_utils.h ne_uri.h ne_socket.h  \
+       ne_basic.h ne_207.h ne_props.h ne_xml.h ne_dates.h ne_string.h    \
+       ne_cookies.h ne_defs.h ne_locks.h ne_alloc.h ne_md5.h ne_i18n.h \
+       ne_redirect.h ne_auth.h ne_compress.h ne_acl.h ne_ssl.h
 
-libneon.a: $(OBJECTS)
-       $(AR) cru $@ $(OBJECTS)
-       $(RANLIB) $@
+all: subdirs
 
-shared: libneon.so
+check: subdirs
+       cd test && $(MAKE) check
 
-libneon.so: $(OBJECTS)
-       gcc -shared -o $@ $(OBJECTS)
+# Useful for doing coverage analysis; use e.g.:
+#   make TESTS=string-tests MODULE=ne_string cover
+cover: subdirs
+       rm -f src/$(MODULE).da
+       cd test && $(MAKE) check
+       cd src; gcov -cb $(MODULE)
 
-examples: nget
+subdirs:
+       cd src && $(MAKE)
+       @echo
+       @echo "  Compilation complete. Run '$(MAKE) install' (as root?) to install neon."
+       @echo
 
-nget: libneon.a example/nget.o lib/basename.o
-       $(CC) $(LDFLAGS) -o $@  example/nget.o lib/basename.o -lneon
+# Uses Tim Waugh's excellent "xmlto" utility, see
+# http://cyberelk.net/tim/xmlto/.  (The docs target is executed at release
+# time before generating a .tar.gz)
+docs: docs-man docs-html
 
-install: install-static install-headers
+docs-man:
+       rm -rf doc/man; mkdir doc/man
+       $(XMLTO) -o `pwd`/doc/man man doc/manual.xml
 
-install-static: libneon.a
-       $(INSTALL) -d $(libdir)
-       cp libneon.a $(libdir)/libneon.a
+docs-pdf:
+       $(XMLTO) -o `pwd`/doc pdf doc/manual.xml
 
-install-shared: libneon.so
-       $(INSTALL) -d $(libdir)
-       cp libneon.so $(libdir)/libneon.so
+docs-ps:
+       $(XMLTO) -o `pwd`/doc ps doc/manual.xml
+
+docs-html:
+       test -d doc/html && rm -rf doc/html || true
+       mkdir doc/html
+       $(XMLTO) -o `pwd`/doc/html -x doc/html.xsl html doc/manual.xml
+
+# Validate the manual source
+docs-valid:
+       xmllint --noout --valid doc/manual.xml
+
+clean:
+       cd src && $(MAKE) clean
+       cd test && $(MAKE) clean
+
+distclean: clean
+       rm -rf Makefile config.h config.status libtool config.log config.cache neon-config autom4te.cache neon.pc test/Makefile test/common/Makefile src/Makefile
+
+again: clean
+
+Makefile: Makefile.in
+       @./config.status Makefile
+
+neon-config: neon-config.in
+       @./config.status neon-config
+
+install-docs: install-man install-html
+
+install-html:
+       $(INSTALL) -d $(DESTDIR)$(docdir)/html
+       for d in $(srcdir)/doc/html/*.html; do \
+               $(INSTALL_DATA) $$d $(DESTDIR)$(docdir)/html; \
+       done
+
+install-man:
+       $(INSTALL) -d $(DESTDIR)$(man3dir)
+       $(INSTALL) -d $(DESTDIR)$(man1dir)
+       for m in $(srcdir)/doc/man/*.3; do \
+        $(INSTALL_DATA) $$m $(DESTDIR)$(man3dir); done
+       for m in $(srcdir)/doc/man/*.1; do \
+        $(INSTALL_DATA) $$m $(DESTDIR)$(man1dir); done
+
+install: install-@ALLOW_INSTALL@
+
+install-memleak:
+       @echo "ERROR: The neon internal memory leak checking code is for testing"
+       @echo "ERROR: purposes only; this copy of neon must not be installed."
+       @false
+
+install-yes: install-lib install-headers install-config install-docs
+
+# libtool does all the necessary magic here
+install-lib: subdirs
+       $(INSTALL) -d $(DESTDIR)$(libdir)
+       $(LIBTOOL) --mode=install $(INSTALL) src/libneon.la \
+               $(DESTDIR)$(libdir)/libneon.la
 
 install-headers:
-       $(INSTALL) -d $(neonincludes)
+       $(INSTALL) -d $(DESTDIR)$(neonincludes)
        @for h in $(DIST_HEADERS); do \
-               echo Installing $$h into $(neonincludes); \
-               $(INSTALL_DATA) src/$$h $(neonincludes)/$$h; \
+               echo Installing $$h into $(DESTDIR)$(neonincludes); \
+               $(INSTALL_DATA) $(srcdir)/src/$$h $(DESTDIR)$(neonincludes)/$$h \
+                       || exit 1; \
        done
 
-neon_dir = src
-config_h = config.h
-
-include src/Makefile.incl
+install-config: neon-config neon.pc
+       $(INSTALL) -d $(DESTDIR)$(bindir)
+       @echo Installing neon-config into $(DESTDIR)$(bindir)
+       @$(INSTALL_SCRIPT) neon-config \
+               $(DESTDIR)$(bindir)/`echo neon-config|sed '$(transform)'`
+       $(INSTALL) -d $(DESTDIR)$(pkgconfigdir)
+       $(INSTALL_DATA) neon.pc $(DESTDIR)$(pkgconfigdir)/neon.pc
 
index 5b96550..dbc5713 100644 (file)
--- a/neon/NEWS
+++ b/neon/NEWS
@@ -1,2 +1,932 @@
-Changes in release 0.1.0
-- Initial release.
+Changes in release 0.24.7:
+* Compression interface fixes:
+ - fix issues handling content decoding and request retries from
+ authentication challenges (Justin Erenkrantz)
+ - fix places where reader callback would receive spurious size=0 calls
+ - fix to pass user-supplied userdata to user-supplied acceptance callback
+* Fix for RFC2617-style digest authentication (Hideaki Takahashi).
+* Fix to pick up gethostbyname() on QNX 6.2.
+
+Changes in release 0.24.6:
+* SECURITY (CVE CAN-2004-0398): Fix sscanf overflow in ne_rfc1036_parse,
+ thanks to Stefan Esser.
+* Link libneon against libexpat during Subversion build using bundled neon.
+* Win32 build script update (Jon Foster).
+
+Changes in release 0.24.5:
+* SECURITY (CVE CAN-2004-0179): Fix format string vulnerabilities in
+ XML/207 response handling, reported by greuff@void.at.
+* Performance fix: avoid seeding the SSL PRNG if not creating an SSL socket.
+* ne_ssl_readable_dname() is now defined to return UTF-8 strings.
+* Fix case where gssapi/gssapi_generic.h was included but not present.
+* Fix ne_utils.c build on platforms where zlib does "#define const".
+* Fix use of ne_proppatch_operation with some C++ compilers.
+* Update libtool for fix to --enable-shared on Darwin.
+* BeOS: check for gethostbyname in -lbind (David Reid).
+
+Changes in release 0.24.4:
+* Ignore unclean SSL closure when response body is delimited by EOF
+ ("Could not read response body: Secure connection truncated" errors
+ with some buggy SSL servers).
+* Fix test/ssl.c syntax errors with C89 compilers (Radu Greab).
+
+Changes in release 0.24.3:
+* Respect configure's --datadir argument (Max Bowsher).
+* Fix build on Windows when OpenSSL is not used.
+* Fix use of SSLv2 (spurious "Server did not present certificate" error).
+* When using SSL via a proxy, prevent leaking server auth credentials
+ to the proxy, or proxy auth credentials to the server.
+
+Changes in release 0.24.2:
+* Fix name resolver with some old versions of glibc.
+* Fix problems with configure's "time_t format string" detection.
+* Fix problems when a broken Kerberos installation is found.
+* When verifying SSL certificates, check iPaddress names in the
+ subjectAltName extension.
+
+Changes in release 0.24.1:
+* Add support for "GSS-Negotiate" Kerberos authentication scheme (from
+ Risko Gergely and Burjan Gabor).
+* Disable Nagle to improve performance of small requests (thanks to
+ Jim Whitehead and Teng Xu).
+* Fix compatibility with OpenSSL 0.9.6 (broken in 0.24.0).
+* Fix prototype mismatch in ne_207.c.
+* Define ssize_t from ne_request.h for Win32.
+* Prevent segfault on zlib initialization failures.
+* ne_sock_init does not fail if PRNG could not be seeded.
+* Fix segfault in cookies code (Markus Mueller).
+* Documentation updates.
+
+Changes in release 0.24.0:
+* Major changes to XML interface:
+ - have the start-element callback either accept, decline, abort, 
+ or return a state integer.
+ - remove 'struct ne_xml_elm'; callbacks are passed {nspace, name}
+ strings along with a state integer.
+ - dropped "collect", "strip-leading-whitespace" modes
+ - push responsibility for accumulating cdata onto caller; drop 'cdata'
+ argument from end-element callback.
+ - don't abort if no handler accepts a particular element, just ignore
+ that branch of the tree.
+ - dropped support for libxml 1.x and expat < 1.95.0.
+ - guarantee that start_element callback is not passed attrs=NULL
+ - add ne_xml_doc_encoding() to retrieve encoding of parsed XML document.
+* Major changes to SSL interface:
+ - rewrite of interfaces for handling server and client certificates;
+ ne_ssl.h: many new functions available.
+ - only PKCS#12-encoded client certs are supported.
+ - changes to most names of SSL-related functions operating on an
+ ne_session, e.g. ne_ssl_load_cert->ne_ssl_trust_cert.
+ - client cert provider callback is passed the set of acceptable CA
+ names sent by the server
+ - the entire chain of certs presented by server is now accessible
+* Remove unused ne_register_progress() from socket layer.
+* Changes to resolver interface: ne_addr_first and _next return const;
+ ne_addr_print renamed to ne_iaddr_print; ne_iaddr_make and ne_iaddr_free
+ have been added.
+* ne_request_create() now duplicates the method string passed in.
+* ne_redirect_location() will now return NULL in some cases.
+* Split socket creation to ne_sock_create() from ne_sock_connect:
+ - should report connect() error messages properly on Win32.
+* Fix several memory leaks in error handling paths.
+* Add a pkg-config file, neon.pc.in.
+
+Changes in release 0.23.9:
+* Fix inability to connect on AIX 4.3.
+* neon-config exports includes needed for OpenSSL given by pkg-config.
+* ne_redirect_location will return NULL if redirect hooks have not
+ been registered for the session (Ralf Mattes <rm@fabula.de>).
+
+Changes in release 0.23.8:
+* SECURITY: Prevent control characters from being included in the
+ reason_phrase field filled in by ne_parse_statusline(), and in
+ the session error string.
+* Disable getaddrinfo() support on HP-UX; fix resolver for HP-UX 11.11.
+* Fix digest auth response verification for >9 responses in session
+ (bug manifests as "Server was not authenticated correctly" error).
+* On Linux, skip slow lookup for IPv6 addresses when IPv6 support is
+ not loaded in kernel (thanks to Daniel Stenberg for this technique).
+* Update to autoconf 2.57 and libtool 1.4.3.
+
+Changes in release 0.23.7:
+* Fix for handling EINTR during write() call (Sergey N Ushakov).
+* When available, use pkg-config to determine compiler flags needed to 
+ use OpenSSL headers and libraries.
+
+Changes in release 0.23.6:
+* Fixes for error handling in socket layer on Win32 from Johan Lindh
+ and Sergey N Ushakov <ushakov@int.com.ru>:
+ - meaningful error messages rather than "No error"
+ - handle persistent connection timeouts properly
+* Fix to use RFC2617-style digest auth when possible (had reverted to 
+ only using RFC2068-style in 0.16.1).
+* Fix NULL pointer dereference on certain ill-formed PROPFIND responses.
+* Allow ne_sock_init to re-initialize after ne_sock_finish has been called
+ (Sergey N Ushakov).
+
+Changes in release 0.23.5:
+* Fix rejection of SSL server certificates which had commonName as
+ the least specific attribute in the subject name.
+* Fix to dereference entities (e.g. "&amp;") in attribute values with libxml.
+* Fix ne_socket.c build on HP-UX 10.20 (thanks to Branko Ãˆibej)
+* Remove misguided insistence on "secure" versions of zlib/OpenSSL;
+ no checks for zlib version are now performed, only OpenSSL 0.9.6 is
+ required.  --with-force-ssl, --with-force-zlib option removed.
+* Add --with-egd[=PATH] option, conditionally enable EGD support; either 
+ using EGD socket at PATH, or fall back on system defaults.  $EGDSOCKET 
+ and $HOME/.entropy are no longer used.
+* Add support for `--la-file' argument to neon-config, which prints the
+ full path of the installed libneon.la file.
+
+Changes in release 0.23.4:
+* Ignore an unclean SSL shutdown on persistent connection timeout
+ (fixing spurious "Secure connection truncated" errors).
+* Fix a segfault on second and subsequent requests using a given
+ session, when the first fails with NE_LOOKUP.
+* Fix configure for gcc installations which produce warnings by default
+ (such as gcc on hppa2.0n-hp-hpux11.00 using native as)
+
+Changes in release 0.23.3:
+* Further build fixes for Win32 (Blair Zajac).
+* Another fix for use of SSL against Tomcat 3.2.
+
+Changes in release 0.23.2:
+* Build fix for Win32 (Blair Zajac).
+
+Changes in release 0.23.1:
+* Identify as correct version, not 0.22.
+
+Changes in release 0.23.0:
+* Improved address resolver (ne_addr_*) replacing ne_name_lookup():
+ - use getaddrinfo() if found; include support for IPv6 (based on work
+ by Noriaki Takamiya <takamiya@po.ntts.co.jp>)
+* For a hostname with multiple addresses, each address is tried in turn
+ until a connection is made.
+* Support for seeding OpenSSL's PRNG via $EGDSOCKET or $HOME/.entropy,
+ to enable SSL on platforms which lack a /dev/random device.
+* RFC2818 compliance for certificate identity checks in SSL:
+ - use `dNSname' values in subjectAltName extension if present
+ - hostname comparison fixed to not be case-sensitive
+* Fix interop with buggy SSL implementation in Tomcat 3.2.
+* Added NE_DBG_SSL debug channel.
+* ne_strerror changed to return the passed-in buffer.
+* Added ne_strnzcpy macro to ne_string.h.
+* Win32 build fixes, improvements, and documentation updates, from 
+ Blair Zajac <blair@orcaware.com>.
+* Fix ne_sock_init so SIGPIPE signals are ignored even if SSL library
+ initialization fails (e.g. platforms without /dev/random).
+* Added reference documentation:
+ - ne_sock_init, ne_addr_*.
+
+Changes in release 0.22.0:
+* Remove the const qualifier from the reason_phrase field in ne_status.
+ - ne_parse_statusline() now strdup's the reason_phrase
+* Remove the status_line argument from ne_207_end_propstat and _end_response
+* Change ne_session_create, ne_session_proxy, ne_sock_connect, and the 
+ 'port' field of the ne_uri structure to use an unsigned int for port numbers
+* ne_uri_defaultport returns unsigned and '0' on an unknown port (not -1).
+* Changes to hooks interface:
+ - pass an ne_request pointer to per-request hooks
+ - replace "accessor" hooks with ne_{get,set}_{request,session}_private
+* Authentication changes:
+ - the hooks changes fix a segfault if auth is enabled for an SSL session
+ through a proxy server
+ - fix ne_forget_auth segfault if either proxy or server auth are not used
+* Improvements to persistent connection retry logic and error handling 
+ in request code; fixing some cases where some errors where incorrectly
+ treated as a persistent connection timeout
+ - a TCP RST at the appropriate time is now treated as a persistent 
+ connection timeout.
+ - handle persistent connection timeouts on SSL connections
+* Changes to SSL support:
+ - improved error handling
+ - OpenSSL 0.9.6f or later is required for security fixes and functional
+ correctness; 0.9.6 or later required for functional correctness
+ - use --with-force-ssl to override OpenSSL version check
+ - fix for proxy CONNECT tunnelling with some proxies (e.g. Traffic-Server)
+ - fix potential segfault if client cert. provider callback is used
+ - fix to use supplied password callback for PEM-encoded client certificates
+ (Daniel Berlin <dberlin@dberlin.org>)
+* strerror_r is used if available for thread-safe error handling.
+* Remove ne_read_file().
+* ne_version_match replaces ne_version_minimum (semantics changed slightly).
+* XML request bodies use a content-type of "application/xml" now; 
+ applications can use NE_XML_MEDIA_TYPE from ne_xml.h
+* Fix decompress code on big-endian or 64-bit platforms.
+* Fix to build on Darwin 6 (aka Mac OS X 10.2) (Wilfredo Sánchez,
+ <wsanchez@mit.edu>)
+* Win32 changes:
+ - remove conflict between OpenSSL's X509_NAME and recent versions of
+ the Platform SDK (Branko Ãˆibej)
+ - fix inverted debug/non-debug build logic (Branko Ãˆibej)
+ - add NODAV and OPENSSL_STATIC flags to neon.mak (Gerald Richter)
+
+Changes in release 0.21.3:
+* Fix segfault if using proxy server with SSL session and server
+ certificate verification fails.
+* Fix leak of proxy hostname once per session (if a proxy is used).
+* Add --with-libs configure argument; e.g. --with-libs=/usr/local picks
+ up any support libraries in /usr/local/{lib,include}
+
+Changes in release 0.21.2:
+* Fix 'make install' for VPATH builds.
+* Use $(mandir) for installing man pages (Rodney Dawes).
+* Follow some simple (yet illegal) relativeURI redirects.
+* Always build ne_compress.obj in Win32 build (Branko Ãˆibej).
+* Fix decompression logic bug (Justin Erenkrantz <jerenkrantz@apache.org>)
+ (could give a decompress failure for particular responses)
+* Fix ne_proppatch() to submit lock tokens for available locks.
+* More optimisation of ne_sock_readline.
+
+Changes in release 0.21.1:
+* Don't include default SSL port in Host request header, which can
+ help interoperability with misbehaving servers (thanks to Rodney Dawes
+ <dobey@ximian.com>).
+* Don't give a "truncated response" error from ne_decompress_destroy if
+ the acceptance function returns non-zero.
+* Fix for Win32 build (Sander Striker <striker@apache.org>).
+* Fix for cookie name/value being free()d (thanks to Dan Mullen).
+* Optimisation of ne_sock_readline.
+
+Changes in release 0.21.0:
+* Socket layer implements read buffering; efficiency and performance 
+ improvement. Based on work by Jeff Johnson <jbj@redhat.com>
+* Cleanup of socket interface:
+ - renamed everything, s/sock_/ne_sock_/, s/SOCK_/NE_SOCK_/
+ - removed unused and inappropriate interfaces.
+ - renaming done by Olof Oberg <mill@pedgr571.sn.umu.se>
+ - see src/ChangeLog for the gory details.
+* Fix typoed 'ne_destroy_fn' typedef (Olof Oberg).
+* Support OpenSSL/ENGINE branch.
+* Bogus ne_utf8_encode/decode functions removed.
+* ne_base64() moved to ne_string.[ch].
+* ne_token drops 'quotes' parameter; ne_qtoken added.
+* ne_buffer_create_sized renamed to ne_buffer_ncreate.
+* ne_xml_get_attr takes extra arguments and can resolve namespaces.
+* ne_accept_response function type takes const ne_status pointer.
+* Drop support for automatically following redirects:
+ - ne_redirect_register just takes a session pointer
+ - ne_redirect_location returns an ne_uri pointer
+* configure changes: --with-ssl and --with-socks no longer take a directory
+ argument.  To use SOCKS or SSL libraries/headers in non-system locations, 
+ use ./configure CPPFLAGS=-I/... LDFLAGS=-L/... 
+* Reference documentation included for most of ne_alloc.h and ne_string.h,
+ and parts of ne_session.h and ne_request.h.
+ - see installed man pages, HTML documentation.
+
+Changes in release 0.20.0:
+* Major changes to DAV lock handling interface (ne_locks.h):
+ - struct ne_lock uses a full URI structure to identify locked resource
+ - ne_lock() requires that owner/token fields are malloc-allocated (or NULL)
+ on entry
+ - introduce a "lock store" type, ne_lock_store, to replace the lock session;
+ accessor functions all renamed to ne_lockstore_*.
+ - ne_lock_iterate replaced with a first/next "cursor"-style interface
+ - If: headers use an absoluteURI (RFC2518 compliance fix).
+ - fix for handling shared locks on DAV servers which return many active locks
+ in the LOCK response (thanks to Keith Wannamaker)
+* Moved URI/path manipulation functions under ne_* namespace (ne_uri.h):
+ - path handling functions renamed to ne_path_*
+ - URI structure handling to ne_uri_*; struct uri becomes ne_uri.
+ - ne_uri_parse doesn't take a 'defaults' parameter any more
+ - if URI port is unspecified, ne_uri_parse sets port to 0 not -1.
+ - added ne_uri_unparse and ne_uri_defaultport functions.
+* New 'ne_fill_server_uri' function to initialize a URI structure with
+ the server details for a given session (useful with locks interface).
+* ne_decompress_{reader,destroy} are defined as passthrough-functions
+ if zlib support is not enabled.
+* API change: ne_ssl_provide_fn returns void not int.
+* Added NE_SSL_FAILMASK for verify failure sanity check.
+* Removed return codes NE_SERVERAUTH and and NE_AUTHPROXY; correct
+ documentation, NE_PROXYAUTH is given for proxy auth failure.
+* Require zlib >= 1.1.4 to avoid possible vulnerability in earlier versions.
+ See http://www.gzip.org/zlib/advisory-2002-03-11.txt for more details.
+ (version check can be skipped by passing --with-force-zlib to configure)
+* New 'ne_ssl_readable_dname' function to create a human-readable string
+ from an X509 distinguished name.
+* Fix support for newer versions of libxml2 (thanks to Jon Trowbridge 
+ <trow@gnu.org>).
+* Fix corruption of reason_phrase in status object returned by
+ ne_propset_status.
+* More lenient handling of whitespace in response headers.
+* ne_content_type_handler will give a charset of "ISO-8859-1" if no charset
+ parameter is specified for a text/* media type (as per RFC2616).
+* Miscellaneous cleanups and fixes (Jeff Johnson <jbj@redhat.com>).
+
+Changes in release 0.19.4:
+* Support bundled build of expat 1.95.x (Branko Ãˆibej).
+
+Changes in release 0.19.3:
+* For platforms lacking snprintf or vsnprintf in libc, require trio.
+* Add NE_FMT_OFF_T to fix Win32 build (Dan Berlin, Branko Ãˆibej).
+* Fix SSL support in Win32 build (Branko Ãˆibej).
+
+Changes in release 0.19.2:
+* Fix non-SSL build broken in 0.19.1.
+* Working SOCKSv5 support (thanks to Torsten Kalix <torsten.kalix@bredex.de>)
+
+Changes in release 0.19.1:
+* Add missing stubs for ne_ssl_* functions for non-SSL build.
+* Fix some error messages in new SSL code.
+
+Changes in release 0.19.0:
+* Major API change: ne_session_create now takes (scheme, hostname, port) 
+ arguments: a session is clarified to be "a group of requests to a
+ certain server".
+ - removal of ne_session_server, ne_set_secure, and ne_set_proxy_decider
+ - ne_session_proxy returns void.
+ - DNS lookups are delayed until request dispatch time.
+* Significant improvements to TLS/SSL support:
+ - SSL is enabled if scheme passed to ne_session_create is "https"
+ - new interfaces to load CA certs and to load SSL library's bundled CA certs
+ - add server cert verification callback.  An SSL connection to a server
+ with an unknown CA will now fail unless a verification callback is used.
+ - enable SSL session caching (performance improvement)
+ - support for wildcard server certs where commonName is "*.example.com".
+ - thanks to Tommi Komulainen for the contribution of code from mutt's
+ IMAP/SSL implementation under the LGPL, from which bits of this were derived.
+* Improved SSL client certificate support:
+ - far simpler interface, all done at ne_session.h level.
+ - supports PKCS#12 and PEM-encoded certificates.
+ - optional callback for only providing client when demanded by server.
+* Support for TLS upgrade is removed, since it isn't useful.
+* If NEON_SSL is defined, API extensions are available to:
+ - allow access to the SSL_CTX * to adjust session SSL options
+ - retrieve the server certificate (X509 *)
+* Decompress fixes:
+ - fix potential segfault in ne_decompress_destroy
+ - check the CRC of the deflated output (and fail if it doesn't match)
+ - fail appropriately on truncated responses, and trailing bytes in response. 
+* Added ne_set_read_timeout to use configurable timeout on socket reads.
+* Malformed response headers will be ignored rather than failing the request.
+* ne_set_error takes printf-style vararg.
+* Fixes for ne_get_range and improve error handling. 
+* Functions which append to an ne_buffer do not return a success value,
+ but they do use ne_realloc/ne_malloc under the hood now, so an OOM callback
+ will be used (with the usual caveats).
+* XML interface does not strip leading whitespace from cdata by default,
+ the NE_XML_STRIPWS flag is available to restore this feature if required.
+* Upgraded to libtool 1.4.2:
+ - should fix --enable-shared on Mac OS X 10.1
+* Test suite now contains over one hundred tests.
+
+Changes in release 0.18.5:
+* Removed old neon.dsp, neon.dsw.
+* Update Win32 build to add OpenSSL and zlib support (Branko Ãˆibej).
+* Fix ne_compress.c to compile on Win32 (Branko Ãˆibej).
+
+Changes in release 0.18.4:
+* Fixes for Content-Type parsing using ne_content_type_handler (Greg Stein)
+ - also now parses the charset parameter from header value.
+* Removed ne_concat() function, which didn't work and wasn't used.
+
+Changes in release 0.18.3:
+* Fix parsing lock timeout from server (Arun Garg).
+* Send Timeout headers in LOCK and refresh LOCK requests (Arun Garg).
+* Updated neon.mak and config.hw.in for Win32 build (patch from
+ Branko Ãˆibej <brane@xbc.nu>).
+* Define XML_BYTE_ORDER for bundled expat build in support macro
+ NEON_XML_PARSER().
+
+Changes in release 0.18.2:
+* Fix --with-neon=PATH in support macros.
+* Support DESTDIR in Makefile install targets (patch by
+ Pawel Golaszewski <blues@blysk.ds.pg.gda.pl>).
+* Portability fixes:
+ - fix configure check for time_t on some platforms (e.g Solaris 2.6).
+ - remove expect100_works bitfield in ne_session structure (thanks to 
+ Yan Periard <yperiard@ems.net>).
+
+Changes in release 0.18.1:
+* Minor fix for authentication: "attempt" counter was not reset correctly
+ after authentication failed, so subsequent requests would not authenticate
+ correctly either.
+* API change: ne_session_destroy returns void (there was no error case).
+* Portability fixes (non-GCC compilers, 64-bit platforms, UnixWare 7)
+* Optimisations in string manipulation routines.
+* config.hw is included in the release tarball again.
+* Improvements in the autoconf support macros:
+ - check for neon-config in PATH if --with-neon is not given
+ - stop if --with-neon is used, and the check for external neon fails
+ - added NEON_WITHOUT_ACL to prevent build of ne_acl.o
+
+Changes in release 0.18.0:
+* API change: authentication callback is passed fixed-size username/password
+ buffers, and an 'attempt' counter.  Authentication is retried *forever*
+ until either it succeeds, or the callback returns non-zero.
+* API clarifications:
+ - ne_propname may have a NULL nspace field, indicating the property has no 
+ namespace.  This holds for properties returned by the propfind interfaces.
+ - added NE_ELM_PROPS_UNUSED as the lowest element number which should
+ be used with handlers added to the XML parser returned by 
+ ne_propfind_get_parser.
+* Fixes and cleanups of lock discovery interface.
+* Fix for short write handling in ne_get() (thanks to rado <dzusto@yahoo.com>).
+* Fix for XML namespace prefix handling where a prefix could be mapped to an 
+ incorrect URI (e.g. in PROPFINDs against mod_dav with >10 namespaces used)
+* Add '--support <feature>' option to neon-config; the script exits with
+ success if given feature is supported. Known features are ssl, dav, zlib.
+* Support for SSL, DAV, zlib is exported by neon.m4 as shell variable 
+ NEON_SUPPORTS_{SSL,DAV,ZLIB}={yes,no} for bundled and external builds.
+* `neon-config --cflags` won't include -I/usr/include for SSL build.
+* Fix to call progress callbacks while sending request bodies again.
+* Test changes:
+ - portability fixes, auth interface and progress tests.
+
+Changes in release 0.17.2:
+* Accept Status-Lines with no reason phrase (Jeremy Elson).
+* Fix handling of persistent connection timeout, and better error
+ handling if sending a request fails.
+* Fix crashes in locking code.
+* Return parse error on XML namespace prefix declaration with
+ an empty value. Thanks to Julian Reschke.
+* Allow passing property names with NULL namespace to ne_proppatch.
+* Fix for cross-compilation (Mo DeJong).
+* Moved ne_propname definition from ne_207.h to ne_props.h.
+* Test changes:
+ - updated for Status-Line parsing changes (Jeremy Elson)
+ - better persistent connection tests
+ - fixed for --disable-webdav build
+
+Changes in release 0.17.1:
+* Add support for ACL method (Arun Garg <arung@pspl.co.in>),
+ see ne_acl.h.
+* Fixes and clean up of libraries exported via `neon-config --libs'
+* Fix timezone handling when parsing dates (on some platforms).
+* Upgrade to autoconf 2.52 and libtool 1.4 (thanks to Mo DeJong).
+* Cleanup/simplification of request dispatching:
+ - better handling of error cases, including fix for a possible
+ infinite loop when the server closes the connection prematurely.
+* Add '--without-zlib' configure option.
+* Test changes:
+ - prettify output; imitate Perl test suite output.
+ - add tests for interim 1xx responses, persistent connections, more
+ unbounded operations.
+
+Changes in release 0.17.0:
+* Add support for decoding gzip Content-Encoding: see ne_compress.h.
+ - built if zlib is found; `neon-config --cflags' will define NEON_ZLIB if so.
+* Rewrite hooks interface to register individual callbacks.
+ - inspired by the Apache 2.0/APR hooks interface
+* Register cookies hooks using ne_cookie_register().
+* Clean up configure scripts to enable use of autoconf 2.5x (Mo DeJong).
+* Use new endianess configure macro to allow cross-compiling (Mo DeJong).
+* Fix invalid C code in sock_init() in Win32 build (Mo DeJong).
+* Fix use of signal() on Win32 (Mo DeJong).
+* Workaround libxml 1.x string handling not being UTF-8.       
+* Test changes:
+ - add tests for decompression interface.
+
+Changes in release 0.16.1:
+* Also handle write errors in ne_get_range.
+* Dump request body blocks in debugging mode.
+* Fix ne_shave() causing memory corruption when the result should 
+  have been the empty string.
+* Refactor auth header parsing code; more efficient now. 
+ - fixes digest auth RFC2617-style broken in 0.16.0
+
+Changes in release 0.16.0:
+* API change: ne_copy takes a depth parameter (thanks to Arun Garg, Medha Atre)
+* API change: validate callback to ne_xml also takes a userdata arg.
+* Added 'ne_lock_refresh' for performing lock refresh (Arun Garg).
+* Add SSL support to Win32 build (Peter Boos <PediB@colorfullife.com>)
+  (see INSTALL.win32 for details).  Compile with USE_DAV_LOCKS also.
+* Remove Server header parser for 100-continue support in ne_options.
+  (and remove broken_expect100 from ne_server_capabilities).
+* Set SIGPIPE disposition to "ignored" in sock_init().
+* On platforms with setvbuf(), turn off buffering for the debug log 
+  stream.
+* Ignore repeated calls to sock_init().
+* Fixes to error handling in ne_get_range.
+* Minor improvements to memory handling in auth code.
+* Fix for start_propstat callback being called with NULL response
+  argument when given invalid XML, causing a segfault in propfind code.
+* Test changes:
+ - add regression test for the propfind segfault.
+ - handle segfaults better (reap the child, flush the debug log).
+
+Changes in release 0.15.3:
+* Fix --with-expat=DIR build.
+
+Changes in release 0.15.2:
+* Fix Win32 for XML parser changes (Gerald Richter).
+* Substitute versions into config.hw at distribution time.
+* Add date parser for ISO8601-formatted dates as defined by RFC2518, e.g. 
+  the creationdate property (Taisuke Yamada <tai@iij.ad.jp>).
+* Fix Y2K bug in RFC1036 date parsing algorithm.
+* Test changes:
+ - add tests for date parsing functions.
+
+Changes in release 0.15.1:
+* Win32 update from Gerald Richter <richter@ecos.de>
+ - new files neon.mak, INSTALL.win32
+* Fix for ne_socket.h includes (Mo DeJong).
+* More improvements for XML parser selection logic:
+ - if parser is required, be sure to fail configure if none is found.
+ - added --with-included-expat for bundled expat logic.
+* Rename --enable-debugging to --enable-debug (Mo DeJong).
+ - added NEON_DEBUG macro to exported autoconf macros.
+* Call progress callbacks for request bodies.
+* Test changes:
+ - check that reading response headers is a bounded operation.
+ - use a pipe between child and parent to avoid race condition and 
+ tedious sleep().
+
+Changes in release 0.15.0:
+* Major API renaming to use ne_/NE_ namespace:
+ - http_ to ne_, HTTP_ to NE_, dav_ to ne_, DAV_ to NE_, neon_ to ne_
+ - hip_xml_ to ne_xml_, HIP_ELM_ to NE_ELM_, HIP_XML_ -> NE_XML_
+ - sbuffer_ to ne_buffer_
+ - DEBUG() to NE_DEBUG(), DEBUG_ to NE_DBG_
+* Type renames:
+ - http_req to ne_request
+ - sbuffer to 'ne_buffer *'
+* Note, 'ne_buffer' is not an implicit pointer type, you must 
+  specify the '*' now, e.g. 'ne_buffer *buf = ne_buffer_create();'.
+* ne_buffer is no longer opaque.
+ - ne_buffer_data() removed: use buf->data instead.
+ - ne_buffer_size() is a macro.
+* Header renames and additions:
+ - http_request.h -> ne_request.h
+ - Session code split into ne_session.h
+ - hip_xml.h -> ne_xml.h, nsocket.h -> ne_socket.h, http_utils.h -> ne_utils.h
+ - neon_md5.h -> ne_md5.h, dav_207.h -> ne_207.h
+ - http_basic.h and dav_basic.h merged into ne_basic.h
+* New functions:
+ - ne_token and ne_shave, to obsolete split_string, shave_string.
+* Removed: ne_get_request_headers().
+* autoconf changes:
+ - disable building shared neon library by default.
+ - option --enable-libxml is replaced by --with-libxml1 and
+ --with-libxml2 to force use of a particular parser.
+* Fix auth code to only take MD5 digests of response body blocks when
+  necessary (thanks to Kai Sommerfeld).
+* Fix alignment bug in MD5 code which could cause SIGBUS on Sparc 
+  architectures (Kai Sommerfeld).
+* Rewrite of request body handling:
+ - ne_set_request_body_fd replaces _stream, using an int fd rather than
+ a FILE *.
+ - added ne_set_request_body_provider to give a callback which is called
+ to provide request body blocks.
+ - removal of 'use_body' hook in favour of 'ne_pull_request_body' function
+ to allow hooks to manually read the request body.
+ - ne_{put,get,post,put_if_unmodified} all take an integer fd rather than a
+ FILE * stream.
+* Test changes:
+ - added framework for testing "over the wire" (fork a server process)
+ - added tests for response message length handling, chunked responses, 
+ header folding, sending request bodies.
+ - start at listing RFC2616 requirements and whether they are met
+ or not in test/STATUS.
+ - test for MD5 alignment bug on Sparc (thanks to Kai Sommerfeld).
+
+Changes in release 0.14.0:
+* Add C++ inclusion safety to http_auth.h (Kai Sommerfeld).
+* Define ssize_t on Win32. (Kai Sommerfeld).
+* Add C++ inclusion safety to dav_locks.h and ne_alloc.h (thanks to
+  Gregor Bornemann <Gregor.Bornemann@germany.sun.com>).
+* Significant API change to properties code, to allow use of allprop
+  and complex properties:
+ - dav_propfind_set_complex and _set_flat are removed.
+ - add parameter to dav_propfind_named to take the list of property names 
+   to be fetched.
+ - new function dav_propfind_set_private to set private callback.
+ - all properties not handled by caller are stored as flat properties.
+* Untested: add basic SOCKSv5 support: configure --with-socks.
+ - please report success/failure to neon@webdav.org
+* Win32/MSVC build files from Magnus Sirwiö <sirwio@hotmail.com>.
+* Fix for expat detection from Shane Mayer <shanemayer42@yahoo.com>.
+* Namespace-protect md5 code and more.
+ - md5_* -> ne_md5_*
+ - ascii_to_md5 -> ne_ascii_to_md5 (and moved to neon_md5.h)
+* Parse authinfo segment in URIs (Johan Lindh <johan@link-Data.com>).
+ - added 'authinfo' field to struct uri.
+* New API: hip_xml_get_attr to retrieve attributes.
+* Store language for properties, access with dav_propset_lang.
+ - only if property is defined on the property element itself.
+* Started a simple test suite (test/*).
+ - includes some simple HTTP server tests.
+* Remove "Content-Length: 0" header for request with no body, fixing
+  interop with Squid 2.3-STABLE1 (thanks to Kai Sommerfeld).
+* http_parse_statusline skips leading whitespace. (Johan Lindh). 
+* Partial fix for timezone/date parsing problems.
+
+Changes in release 0.13.0:
+* Fix ne_strndup allocating one byte less than it should (Kai Sommerfeld)
+ - if you use uri_parse, this bug may have caused subtle memory corruption 
+   in your application. 
+* Revert API changes in 0.12: property values are not UTF-8 encoded/decoded
+  internally.  (thanks to Greg Stein)
+* Add another optional argument to NEON_BUNDLED macros, actions to
+  be run if bundled build is *not* selected.
+* API change: added argument to http_add_hooks to register cleanup function
+  for the cookie.
+* Removed dav_lock_unregister in favour of automatic cleanup when session
+  is destroyed.
+* Fixed leaks in redirect code (Kai Sommerfeld).
+* Fixed crashes in hip_xml_destroy (Kai Sommerfeld).
+* Redirects to a different hostname/port/scheme are never followed: the request
+  will fail with HTTP_REDIRECT instead.  Redirect notification callback is 
+  only called for *followed* redirects.
+  New API: http_redirect_location() for retrieving location of last redirect.
+* Authentication is now implemented as a hook, independently of http_request.c:
+ - API change: removed 'hostname' argument from auth callbacks.
+ - API change: you must now include http_auth.h from your application.
+ - Also fixes case of using server and proxy authentication simultaneously
+* Added 'http_forget_auth' to clear authentication session.
+* New API: http_session_hook_private for retrieving private per-session cookie 
+  for hooks.
+* API change: http_set_request_body_stream has a return error value.
+* API change: http_set_request_body_buffer now takes the buffer length too.
+* New API: caller-pulls interface for reading response body: 
+  http_begin_request, http_end_request, http_read_response_block. 
+  An alternative to using the (much simpler) http_request_dispatch.
+* Make --disable-webdav build work.
+* New API: dav_propnames for retrieving property names.
+* New API: dav_propfind_get_request to access request object of handler.
+* API change: progress and connection status callbacks implemented at
+  http_request.h level. Socket-level status callbacks removed, progress
+  callbacks made per-socket.
+* Supports new expat (Sam TH <sam@uchicago.edu>)
+* Supports libxml2 (in preference to libxml1).
+* API change: added namespace protection to base64 and dates functions:
+  all have ne_ prefix now.
+* Fixed ranged GETs where a specific range is requested (Johan Lindh 
+  <johan@linkdata.se>).
+* Limit number of response header fields to 100.
+* Allow requests for the '*' URI even if a proxy server is in use.
+* libxml: Get useful error messages for parse errors.
+
+Changes in release 0.12.0:
+* Portability fixes to http_request.c and http_auth.c.
+ - fixes digest auth on big-endian architectures.
+* Fix warnings from stray tokens after #endif's in uri.h and string_utils.h.
+* Add C++ inclusion safety to http_redirect.h (Kai Sommerfeld 
+  <kai.sommerfeld@germany.sun.com>).
+* Make redirects to a different host work (Kai Sommerfeld).
+* Fix reading response bodies when non-chunked and no Content-Length
+  (Kai Sommerfeld).
+* API change: 'http_add_hooks takes a 'const' request object.
+* Fixed memory leaks in session hooks (thanks to Kai Sommerfeld).
+* Fix passing NULL props argument to dav_simple_propfind, to support
+  allprop requests.
+**** MAJOR INTERFACE CHANGE ****
+ - URIs passed to http_request_create() are NOT escaped by neon. You 
+   MUST do this yourself to remain HTTP compliant, using e.g. 
+   uri_abspath_escape. (Kai Sommerfeld)
+* Added --disable-webdav flag to configure, to disable DAV support in
+  the library.  This allows building neon without an XML parser. 
+* Corresponding NEON_WITHOUT_WEBDAV macro for use in bundled builds.
+* Fix Makefile dependancies.
+* A bundled neon directory builds or doesn't build automatically
+  (i.e. you recurse into it unconditionally).
+* API clarification:
+ - dav_propset_status may return NULL if the server does not return
+   a response for the given property (issue is open for debate).
+* API change up for debate:
+ - Property values to dav_proppatch are UTF-8 encoded internally.
+ - Property values in dav_propfind_* are UTF-8 decoded internally.
+* API additions: ne_realloc, ne_utf8_encode.
+
+Changes in release 0.11.0:
+* Added SSL client certificate support with 'sock_set_client_cert'.
+ - Supports certs in PEM-encoded files.
+ - Specify a callback for prompting the user for the password with 
+     sock_set_key_prompt.
+* Added 'ne_oom_callback', to register a callback which is used if
+  malloc() returns NULL.  (Mike Rosellini <m@icopyright.com>)
+* Register appropriate callback with libxml to handle <![CDATA blocks
+  as normal character data (fixes PROPFINDs against sharemation.com).
+* Added 'NEON_REQUIRE' macro to declare that you require a neon library
+  of a given minimum version, e.g.  NEON_REQUIRE(0,10) means "I need 
+  neon 0.11 or later".  The _BUNDLED macros will fall back on the bundled
+  copy if an external library is found which is not of a new enough version.
+* Added neon_version_minimum() function call for run-time version detection.
+* neon_config.h has been removed.
+* Use closesocket() to close sockets on Win32 (Markus Fleck <fleck@isoc.de>).
+
+Changes in release 0.10.1:
+* Default expect-100 to OFF.
+
+Changes in release 0.10.0:
+* hip_xml API changes:
+ - The search for a handler for a new child element begins at the
+   handler of the parent element, and carries on up the stack.
+   (previously, it always started from the base of the stack)
+ - Documentation written: doc/parsing-xml.txt
+* Remove memory leaks and tidy debugging output in new properties code.
+* API changes to DAV locking interface: 
+ - New function: dav_lock_copy to copy a lock object.
+ - Re-ordered arguments to callback of dav_lock_discover, and made the
+   lock object passed back const.
+ - Fix leaks and crashes due to vague interface definitions.
+* API change to dav_propfind_set_complex: use a callback to return the
+  'private' structure.
+* NEON_NORMAL_BUILD and NEON_LIBTOOL_BUILD macros defined for setting
+  up neon's Makefile in a bundled build: see macros/neon.m4.
+* NEON_VPATH_BUNDLED macro added which takes separate srcdir and
+  builddir arguments for supporting VPATH builds (thanks to Peter Moulder 
+  <pjm@bofh.asn.au>).
+* Added optional final argument to NEON_(VPATH_)BUNDLED, which gives
+  a set of actions to be run if the bundled build is chosen.
+* NEON_SSL checks for OpenSSL in /usr too.
+* API change: when using http_session_decide_proxy, it MUST be called
+  before using http_session_server to prevent the DNS lookup on the origin
+  server being optimised out.  The real scheme in use is passed to the 
+  callback now.
+* New function, dav_207_ignore_unknown, to ignore any unknown XML fragments
+  in the 207 response.  Used by properties layer.
+
+Changes in release 0.9.2:
+* Fix using both dav_propfind_set_complex and dav_propfind_set_flat with the
+  same propfind_handler.
+
+Changes in release 0.9.1:
+* dav_propfind interface
+ - Guarantee that the 'private' structure will be initialized to zero on
+   creation.
+ - Make it the *callers* responsibility to free() the private structure.
+* Fix a few arguments/variables which mirrored globally declared symbols.
+
+Changes in release 0.9.0:
+* Removed old dav_propfind_* interface, replaced with a better, more
+  powerful, and easier to use interface:
+ - 'dav_simple_propfind' interface for just fetching "flat" (byte-string)
+   properties.
+ - 'dav_propfind_*' interface for fetching flat and/or "complex" (structured
+   XML) properties.
+ - Lets you retrieve the 'status' information, to see what happened if
+   fetching the property failed (e.g 404 Not Found).
+* Fixes to doc/using-neon.txt (thanks to Greg Stein).
+* Allow building when srcdir != builddir (Mo DeJong <mdejong@cygnus.com>)
+
+Changes in release 0.8.1:
+* Fix segfault in PROPFIND code.
+
+Changes in release 0.8.0:
+* Fix for using COPY/MOVE over SSL (thanks to David Sloat).
+* Fix for using a proxy server and SSL.
+* Added 'http_get_scheme' API call.
+* Added 'http_redirect.h' to list of installed headers (thanks to everyone ;).
+* Changes for building on Windows (Peter Boos <PediB@colorfullife.com>)
+* Fixes for building on BeOS (Sam TH <sam@uchicago.edu> and David Reid
+  <dreid@jetnet.co.uk>).
+* Add buffering to socket code for pre-BONE BeOS systems (David Reid).
+* Interface changes for hip_xml:
+ - Renamed hip_xml_add_(mixed_)handler to hip_xml_push_(mixed_)handler
+ - Documentation updates.
+ - Added HIP_ELM_UNUSED for lowest element id which should be used.
+*** MAJOR INTERFACE CHANGE ***
+ - Removed 'http_status *' pointer from http_request_dispatch.
+ - Added http_get_status(req) to retrieve the response-status information
+   instead. You don't have to declare an http_status object yourself now.
+* Similarly, added DAV_ELM_207_UNUSED for lowest element id which should
+  be used by users of dav_207_* code (incl. use of dav_propfind_*
+  code).
+* New NEON_* autoconf macro interface:
+ - Use NEON_BUNDLED if sources are bundled, otherwise NEON_LIBRARY.
+ - The NEON_XML_PARSER macro is NOT called automatically.  You must
+   call this yourself if using NEON_BUNDLED; see doc/using-neon.txt
+   for details.
+* Fix use of 'socket' in nsocket.h function prototypes (Greg Stein).
+* Remove extra backslash at line 69 of src/Makefile.incl (Dirk Bergstrom).
+* Examples directory is now a separate package.
+
+Changes in release 0.7.7:
+* Another fix for linking against a libtool-built expat (Greg Stein).
+
+Changes in release 0.7.6:
+* Better check for closed SSL connection after doing SSL_peek. (thanks
+  to Jeff Costlow <j.costlow@f5.com>).
+* Attempt at correct sock_block() implementation for SSL.
+* sock_peek() will return SOCK_CLOSED correctly.
+
+Changes in release 0.7.5:
+* Fixed workaround for linking against a libtool-built expat (Greg Stein).
+
+Changes in release 0.7.4:
+* Fix for fd leak on connect failure (David Sloat <d.sloat@f5.com>).
+* Fix for Digest auth against IIS5 (David Sloat).
+* Workaround for linking against a libtool-built libexpat.la (Greg Stein).
+
+Changes in release 0.7.3:
+* Check for -lsocket and -linet in configure.
+* Workaround for SSL problems.
+
+Changes in release 0.7.2:
+* Define SHELL in Makefile (thanks to Eric Mumpower <nocturne@arepa.com>).
+* Added 'all' target to Makefile (Greg Stein <gstein@lyra.org>)
+* Added '--with-expat' argument to configure (Greg Stein)
+* Added 'dav_propfind_destroy' function.
+
+Changes in release 0.7.1:
+* Don't register response body/header authentication callbacks if no 
+  credentials-supplying callback has been registered (speed optimisation).
+
+Changes in release 0.7.0:
+* Deprecated use of 'NULL' to http_add_response_header_handler.
+  New interface, http_add_response_header_catcher, to register
+  a callback which is passed ALL response headers regardless of name.
+* Speed optimisation (~10%?): storing response-header handlers in a
+  hash table for faster look.
+* New SBUFFER_CAST() macro for getting to the 'char *' of an sbuffer
+  as fast as possible.
+
+Changes in release 0.6.1:
+* Fix for retrying request if connection is closed by server.
+* Make redirect hook work for >1 request per session.
+
+Changes in release 0.6.0:
+* New interface to allow following HTTP redirects (301/302 responses).
+  A callback must be given to get user confirmation if the request method
+  is not GET, HEAD, or PROPFIND.
+* New interface to determine whether the proxy server should be used
+  for a given request: http_session_decide_proxy.
+* Fix nget build again. Support automatic redirects in 'nget'.
+* Add --with-extra-includes and --with-extra-libs configure parameters
+  to point configure at 
+
+Changes in release 0.5.1:
+* Prevent segfault if USE_DAV_LOCKS is defined, and a locking session
+  is not registered (thanks to David Sloat).
+
+Changes in release 0.5.0:
+* Rename xmalloc, xstrdup etc to ne_malloc, ne_strdup etc.
+* Some speed optimisation in response-header reading.
+* Use 'off_t' rather than 'size_t' in sock_progress callback,
+  sock_readfile_blocked, and sock_transfer.
+
+Changes in release 0.4.2:
+* Fix for sending request bodies after getting 100-continue response.
+
+Changes in release 0.4.1:
+* Fix nget build.
+
+Changes in release 0.4.0:
+* Install library headers into .../include/neon not .../include/libneon
+* Install all necessary library headers.
+* Compile support for WebDAV locking throughout the library
+* Rename md5.h to neon_md5.h (avoids conflict with md5.h in OpenSSL)
+* Rename socket.h to nsocket.h (avoids possible conflict with C library)
+* Update licensing notice on macros/neon*.m4: note that these files are
+  NOT under the LGPL, and can be used in other packages regardless of
+  the license the package uses.
+* Update NEON_LIBRARY m4 function to allow optional specification of
+  names of bundled neon/expat source directories.
+* Increase socket read timeout to 60 seconds.
+* Added an POST method: from Sander Alberink <sander.alberink@cmg.nl>.
+* Added 'http_get_request_headers' to return the sbuffer containing
+  all request headers.
+* Allow passing NULL as name to http_add_response_header_handler: 
+  the handler callback is passed the entire header string, of ALL 
+  response headers.
+
+Changes in release 0.3.1:
+* Compile fix for dav_locks.c (thanks to Paul D'Anna)
+
+Changes in release 0.3.0:
+* Rewrite of socket handling layer. All sock_* functions changed.
+* Added basic SSL support: --with-ssl (requires OpenSSL).
+  NOTE: Certificates are NOT presented for verification.
+* 'nget' accepts URL's using the 'https' scheme.
+* New example program, 'nserver', to display the Server: string, 
+  e.g. 'nserver https://www.eu.c2.net/'
+* Fixed request re-send when persistent connection times out.
+* "Hooks" support: allow external hooks into the HTTP request/
+  response dispatch loop.
+* New printf-style interface for adding request headers.
+* Make symbols used in header files C++-safe (Tom Bednarz).
+* WebDAV locking support: lock discovery, LOCK (exclusive/shared)
+  UNLOCK. "If:" headers are sent as appropriate. Simple interface
+  for implementors of new methods to indicate which locks are 
+  required for the method.
+* Primitive HTTP cookies support.
+* Primitive hack at a GNOME-based GUI example program "nbrowse".
+  Enable build with --enable-gnome-examples. It crashes, and
+  not much else. Requires GNOME and POSIX threads. Example usage:
+       'nbrowse dav.ics.uci.edu /msdav/'
+  Many thanks to Lee Mallabone for Gtk help, and showing how to
+  use Gtk and threads. 
+
+Changes in release 0.2.0:
+* Use libtool: new configure options to select whether to build
+  shared and/or static libraries. Should build shared libraries
+  portably now.  
+* Complete rewrite of the hip_xml interface to use opaque pointers.
+  New functions: hip_xml_create, hip_xml_destroy: create parser.
+  hip_xml_{set,get}_error: Access to error string.
+  hip_xml_add_handler: Register callbacks for a set of elements.
+  hip_xml_valid: Returns whether the parse was valid or not.
+  Removed functions: hip_xml_init, hip_xml_finish.
+* Removed functions made reduntant by above changes in dav_207.
+* Don't include config.h in header files
+* Fix PROPFIND allprop request body (Michael Sobolev)
+* Added C++ safety macros around header files.
+* Added neon-config script for getting correct CFLAGS and LIBS
+  values for using libneon in applications.
+
+Changes in release 0.1.1:
+* Fix for short writes in GET
+
+Changes in release 0.1.0:
+* Initial release.
index accdb97..404db9e 100644 (file)
@@ -1,10 +1,52 @@
+In alphabetical order:
 
+Arun Garg <arung@pspl.co.in>
+Blair Zajac <blair@orcaware.com>
+Branko Ãˆibej <brane@xbc.nu>
+Daniel Berlin <dberlin@dberlin.org>
+David Sloat <d.sloat@f5.com>
+David Reid <dreid@jetnet.co.uk>
+Dirk Bergstrom <dirk@juniper.net>
+Gerald Richter <richter@ecos.de>
 Greg Stein <gstein@lyra.org>
+Gregor Bornemann <Gregor.Bornemann@germany.sun.com>
+Jeff Johnson <jbj@redhat.com>
+Jeremy Elson <jelson@circlemud.org>
+Jim Whitehead <ejw@cse.ucsc.edu>
+Johan Lindh <johan@linkdata.se>
+Justin Erenkrantz <jerenkrantz@apache.org>
+Kai Sommerfeld <kai.sommerfeld@germany.sun.com>
+Keith Wannamaker <keith@wannamaker.org>
+Lee Mallabone <lee0@callnetuk.com>
+Magnus Sirwiö <sirwio@hotmail.com>
+Markus Mueller <markus-m.mueller@ubs.com>
+Max Bowsher <maxb@ukf.net>
 Michael Sobolev <mss@despair.spb.ru>
+Mike Rosellini <m@icopyright.com>
+Mo DeJong <mdejong@cygnus.com>
+Noriaki Takamiya <takamiya@po.ntts.co.jp>
+Olof Oberg <mill@pedgr571.sn.umu.se>
+Pawel Golaszewski <blues@ds.pg.gda.pl>
+Peter Boos <PediB@colorfullife.com>
+Peter Moulder <pjm@bofh.asn.au>
+rado <dzusto@yahoo.com>
+Risko Gergely <risko@risko.hu>
+Rodney Dawes <dobey@ximian.com>
+Sam TH <sam@uchicago.edu>
+Sander Alberink <sander.alberink@cmg.nl>
+Sander Striker <striker@apache.org>
+Stefan Esser <s.esser@e-matters.de>
+Shane Mayer <shanemayer42@yahoo.com>
+Taisuke Yamada <tai@iij.ad.jp>
+Teng Xu <txu@soe.ucsc.edu>
+Tom Bednarz <tombednarz@hotmail.com>
+Tom Lee <i_am_gnomey@hotmail.com>
+Torsten Kalix <torsten.kalix@bredex.de>
+Wilfredo Sánchez <wsanchez@mit.edu>
 
 Originators of stolen code:
 
+Tommi Komulainen <Tommi.Komulainen@iki.fi>
 Daniel Veillard <Daniel.Veillard@w3.org>
 Eric S Raymond <esr@snark.thyrsus.com>
 Ulrich Drepper <drepper@gnu.org>
-
index 5556706..fcce1d3 100644 (file)
--- a/neon/TODO
+++ b/neon/TODO
 
 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
+For one-point-oh
+----------------
 
-2. Add proper domain support to authentication code. (requires full
-   URI parsing support). Need to tell the auth layer the server
-   details.
+23. Mechanism for aborting a request mid-response; e.g., when a GET
+    fails due to out of disk space, abort the download.
+
+31. Make it threadsafe:
+    socket.c: getservbyname -> getservbyname_r.
+
+38. Replace all use of split_string/pair_string with ne_token.
 
-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?
+40. XML body acceptance callback should check Content-Type. Should
+    also pass encoding to expat if one is given (how about libxml?).
+    Recent mod_dav's return XML bodies in 424 responses which need
+    displaying properly.
 
-4. Better cnonce generation for authentication: use /dev/random or
-   whatever like mod_auth_digest.
+44. Finer-grained connection status feedback, i.e., "Sent Request",
+    "Got response status-line"... "Reading response body"
 
-5. Add request hooks to remove all authentication code from
-   http_request.c.
+58. 2616 is quite strict about when to retry non-idempotent requests
+    and when not to.  neon may not be compliant here.
 
-6. PUT/GET with ranges... http_get_range
+61. Make everything namespace-safe:
+       remove split_string/pair_string.
 
-7. hip_xml/dav_207/dav_prop might need a revamp.
+62. Select which auth mechanisms are allowed, e.g. JUST SAY NO to
+    basic might very well be useful to some apps.
 
-   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:
+63. Unconditionally turn off Nagle algorithm.
 
-   1) Allowing "sub-handler" for allowing parsing property element
-   contents independantly of the overally 207 response parse.
+Longer term
+-----------
 
-   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...
+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
+   DON'T do this inside ne_basic.c, do it separately in
+   ne_author.c or something. 
 
-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:
+2. Add proper domain support to authentication code. (requires full
+   URI parsing support). Need to tell the auth layer the server
+   details.
 
-   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?
+6. PUT with ranges... ne_put_range
 
 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.
+   subversion project (http://subversion.tigris.org/) who might build
+   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.
+    support for this (www.sharemation.com). The UI is probably the
+    hardest problem here.
+      => Jim Whitehead's UCI postgrad team is working on this and
+    has written a DASL implementation.
 
-16. Socket status notification (socket.c:sock_register_*) is awful.
+14. Improved request-header manipulation... some kind of indexed table
+    (a la Apache, libghttp, so we're sure we don't add the same header
+    to the request twice.  Better control over adding Cache-Control
+    headers would be good too.
 
 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.
+    ne_request.c, ne_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.
 
-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.
+20. Add decent and proper 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)
+    auth_session, so I can log into e.g. /foo/ and /bar/ (which are
+    not in the same authentication domain) and switch between them
+    without having to re-enter passwords all the time.
+
+28. Support response caching?
+
+35. Allow i18n'ization if building a shared library, iff gettext
+    support is on the system (and hence add -lintl or whatever to
+    NEON_LIBS). If system has no gettext support, then it's probably
+    impossible to support i18n in the library (although *applications*
+    can support it by bundling gettext themselves). Take a look at how
+    other libraries handle this.
+
+46. Asynchronous request-dispatching? Makes integration into GUI loop
+    easy... any other reasons?  Must leave existing request_dispatch
+    interface intact.
+
+47. Indexed table-based response-header access? Might simplify things
+    like response body acceptance callbacks (i.e., can get access to
+    Content-Type header for XML).
+
+48. Possibly, store the time of last interaction over the TCP socket,
+    call it 't'.  If the next request is made after t+20, presume the
+    persistent connection is dead, so re-connect automatically.  If we
+    don't do this, then we have two wasted write() calls making the
+    request, then failing, then re-connecting.  It's really only worth
+    doing this if this actually saves any packets on the wire, which
+    it probably doesn't. strace / tcpdump might help here.
+
+50. opendir/readdir/closedir-esque interface for PROPFIND depth 1, 
+    a la EZDAV. (cadaver has it already)
+
+53. "ne_session" concept is hazy.  Abstract out a "connection" concept
+    too, and allow >1 connection per-session in multi-threaded use,
+    somehow.
+
+57. Add function to map of status-code values to i18n-ized reason
+    phrase.
 
index 2bd9617..cc95493 100644 (file)
@@ -1,6 +1,24 @@
 #!/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
+rm -f ltconfig ltmain.sh config.cache aclocal.m4
+# remove the autoconf cache
+rm -rf autom4te*.cache
+# create a .version file for configure.in
+if test ! -f .version; then
+   # Building from CVS rather than in a release
+   echo 0.0.0-dev > .version
+   # for the documentation:
+   date +"%e %B %Y" | tr -d '\n' > doc/date.xml
+   echo -n 0.0.0-dev > doc/version.xml
+fi
+set -e
+echo -n "aclocal... "
+${ACLOCAL:-aclocal} -I macros
+echo -n "autoheader... "
+${AUTOHEADER:-autoheader}
+echo -n "libtoolize... "
+${LIBTOOLIZE:-libtoolize} --copy --force >/dev/null
+echo -n "autoconf... "
+${AUTOCONF:-autoconf} -Wall
+echo okay.
+# remove the autoconf cache
+rm -rf autom4te*.cache
index ba6c745..0ba04ce 100644 (file)
+dnl Require autoconf 2.53 for an AC_C_BIGENDIAN which supports 
+dnl cross-compiling.
+AC_PREREQ(2.53)
 
-AC_INIT(src/http_request.c)
+dnl Extract the version (sans LF) from .version, created at release-time.
+m4_define(ne_version, [m4_translit(m4_include(.version), [
+])])
+
+AC_INIT(neon, ne_version, [neon@webdav.org])
+
+AC_COPYRIGHT([Copyright (c) 2000, 2001, 2002 Joe Orton
+This configure script may be copied, distributed and modified under the 
+terms of the GNU Library General Public license; see COPYING for more details])
 
 AC_CONFIG_HEADER(config.h)
+AC_CONFIG_SRCDIR(src/ne_request.c)
+
+NEON_WITH_LIBS
 
-AC_DEFINE([_GNU_SOURCE])
+# Pass through initial LDFLAGS verbatim to neon-config, so that extra
+# libraries which are detected (e.g. OpenSSL) can still be found when
+# building using the --libs output of neon-config.
+user_LDFLAGS=$LDFLAGS
+AC_SUBST(user_LDFLAGS)
+
+# By default, allow 'make install' to work.
+ALLOW_INSTALL=yes
+AC_SUBST(ALLOW_INSTALL)
+
+# Always defined
+AC_DEFINE([_GNU_SOURCE], 1, [Unconditionally define _GNU_SOURCE])
+# Defined when neon is built as library
+AC_DEFINE(NEON_IS_LIBRARY, 1, [Define when building neon as a library])
 
-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
+dnl Avoid libtool 1.5 bug where configure fails if a C++ compiler
+dnl is not available.
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_SHARED
+AC_PROG_LIBTOOL
 
 AC_EXEEXT
 
-NEON_XML_PARSER
+top_builddir=`pwd`
+AC_SUBST(top_builddir)
+
+AC_ARG_ENABLE(webdav,
+AC_HELP_STRING([--disable-webdav], [disable WebDAV support]))
+
+if test "$enable_webdav" = "no"; then
+  NEON_WITHOUT_WEBDAV
+else
+  # Yes, we do need an XML parser. The _BUNDLED macros handle
+  # this normally.
+  NEON_NEED_XML_PARSER=yes
+fi
+
+# The bundled macros also set this, which makes sure we recurse
+# into the 'src' directory.
+NEON_BUILD_BUNDLED=yes
+AC_SUBST(NEON_BUILD_BUNDLED)
+
+# Define NEON_VERSION* and make the appropriate substitutions.
+NEON_VERSIONS
+
+# Pass the interface version on to libtool when linking libneon.la
+NEON_LINK_FLAGS="-version-info ${NEON_INTERFACE_VERSION}"
+
+# Checks to compile test suite
+NEON_TEST
 
 LIBNEON_SOURCE_CHECKS
 
-AC_OUTPUT(Makefile)
+# Use the libtool-type build.
+NEON_LIBTOOL_BUILD
+# Find an XML parser
+NEON_XML_PARSER
+# Extra checks for debugging, compiler warnings
+NEON_DEBUG
+# Leave till last to prevent CFLAGS affecting checks.
+NEON_WARNINGS
+
+CFLAGS="$CFLAGS -I\${top_builddir}"
+
+dnl Substitute NEON_VERSION for neon-config too.
+AC_SUBST(NEON_VERSION)
+
+AC_ARG_ENABLE(memleak, 
+AC_HELP_STRING([--enable-memleak], [for test builds only: enable memory leak checking]))
+
+dnl Have autoheader include the following template in config.h.in:
+AH_VERBATIM([NEON_MEMLEAK], 
+[/* Enable memory leak detection. */
+#ifdef NEON_MEMLEAK
+# include "memleak.h"
+#endif])
 
-cat <<EOF
+if test "$enable_memleak" = "yes"; then
+  CPPFLAGS="$CPPFLAGS -DNEON_MEMLEAK -I\$(top_srcdir)/src"
+  # disable 'make install'
+  ALLOW_INSTALL=memleak
+fi
 
-Using configuration:
+# Enable tests for optional features
+TESTS="\$(BASIC_TESTS)"
+HELPERS=""
+if test "$NEON_SUPPORTS_SSL" = "yes"; then
+   # Only enable SSL tests if an openssl binary is found (needed to make
+   # certs etc).
+   AC_PATH_PROG(OPENSSL, openssl, notfound)
+   if test "$OPENSSL" != "notfound"; then
+     TESTS="$TESTS \$(SSL_TESTS)"
+     HELPERS="$HELPERS \$(SSL_HELPERS)"
+   else
+     AC_MSG_WARN([no openssl binary in \$PATH: SSL tests disabled])
+   fi
+fi
+if test "$NEON_SUPPORTS_DAV" = "yes"; then
+   TESTS="$TESTS \$(DAV_TESTS)"
+fi
+if test "$NEON_SUPPORTS_ZLIB" = "yes"; then
+   TESTS="$TESTS \$(ZLIB_TESTS)"
+   HELPERS="$HELPERS \$(ZLIB_HELPERS)"
+fi
+AC_SUBST(HELPERS)
+AC_SUBST(TESTS)
+
+AC_CONFIG_FILES([neon-config], [chmod +x neon-config])
+AC_CONFIG_FILES([Makefile src/Makefile test/Makefile neon.pc])
+
+AC_OUTPUT
+
+# for VPATH builds:
+test -d test/common || mkdir test/common
+
+AC_MSG_NOTICE([Configured to build AC_PACKAGE_STRING:
 
   Install prefix:  ${prefix}
   Compiler:        ${CC}
   XML Parser:      ${neon_xml_parser_message}
+  SSL library:     ${neon_ssl_message}
+  zlib support:    ${neon_zlib_message}
+  Build libraries: Shared=${enable_shared}, Static=${enable_static}
+Now run 'make' to compile the neon library.
+])
 
-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.)
+case $ALLOW_INSTALL in
+memleak)
+  AC_MSG_NOTICE([Configured with development-only flags:
 
-EOF
+WARNING: This copy of neon has been configured with memory leak checking
+WARNING: enabled, which should only be used in a development copy of neon.
+WARNING: This neon library should not be installed for use by applications.
+]);;
+esac
diff --git a/neon/doc/biblio.xml b/neon/doc/biblio.xml
new file mode 100644 (file)
index 0000000..cda37a7
--- /dev/null
@@ -0,0 +1,74 @@
+<bibliography id="biblio"> <!-- -*- xml -*- -->
+
+<biblioentry id="bib.ssltls">
+  <abbrev>SSL-and-TLS</abbrev>
+  <title><ulink url="http://www.rtfm.com/sslbook/">SSL and
+  TLS: Designing and Building Secure Systems</ulink></title>
+  <author><firstname>Eric</firstname><surname>Rescorla</surname></author>
+  <isbn>0-201-62598-3</isbn>
+  <publisher><publishername>Addison-Wesley</publishername></publisher>
+  <pubdate>March 2001</pubdate>
+</biblioentry>
+
+<biblioentry id="bib.xsltrec">
+  <abbrev>REC-XML-names</abbrev>
+  <editor><firstname>James</firstname><surname>Clark</surname></editor>
+  <title><ulink url="http://www.w3.org/TR/xslt">XSL Transformations
+  (XSLT) Version 1.0</ulink></title> <publishername>W3C
+  Recommendation</publishername> <pubdate>16 November 1999</pubdate>
+</biblioentry>
+
+<biblioentry id="bib.rfc2616">
+  <abbrev>RFC2616</abbrev>
+  <title><ulink url="http://www.ietf.org/rfc/rfc2616.txt">Hypertext Transfer 
+  Protocol&mdash;HTTP/1.1</ulink></title>
+  <authorgroup>
+    <author><firstname>Roy</firstname><surname>Fielding</surname></author>
+    <author><firstname>Jim</firstname><surname>Gettys</surname></author>
+    <author><firstname>Jeff</firstname><surname>Mogul</surname></author>
+    <author><firstname>Henrik</firstname><surname>Frystyk</surname></author>
+    <author><firstname>Larry</firstname><surname>Masinter</surname></author>
+    <author><firstname>Paul</firstname><surname>Leach</surname></author>
+    <author><firstname>Tim</firstname><surname>Berners-Lee</surname></author>
+  </authorgroup>
+  <publishername>IETF</publishername>
+  <pubdate>June 1999</pubdate>
+</biblioentry>
+
+<biblioentry id="bib.rfc2518">
+  <abbrev>RFC2518</abbrev>
+  <title><ulink url="http://www.ietf.org/rfc/rfc2518.txt">HTTP Extensions for Distributed Authoring&mdash;WEBDAV</ulink></title>
+  <authorgroup>
+    <author><firstname>Yaron</firstname><surname>Goland</surname></author>
+    <author><firstname>Jim</firstname><surname>Whitehead</surname></author>
+    <author><firstname>Asad</firstname><surname>Faizi</surname></author>
+    <author><firstname>Steve</firstname><surname>Carter</surname></author>
+    <author><firstname>Del</firstname><surname>Jensen</surname></author>
+  </authorgroup>
+  <publishername>IETF</publishername>
+  <pubdate>February 1999</pubdate>
+</biblioentry>
+
+<biblioentry id="bib.rfc3280">
+  <abbrev>RFC3280</abbrev>
+  <title><ulink url="http://www.ietf.org/rfc/rfc3280.txt">Internet X.509 Public Key Infrastructure
+       Certificate and Certificate Revocation List (CRL) Profile</ulink></title>
+  <authorgroup>
+    <author><firstname>Russel</firstname><surname>Housley</surname></author>
+    <author><firstname>Warwick</firstname><surname>Ford</surname></author>
+    <author><firstname>Tim</firstname><surname>Polk</surname></author>
+    <author><firstname>David</firstname><surname>Solo</surname></author>
+  </authorgroup>
+  <publishername>IETF</publishername>
+  <pubdate>April 2002</pubdate>
+</biblioentry>
+
+<!-- RFCs: 2617 -->
+
+<!-- Other interesting RFCs:
+
+ 3490 : Internationalizing Domain Names in Applications (IDNA) 
+ 3493 : Basic Socket Interface Extensions for IPv6 
+ -->
+
+</bibliography>
diff --git a/neon/doc/date.xml b/neon/doc/date.xml
new file mode 100644 (file)
index 0000000..4a8caa9
--- /dev/null
@@ -0,0 +1 @@
+ 5 July 2004
\ No newline at end of file
diff --git a/neon/doc/fdl.sgml b/neon/doc/fdl.sgml
new file mode 100644 (file)
index 0000000..37470db
--- /dev/null
@@ -0,0 +1,466 @@
+<appendix id="gfdl">
+<title>GNU Free Documentation License</title>
+<!-- - GNU Project - Free Software Foundation (FSF) -->
+<!-- LINK REV="made" HREF="mailto:webmasters@gnu.org" -->
+
+
+  <!-- sect1>
+    <title>GNU Free Documentation License</title -->
+
+    <para>Version 1.1, March 2000</para>
+
+    <blockquote>
+      <para>Copyright (C) 2000  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.</para>
+    </blockquote>
+
+  <sect1 label="0">
+    <title>PREAMBLE</title>
+
+    <para>The purpose of this License is to make a manual, textbook,
+    or other written document "free" in the sense of freedom: to
+    assure everyone the effective freedom to copy and redistribute it,
+    with or without modifying it, either commercially or
+    noncommercially.  Secondarily, this License preserves for the
+    author and publisher a way to get credit for their work, while not
+    being considered responsible for modifications made by
+    others.</para>
+
+    <para>This License is a kind of "copyleft", which means that
+    derivative works of the document must themselves be free in the
+    same sense.  It complements the GNU General Public License, which
+    is a copyleft license designed for free software.</para>
+
+    <para>We have designed this License in order to use it for manuals
+    for free software, because free software needs free documentation:
+    a free program should come with manuals providing the same
+    freedoms that the software does.  But this License is not limited
+    to software manuals; it can be used for any textual work,
+    regardless of subject matter or whether it is published as a
+    printed book.  We recommend this License principally for works
+    whose purpose is instruction or reference.</para>
+  </sect1>
+
+  <sect1 label="1">
+    <title>APPLICABILITY AND DEFINITIONS</title>
+
+    <para>This License applies to any manual or other work that
+    contains a notice placed by the copyright holder saying it can be
+    distributed under the terms of this License.  The "Document",
+    below, refers to any such manual or work.  Any member of the
+    public is a licensee, and is addressed as "you".</para>
+
+    <para>A "Modified Version" of the Document means any work
+    containing the Document or a portion of it, either copied
+    verbatim, or with modifications and/or translated into another
+    language.</para>
+
+    <para>A "Secondary Section" is a named appendix or a front-matter
+    section of the Document that deals exclusively with the
+    relationship of the publishers or authors of the Document to the
+    Document's overall subject (or to related matters) and contains
+    nothing that could fall directly within that overall subject.
+    (For example, if the Document is in part a textbook of
+    mathematics, a Secondary Section may not explain any mathematics.)
+    The relationship could be a matter of historical connection with
+    the subject or with related matters, or of legal, commercial,
+    philosophical, ethical or political position regarding
+    them.</para>
+
+    <para>The "Invariant Sections" are certain Secondary Sections
+    whose titles are designated, as being those of Invariant Sections,
+    in the notice that says that the Document is released under this
+    License.</para>
+
+    <para>The "Cover Texts" are certain short passages of text that
+    are listed, as Front-Cover Texts or Back-Cover Texts, in the
+    notice that says that the Document is released under this
+    License.</para>
+
+    <para>A "Transparent" copy of the Document means a
+    machine-readable copy, represented in a format whose specification
+    is available to the general public, whose contents can be viewed
+    and edited directly and straightforwardly with generic text
+    editors or (for images composed of pixels) generic paint programs
+    or (for drawings) some widely available drawing editor, and that
+    is suitable for input to text formatters or for automatic
+    translation to a variety of formats suitable for input to text
+    formatters.  A copy made in an otherwise Transparent file format
+    whose markup has been designed to thwart or discourage subsequent
+    modification by readers is not Transparent.  A copy that is not
+    "Transparent" is called "Opaque".</para>
+
+    <para>Examples of suitable formats for Transparent copies include
+    plain ASCII without markup, Texinfo input format, LaTeX input
+    format, SGML or XML using a publicly available DTD, and
+    standard-conforming simple HTML designed for human modification.
+    Opaque formats include PostScript, PDF, proprietary formats that
+    can be read and edited only by proprietary word processors, SGML
+    or XML for which the DTD and/or processing tools are not generally
+    available, and the machine-generated HTML produced by some word
+    processors for output purposes only.</para>
+
+    <para>The "Title Page" means, for a printed book, the title page
+    itself, plus such following pages as are needed to hold, legibly,
+    the material this License requires to appear in the title page.
+    For works in formats which do not have any title page as such,
+    "Title Page" means the text near the most prominent appearance of
+    the work's title, preceding the beginning of the body of the
+    text.</para>
+  </sect1>
+
+  <sect1 label="2">
+    <title>VERBATIM COPYING</title>
+
+    <para>You may copy and distribute the Document in any medium,
+    either commercially or noncommercially, provided that this
+    License, the copyright notices, and the license notice saying this
+    License applies to the Document are reproduced in all copies, and
+    that you add no other conditions whatsoever to those of this
+    License.  You may not use technical measures to obstruct or
+    control the reading or further copying of the copies you make or
+    distribute.  However, you may accept compensation in exchange for
+    copies.  If you distribute a large enough number of copies you
+    must also follow the conditions in section 3.</para>
+
+    <para>You may also lend copies, under the same conditions stated
+    above, and you may publicly display copies.</para>
+  </sect1>
+
+  <sect1 label="3">
+    <title>COPYING IN QUANTITY</title>
+
+    <para>If you publish printed copies of the Document numbering more
+    than 100, and the Document's license notice requires Cover Texts,
+    you must enclose the copies in covers that carry, clearly and
+    legibly, all these Cover Texts: Front-Cover Texts on the front
+    cover, and Back-Cover Texts on the back cover.  Both covers must
+    also clearly and legibly identify you as the publisher of these
+    copies.  The front cover must present the full title with all
+    words of the title equally prominent and visible.  You may add
+    other material on the covers in addition.  Copying with changes
+    limited to the covers, as long as they preserve the title of the
+    Document and satisfy these conditions, can be treated as verbatim
+    copying in other respects.</para>
+
+    <para>If the required texts for either cover are too voluminous to
+    fit legibly, you should put the first ones listed (as many as fit
+    reasonably) on the actual cover, and continue the rest onto
+    adjacent pages.</para>
+
+    <para>If you publish or distribute Opaque copies of the Document
+    numbering more than 100, you must either include a
+    machine-readable Transparent copy along with each Opaque copy, or
+    state in or with each Opaque copy a publicly-accessible
+    computer-network location containing a complete Transparent copy
+    of the Document, free of added material, which the general
+    network-using public has access to download anonymously at no
+    charge using public-standard network protocols.  If you use the
+    latter option, you must take reasonably prudent steps, when you
+    begin distribution of Opaque copies in quantity, to ensure that
+    this Transparent copy will remain thus accessible at the stated
+    location until at least one year after the last time you
+    distribute an Opaque copy (directly or through your agents or
+    retailers) of that edition to the public.</para>
+
+    <para>It is requested, but not required, that you contact the
+    authors of the Document well before redistributing any large
+    number of copies, to give them a chance to provide you with an
+    updated version of the Document.</para>
+  </sect1>
+
+  <sect1 label="4">
+    <title>MODIFICATIONS</title>
+
+    <para>You may copy and distribute a Modified Version of the
+    Document under the conditions of sections 2 and 3 above, provided
+    that you release the Modified Version under precisely this
+    License, with the Modified Version filling the role of the
+    Document, thus licensing distribution and modification of the
+    Modified Version to whoever possesses a copy of it.  In addition,
+    you must do these things in the Modified Version:</para>
+
+    <orderedlist numeration="upperalpha">
+      <listitem><para>Use in the Title Page
+      (and on the covers, if any) a title distinct from that of the
+      Document, and from those of previous versions (which should, if
+      there were any, be listed in the History section of the
+      Document).  You may use the same title as a previous version if
+      the original publisher of that version gives permission.</para>
+      </listitem>
+
+      <listitem><para>List on the Title Page,
+      as authors, one or more persons or entities responsible for
+      authorship of the modifications in the Modified Version,
+      together with at least five of the principal authors of the
+      Document (all of its principal authors, if it has less than
+      five).</para>
+      </listitem>
+
+      <listitem><para>State on the Title page
+      the name of the publisher of the Modified Version, as the
+      publisher.</para>
+      </listitem>
+
+      <listitem><para>Preserve all the
+      copyright notices of the Document.</para>
+      </listitem>
+
+      <listitem><para>Add an appropriate
+      copyright notice for your modifications adjacent to the other
+      copyright notices.</para>
+      </listitem>
+
+      <listitem><para>Include, immediately
+      after the copyright notices, a license notice giving the public
+      permission to use the Modified Version under the terms of this
+      License, in the form shown in the Addendum below.</para>
+      </listitem>
+
+      <listitem><para>Preserve in that license
+      notice the full lists of Invariant Sections and required Cover
+      Texts given in the Document's license notice.</para>
+      </listitem>
+
+      <listitem><para>Include an unaltered
+      copy of this License.</para>
+      </listitem>
+
+      <listitem><para>Preserve the section
+      entitled "History", and its title, and add to it an item stating
+      at least the title, year, new authors, and publisher of the
+      Modified Version as given on the Title Page.  If there is no
+      section entitled "History" in the Document, create one stating
+      the title, year, authors, and publisher of the Document as given
+      on its Title Page, then add an item describing the Modified
+      Version as stated in the previous sentence.</para>
+      </listitem>
+
+      <listitem><para>Preserve the network
+      location, if any, given in the Document for public access to a
+      Transparent copy of the Document, and likewise the network
+      locations given in the Document for previous versions it was
+      based on.  These may be placed in the "History" section.  You
+      may omit a network location for a work that was published at
+      least four years before the Document itself, or if the original
+      publisher of the version it refers to gives permission.</para>
+      </listitem>
+
+      <listitem><para>In any section entitled
+      "Acknowledgements" or "Dedications", preserve the section's
+      title, and preserve in the section all the substance and tone of
+      each of the contributor acknowledgements and/or dedications
+      given therein.</para>
+      </listitem>
+
+      <listitem><para>Preserve all the
+      Invariant Sections of the Document, unaltered in their text and
+      in their titles.  Section numbers or the equivalent are not
+      considered part of the section titles.</para>
+      </listitem>
+
+      <listitem><para>Delete any section
+      entitled "Endorsements".  Such a section may not be included in
+      the Modified Version.</para>
+      </listitem>
+
+      <listitem><para>Do not retitle any
+      existing section as "Endorsements" or to conflict in title with
+      any Invariant Section.</para>
+      </listitem>
+    </orderedlist>
+    
+    <para>If the Modified Version includes new front-matter sections
+    or appendices that qualify as Secondary Sections and contain no
+    material copied from the Document, you may at your option
+    designate some or all of these sections as invariant.  To do this,
+    add their titles to the list of Invariant Sections in the Modified
+    Version's license notice.  These titles must be distinct from any
+    other section titles.</para>
+
+    <para>You may add a section entitled "Endorsements", provided it
+    contains nothing but endorsements of your Modified Version by
+    various parties--for example, statements of peer review or that
+    the text has been approved by an organization as the authoritative
+    definition of a standard.</para>
+
+    <para>You may add a passage of up to five words as a Front-Cover
+    Text, and a passage of up to 25 words as a Back-Cover Text, to the
+    end of the list of Cover Texts in the Modified Version.  Only one
+    passage of Front-Cover Text and one of Back-Cover Text may be
+    added by (or through arrangements made by) any one entity.  If the
+    Document already includes a cover text for the same cover,
+    previously added by you or by arrangement made by the same entity
+    you are acting on behalf of, you may not add another; but you may
+    replace the old one, on explicit permission from the previous
+    publisher that added the old one.</para>
+
+    <para>The author(s) and publisher(s) of the Document do not by
+    this License give permission to use their names for publicity for
+    or to assert or imply endorsement of any Modified Version.</para>
+  </sect1>
+
+  <sect1 label="5">
+    <title>COMBINING DOCUMENTS</title>
+
+    <para>You may combine the Document with other documents released
+    under this License, under the terms defined in section 4 above for
+    modified versions, provided that you include in the combination
+    all of the Invariant Sections of all of the original documents,
+    unmodified, and list them all as Invariant Sections of your
+    combined work in its license notice.</para>
+
+    <para>The combined work need only contain one copy of this
+    License, and multiple identical Invariant Sections may be replaced
+    with a single copy.  If there are multiple Invariant Sections with
+    the same name but different contents, make the title of each such
+    section unique by adding at the end of it, in parentheses, the
+    name of the original author or publisher of that section if known,
+    or else a unique number.  Make the same adjustment to the section
+    titles in the list of Invariant Sections in the license notice of
+    the combined work.</para>
+
+    <para>In the combination, you must combine any sections entitled
+    "History" in the various original documents, forming one section
+    entitled "History"; likewise combine any sections entitled
+    "Acknowledgements", and any sections entitled "Dedications".  You
+    must delete all sections entitled "Endorsements."</para>
+  </sect1>
+
+  <sect1 label="6">
+    <title>COLLECTIONS OF DOCUMENTS</title>
+
+    <para>You may make a collection consisting of the Document and
+    other documents released under this License, and replace the
+    individual copies of this License in the various documents with a
+    single copy that is included in the collection, provided that you
+    follow the rules of this License for verbatim copying of each of
+    the documents in all other respects.</para>
+
+    <para>You may extract a single document from such a collection,
+    and distribute it individually under this License, provided you
+    insert a copy of this License into the extracted document, and
+    follow this License in all other respects regarding verbatim
+    copying of that document.</para>
+  </sect1>
+
+  <sect1 label="7">
+    <title>AGGREGATION WITH INDEPENDENT WORKS</title>
+    
+    <para>A compilation of the Document or its derivatives with other
+    separate and independent documents or works, in or on a volume of
+    a storage or distribution medium, does not as a whole count as a
+    Modified Version of the Document, provided no compilation
+    copyright is claimed for the compilation.  Such a compilation is
+    called an "aggregate", and this License does not apply to the
+    other self-contained works thus compiled with the Document, on
+    account of their being thus compiled, if they are not themselves
+    derivative works of the Document.</para>
+
+    <para>If the Cover Text requirement of section 3 is applicable to
+    these copies of the Document, then if the Document is less than
+    one quarter of the entire aggregate, the Document's Cover Texts
+    may be placed on covers that surround only the Document within the
+    aggregate.  Otherwise they must appear on covers around the whole
+    aggregate.</para>
+  </sect1>
+
+  <sect1 label="8">
+    <title>TRANSLATION</title>
+
+    <para>Translation is considered a kind of modification, so you may
+    distribute translations of the Document under the terms of section
+    4.  Replacing Invariant Sections with translations requires
+    special permission from their copyright holders, but you may
+    include translations of some or all Invariant Sections in addition
+    to the original versions of these Invariant Sections.  You may
+    include a translation of this License provided that you also
+    include the original English version of this License.  In case of
+    a disagreement between the translation and the original English
+    version of this License, the original English version will
+    prevail.</para>
+  </sect1>
+
+  <sect1 label="9">
+    <title>TERMINATION</title>
+    
+    <para>You may not copy, modify, sublicense, or distribute the
+    Document except as expressly provided for under this License.  Any
+    other attempt to copy, modify, sublicense or distribute the
+    Document 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.</para>
+  </sect1>
+
+  <sect1 label="10">
+    <title>FUTURE REVISIONS OF THIS LICENSE</title>
+
+    <para>The Free Software Foundation may publish new, revised
+    versions of the GNU Free Documentation 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.  See <ulink
+    url="http://www.gnu.org/copyleft/">http://www.gnu.org/copyleft/</ulink>.</para>
+
+    <para>Each version of the License is given a distinguishing
+    version number.  If the Document specifies that a particular
+    numbered version of this License "or any later version" applies to
+    it, you have the option of following the terms and conditions
+    either of that specified version or of any later version that has
+    been published (not as a draft) by the Free Software Foundation.
+    If the Document does not specify a version number of this License,
+    you may choose any version ever published (not as a draft) by the
+    Free Software Foundation.</para>
+  </sect1>
+
+  <sect1 label="">
+    <title>How to use this License for your documents</title>
+
+    <para>To use this License in a document you have written, include
+    a copy of the License in the document and put the following
+    copyright and license notices just after the title page:</para>
+
+<blockquote><para>
+      Copyright (c)  YEAR  YOUR NAME.
+      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 the Invariant Sections being LIST THEIR TITLES, with the
+      Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+      A copy of the license is included in the section entitled "GNU
+      Free Documentation License".
+</para></blockquote>
+
+    <para>If you have no Invariant Sections, write "with no Invariant
+    Sections" instead of saying which ones are invariant.  If you have
+    no Front-Cover Texts, write "no Front-Cover Texts" instead of
+    "Front-Cover Texts being LIST"; likewise for Back-Cover
+    Texts.</para>
+
+    <para>If your document contains nontrivial examples of program
+    code, we recommend releasing these examples in parallel under your
+    choice of free software license, such as the GNU General Public
+    License, to permit their use in free software.</para>
+  </sect1>
+
+</appendix>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-parent-document: ("referenz.sgml" "appendix")
+sgml-exposed-tags:nil
+sgml-local-ecat-files:nil
+sgml-local-catalogs: CATALOG
+sgml-validate-command: "nsgmls -s referenz.sgml"
+ispell-skip-sgml: t
+End:
+-->
diff --git a/neon/doc/feat.xml b/neon/doc/feat.xml
new file mode 100644 (file)
index 0000000..547ab9f
--- /dev/null
@@ -0,0 +1,78 @@
+    <sect1 id="features">
+      <title>Feature list</title>
+
+      <para>The major features of the neon library are as follows:</para>
+
+      <itemizedlist>
+
+       <listitem><para>A high-level interface to common HTTP and WebDAV
+methods.  This allows you to easily dispatch a GET or a MKCOL request
+against a resource with a single function call.</para></listitem>
+
+       <listitem><para>A low-level interface for HTTP request
+handling; allowing you to implement requests using arbitrary methods
+and request headers, capture arbitrary response headers, and so
+on.</para></listitem>
+
+       <listitem><para>Persistent connection support; neon groups a
+set of requests to a server into a "session"; requests within that
+session can use a persistent (also known as "keep-alive")
+connection.</para></listitem>
+
+       <listitem><para>Modern HTTP authentication support: a complete
+implementation of the new authentication standard, RFC2617,
+supporting the Digest (MD5) and Basic schemes, including integrity
+checking.  Credentials are supplied by an application-defined
+callback.</para></listitem>
+
+       <listitem><para>Proxy server support; a session can be set to
+use a proxy server.  Authentication is supported for the Proxy as well
+as the origin server.</para></listitem>
+
+       <listitem><para>Complete SSL support; a simple interface for
+enabling SSL, hiding the complexity of using an SSL library directly.
+Client certificate support, callback-based server certificate
+verification, along with functions to load trusted CA
+certificates.</para></listitem>
+
+<!--
+       <listitem><para>Compression support.</para></listitem>
+-->
+
+       <listitem><para>Generic XML parsing interface for handling XML
+response bodies using SAX-like callbacks.  Both the expat and libxml
+XML parser libraries are supported.</para></listitem>
+
+       <listitem><para>WebDAV metadata support; set and remove
+properties, query properties (PROPFIND); simple interface for
+retrieving "flat" byte-string properties, more advanced support for
+parsing "complex" XML structured properties.</para></listitem>
+
+<!--
+       <listitem><para>WebDAV locking support</para></listitem>
+-->
+
+       <listitem><para>Build environment support: the neon source
+tree is designed so that it can be embedded in your application's
+build tree; autoconf macros are supplied for integration.  To get
+started quickly a <xref linkend="refconfig"/> script is included,
+to easily determine how to compile and link against an installed copy
+of neon</para></listitem>
+
+       <listitem><para>Complete test suite: the neon test suite
+comprises half as many lines of source code as the library itself,
+including many tests for protocol compliance in network behaviour, and
+that the library implementation meets the guarantees made by the
+API.</para> </listitem>
+
+<!-- 
+
+       <listitem><para>Thorough documentation: neon documentation is
+provided in HTML and man page formats (from a single DocBook XML
+source)</para></listitem>
+
+-->
+
+      </itemizedlist>
+       
+    </sect1>
index 42fc397..a96867d 100644 (file)
 </xsl:attribute-set>
 
 <!-- use sane ANSI C function prototypes -->
-<xsl:variable name="funcsynopsis.style">ansi</xsl:variable>
+<xsl:variable name="funcsynopsis.style" select="'ansi'"/>
+
+<!-- split each sect1 into a separate chunk -->
+<xsl:variable name="chunk.first.sections" select="1"/>
 
 <!-- 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"/>
 
+<!-- don't include manual page numbers in refentry cross-references, they
+  look weird -->
+<xsl:variable name="refentry.xref.manvolnum" select="0"/>
+
+<!-- do generate variablelist's as tables -->
+<xsl:variable name="variablelist.as.table" select="1"/>
+
+<!-- and css'ize the tables so they can look pretty -->
+<xsl:variable name="table.borders.with.css" select="1"/>
+
+<!-- disable ugly tabular output -->
+<xsl:variable name="funcsynopsis.tabular.threshold" select="99999"/>
+
+<!-- change some presentation choices --> 
+<xsl:template match="type">
+  <xsl:call-template name="inline.italicseq"/>
+</xsl:template>
+
+<xsl:template match="parameter">
+  <xsl:call-template name="inline.monoseq"/>
+</xsl:template>
+
+<!-- enclose the whole funcprototype in <code> -->
+<xsl:template match="funcprototype" mode="ansi-nontabular">
+  <div class="funcprototype"><code>
+     <xsl:apply-templates mode="ansi-nontabular"/>
+  </code></div>
+</xsl:template>
+
 </xsl:stylesheet>
 
diff --git a/neon/doc/html/apas01.html b/neon/doc/html/apas01.html
new file mode 100644 (file)
index 0000000..a0671cc
--- /dev/null
@@ -0,0 +1,18 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>PREAMBLE</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="next" href="apas02.html" title="APPLICABILITY AND DEFINITIONS"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">PREAMBLE</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="gfdl.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas02.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3036814"></a>PREAMBLE</h2></div></div><div></div></div><p>The purpose of this License is to make a manual, textbook,
+    or other written document "free" in the sense of freedom: to
+    assure everyone the effective freedom to copy and redistribute it,
+    with or without modifying it, either commercially or
+    noncommercially.  Secondarily, this License preserves for the
+    author and publisher a way to get credit for their work, while not
+    being considered responsible for modifications made by
+    others.</p><p>This License is a kind of "copyleft", which means that
+    derivative works of the document must themselves be free in the
+    same sense.  It complements the GNU General Public License, which
+    is a copyleft license designed for free software.</p><p>We have designed this License in order to use it for manuals
+    for free software, because free software needs free documentation:
+    a free program should come with manuals providing the same
+    freedoms that the software does.  But this License is not limited
+    to software manuals; it can be used for any textual work,
+    regardless of subject matter or whether it is published as a
+    printed book.  We recommend this License principally for works
+    whose purpose is instruction or reference.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="gfdl.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Appendix A. GNU Free Documentation License </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> APPLICABILITY AND DEFINITIONS</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas02.html b/neon/doc/html/apas02.html
new file mode 100644 (file)
index 0000000..ad6ccd9
--- /dev/null
@@ -0,0 +1,49 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>APPLICABILITY AND DEFINITIONS</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas01.html" title="PREAMBLE"><link rel="next" href="apas03.html" title="VERBATIM COPYING"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">APPLICABILITY AND DEFINITIONS</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas01.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas03.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3036825"></a>APPLICABILITY AND DEFINITIONS</h2></div></div><div></div></div><p>This License applies to any manual or other work that
+    contains a notice placed by the copyright holder saying it can be
+    distributed under the terms of this License.  The "Document",
+    below, refers to any such manual or work.  Any member of the
+    public is a licensee, and is addressed as "you".</p><p>A "Modified Version" of the Document means any work
+    containing the Document or a portion of it, either copied
+    verbatim, or with modifications and/or translated into another
+    language.</p><p>A "Secondary Section" is a named appendix or a front-matter
+    section of the Document that deals exclusively with the
+    relationship of the publishers or authors of the Document to the
+    Document's overall subject (or to related matters) and contains
+    nothing that could fall directly within that overall subject.
+    (For example, if the Document is in part a textbook of
+    mathematics, a Secondary Section may not explain any mathematics.)
+    The relationship could be a matter of historical connection with
+    the subject or with related matters, or of legal, commercial,
+    philosophical, ethical or political position regarding
+    them.</p><p>The "Invariant Sections" are certain Secondary Sections
+    whose titles are designated, as being those of Invariant Sections,
+    in the notice that says that the Document is released under this
+    License.</p><p>The "Cover Texts" are certain short passages of text that
+    are listed, as Front-Cover Texts or Back-Cover Texts, in the
+    notice that says that the Document is released under this
+    License.</p><p>A "Transparent" copy of the Document means a
+    machine-readable copy, represented in a format whose specification
+    is available to the general public, whose contents can be viewed
+    and edited directly and straightforwardly with generic text
+    editors or (for images composed of pixels) generic paint programs
+    or (for drawings) some widely available drawing editor, and that
+    is suitable for input to text formatters or for automatic
+    translation to a variety of formats suitable for input to text
+    formatters.  A copy made in an otherwise Transparent file format
+    whose markup has been designed to thwart or discourage subsequent
+    modification by readers is not Transparent.  A copy that is not
+    "Transparent" is called "Opaque".</p><p>Examples of suitable formats for Transparent copies include
+    plain ASCII without markup, Texinfo input format, LaTeX input
+    format, SGML or XML using a publicly available DTD, and
+    standard-conforming simple HTML designed for human modification.
+    Opaque formats include PostScript, PDF, proprietary formats that
+    can be read and edited only by proprietary word processors, SGML
+    or XML for which the DTD and/or processing tools are not generally
+    available, and the machine-generated HTML produced by some word
+    processors for output purposes only.</p><p>The "Title Page" means, for a printed book, the title page
+    itself, plus such following pages as are needed to hold, legibly,
+    the material this License requires to appear in the title page.
+    For works in formats which do not have any title page as such,
+    "Title Page" means the text near the most prominent appearance of
+    the work's title, preceding the beginning of the body of the
+    text.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas01.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas03.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">PREAMBLE </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> VERBATIM COPYING</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas03.html b/neon/doc/html/apas03.html
new file mode 100644 (file)
index 0000000..9b5ff2c
--- /dev/null
@@ -0,0 +1,11 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>VERBATIM COPYING</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas02.html" title="APPLICABILITY AND DEFINITIONS"><link rel="next" href="apas04.html" title="COPYING IN QUANTITY"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">VERBATIM COPYING</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas02.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas04.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037781"></a>VERBATIM COPYING</h2></div></div><div></div></div><p>You may copy and distribute the Document in any medium,
+    either commercially or noncommercially, provided that this
+    License, the copyright notices, and the license notice saying this
+    License applies to the Document are reproduced in all copies, and
+    that you add no other conditions whatsoever to those of this
+    License.  You may not use technical measures to obstruct or
+    control the reading or further copying of the copies you make or
+    distribute.  However, you may accept compensation in exchange for
+    copies.  If you distribute a large enough number of copies you
+    must also follow the conditions in section 3.</p><p>You may also lend copies, under the same conditions stated
+    above, and you may publicly display copies.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas04.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">APPLICABILITY AND DEFINITIONS </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> COPYING IN QUANTITY</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas04.html b/neon/doc/html/apas04.html
new file mode 100644 (file)
index 0000000..8ccf414
--- /dev/null
@@ -0,0 +1,31 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>COPYING IN QUANTITY</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas03.html" title="VERBATIM COPYING"><link rel="next" href="apas05.html" title="MODIFICATIONS"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">COPYING IN QUANTITY</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas03.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas05.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037809"></a>COPYING IN QUANTITY</h2></div></div><div></div></div><p>If you publish printed copies of the Document numbering more
+    than 100, and the Document's license notice requires Cover Texts,
+    you must enclose the copies in covers that carry, clearly and
+    legibly, all these Cover Texts: Front-Cover Texts on the front
+    cover, and Back-Cover Texts on the back cover.  Both covers must
+    also clearly and legibly identify you as the publisher of these
+    copies.  The front cover must present the full title with all
+    words of the title equally prominent and visible.  You may add
+    other material on the covers in addition.  Copying with changes
+    limited to the covers, as long as they preserve the title of the
+    Document and satisfy these conditions, can be treated as verbatim
+    copying in other respects.</p><p>If the required texts for either cover are too voluminous to
+    fit legibly, you should put the first ones listed (as many as fit
+    reasonably) on the actual cover, and continue the rest onto
+    adjacent pages.</p><p>If you publish or distribute Opaque copies of the Document
+    numbering more than 100, you must either include a
+    machine-readable Transparent copy along with each Opaque copy, or
+    state in or with each Opaque copy a publicly-accessible
+    computer-network location containing a complete Transparent copy
+    of the Document, free of added material, which the general
+    network-using public has access to download anonymously at no
+    charge using public-standard network protocols.  If you use the
+    latter option, you must take reasonably prudent steps, when you
+    begin distribution of Opaque copies in quantity, to ensure that
+    this Transparent copy will remain thus accessible at the stated
+    location until at least one year after the last time you
+    distribute an Opaque copy (directly or through your agents or
+    retailers) of that edition to the public.</p><p>It is requested, but not required, that you contact the
+    authors of the Document well before redistributing any large
+    number of copies, to give them a chance to provide you with an
+    updated version of the Document.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas03.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas05.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">VERBATIM COPYING </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> MODIFICATIONS</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas05.html b/neon/doc/html/apas05.html
new file mode 100644 (file)
index 0000000..c94b47b
--- /dev/null
@@ -0,0 +1,74 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>MODIFICATIONS</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas04.html" title="COPYING IN QUANTITY"><link rel="next" href="apas06.html" title="COMBINING DOCUMENTS"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">MODIFICATIONS</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas04.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas06.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037880"></a>MODIFICATIONS</h2></div></div><div></div></div><p>You may copy and distribute a Modified Version of the
+    Document under the conditions of sections 2 and 3 above, provided
+    that you release the Modified Version under precisely this
+    License, with the Modified Version filling the role of the
+    Document, thus licensing distribution and modification of the
+    Modified Version to whoever possesses a copy of it.  In addition,
+    you must do these things in the Modified Version:</p><div class="orderedlist"><ol type="A"><li><p>Use in the Title Page
+      (and on the covers, if any) a title distinct from that of the
+      Document, and from those of previous versions (which should, if
+      there were any, be listed in the History section of the
+      Document).  You may use the same title as a previous version if
+      the original publisher of that version gives permission.</p></li><li><p>List on the Title Page,
+      as authors, one or more persons or entities responsible for
+      authorship of the modifications in the Modified Version,
+      together with at least five of the principal authors of the
+      Document (all of its principal authors, if it has less than
+      five).</p></li><li><p>State on the Title page
+      the name of the publisher of the Modified Version, as the
+      publisher.</p></li><li><p>Preserve all the
+      copyright notices of the Document.</p></li><li><p>Add an appropriate
+      copyright notice for your modifications adjacent to the other
+      copyright notices.</p></li><li><p>Include, immediately
+      after the copyright notices, a license notice giving the public
+      permission to use the Modified Version under the terms of this
+      License, in the form shown in the Addendum below.</p></li><li><p>Preserve in that license
+      notice the full lists of Invariant Sections and required Cover
+      Texts given in the Document's license notice.</p></li><li><p>Include an unaltered
+      copy of this License.</p></li><li><p>Preserve the section
+      entitled "History", and its title, and add to it an item stating
+      at least the title, year, new authors, and publisher of the
+      Modified Version as given on the Title Page.  If there is no
+      section entitled "History" in the Document, create one stating
+      the title, year, authors, and publisher of the Document as given
+      on its Title Page, then add an item describing the Modified
+      Version as stated in the previous sentence.</p></li><li><p>Preserve the network
+      location, if any, given in the Document for public access to a
+      Transparent copy of the Document, and likewise the network
+      locations given in the Document for previous versions it was
+      based on.  These may be placed in the "History" section.  You
+      may omit a network location for a work that was published at
+      least four years before the Document itself, or if the original
+      publisher of the version it refers to gives permission.</p></li><li><p>In any section entitled
+      "Acknowledgements" or "Dedications", preserve the section's
+      title, and preserve in the section all the substance and tone of
+      each of the contributor acknowledgements and/or dedications
+      given therein.</p></li><li><p>Preserve all the
+      Invariant Sections of the Document, unaltered in their text and
+      in their titles.  Section numbers or the equivalent are not
+      considered part of the section titles.</p></li><li><p>Delete any section
+      entitled "Endorsements".  Such a section may not be included in
+      the Modified Version.</p></li><li><p>Do not retitle any
+      existing section as "Endorsements" or to conflict in title with
+      any Invariant Section.</p></li></ol></div><p>If the Modified Version includes new front-matter sections
+    or appendices that qualify as Secondary Sections and contain no
+    material copied from the Document, you may at your option
+    designate some or all of these sections as invariant.  To do this,
+    add their titles to the list of Invariant Sections in the Modified
+    Version's license notice.  These titles must be distinct from any
+    other section titles.</p><p>You may add a section entitled "Endorsements", provided it
+    contains nothing but endorsements of your Modified Version by
+    various parties--for example, statements of peer review or that
+    the text has been approved by an organization as the authoritative
+    definition of a standard.</p><p>You may add a passage of up to five words as a Front-Cover
+    Text, and a passage of up to 25 words as a Back-Cover Text, to the
+    end of the list of Cover Texts in the Modified Version.  Only one
+    passage of Front-Cover Text and one of Back-Cover Text may be
+    added by (or through arrangements made by) any one entity.  If the
+    Document already includes a cover text for the same cover,
+    previously added by you or by arrangement made by the same entity
+    you are acting on behalf of, you may not add another; but you may
+    replace the old one, on explicit permission from the previous
+    publisher that added the old one.</p><p>The author(s) and publisher(s) of the Document do not by
+    this License give permission to use their names for publicity for
+    or to assert or imply endorsement of any Modified Version.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas04.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas06.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">COPYING IN QUANTITY </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> COMBINING DOCUMENTS</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas06.html b/neon/doc/html/apas06.html
new file mode 100644 (file)
index 0000000..adafa04
--- /dev/null
@@ -0,0 +1,18 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>COMBINING DOCUMENTS</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas05.html" title="MODIFICATIONS"><link rel="next" href="apas07.html" title="COLLECTIONS OF DOCUMENTS"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">COMBINING DOCUMENTS</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas05.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas07.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3038074"></a>COMBINING DOCUMENTS</h2></div></div><div></div></div><p>You may combine the Document with other documents released
+    under this License, under the terms defined in section 4 above for
+    modified versions, provided that you include in the combination
+    all of the Invariant Sections of all of the original documents,
+    unmodified, and list them all as Invariant Sections of your
+    combined work in its license notice.</p><p>The combined work need only contain one copy of this
+    License, and multiple identical Invariant Sections may be replaced
+    with a single copy.  If there are multiple Invariant Sections with
+    the same name but different contents, make the title of each such
+    section unique by adding at the end of it, in parentheses, the
+    name of the original author or publisher of that section if known,
+    or else a unique number.  Make the same adjustment to the section
+    titles in the list of Invariant Sections in the license notice of
+    the combined work.</p><p>In the combination, you must combine any sections entitled
+    "History" in the various original documents, forming one section
+    entitled "History"; likewise combine any sections entitled
+    "Acknowledgements", and any sections entitled "Dedications".  You
+    must delete all sections entitled "Endorsements."</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas05.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas07.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">MODIFICATIONS </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> COLLECTIONS OF DOCUMENTS</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas07.html b/neon/doc/html/apas07.html
new file mode 100644 (file)
index 0000000..a577d95
--- /dev/null
@@ -0,0 +1,10 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>COLLECTIONS OF DOCUMENTS</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas06.html" title="COMBINING DOCUMENTS"><link rel="next" href="apas08.html" title="AGGREGATION WITH INDEPENDENT WORKS"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">COLLECTIONS OF DOCUMENTS</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas06.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas08.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037562"></a>COLLECTIONS OF DOCUMENTS</h2></div></div><div></div></div><p>You may make a collection consisting of the Document and
+    other documents released under this License, and replace the
+    individual copies of this License in the various documents with a
+    single copy that is included in the collection, provided that you
+    follow the rules of this License for verbatim copying of each of
+    the documents in all other respects.</p><p>You may extract a single document from such a collection,
+    and distribute it individually under this License, provided you
+    insert a copy of this License into the extracted document, and
+    follow this License in all other respects regarding verbatim
+    copying of that document.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas06.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas08.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">COMBINING DOCUMENTS </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> AGGREGATION WITH INDEPENDENT WORKS</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas08.html b/neon/doc/html/apas08.html
new file mode 100644 (file)
index 0000000..f67d8d2
--- /dev/null
@@ -0,0 +1,14 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>AGGREGATION WITH INDEPENDENT WORKS</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas07.html" title="COLLECTIONS OF DOCUMENTS"><link rel="next" href="apas09.html" title="TRANSLATION"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">AGGREGATION WITH INDEPENDENT WORKS</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas07.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas09.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037588"></a>AGGREGATION WITH INDEPENDENT WORKS</h2></div></div><div></div></div><p>A compilation of the Document or its derivatives with other
+    separate and independent documents or works, in or on a volume of
+    a storage or distribution medium, does not as a whole count as a
+    Modified Version of the Document, provided no compilation
+    copyright is claimed for the compilation.  Such a compilation is
+    called an "aggregate", and this License does not apply to the
+    other self-contained works thus compiled with the Document, on
+    account of their being thus compiled, if they are not themselves
+    derivative works of the Document.</p><p>If the Cover Text requirement of section 3 is applicable to
+    these copies of the Document, then if the Document is less than
+    one quarter of the entire aggregate, the Document's Cover Texts
+    may be placed on covers that surround only the Document within the
+    aggregate.  Otherwise they must appear on covers around the whole
+    aggregate.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas07.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas09.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">COLLECTIONS OF DOCUMENTS </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> TRANSLATION</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas09.html b/neon/doc/html/apas09.html
new file mode 100644 (file)
index 0000000..73b4341
--- /dev/null
@@ -0,0 +1,11 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>TRANSLATION</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas08.html" title="AGGREGATION WITH INDEPENDENT WORKS"><link rel="next" href="apas10.html" title="TERMINATION"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">TRANSLATION</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas08.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas10.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037619"></a>TRANSLATION</h2></div></div><div></div></div><p>Translation is considered a kind of modification, so you may
+    distribute translations of the Document under the terms of section
+    4.  Replacing Invariant Sections with translations requires
+    special permission from their copyright holders, but you may
+    include translations of some or all Invariant Sections in addition
+    to the original versions of these Invariant Sections.  You may
+    include a translation of this License provided that you also
+    include the original English version of this License.  In case of
+    a disagreement between the translation and the original English
+    version of this License, the original English version will
+    prevail.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas08.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas10.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">AGGREGATION WITH INDEPENDENT WORKS </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> TERMINATION</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas10.html b/neon/doc/html/apas10.html
new file mode 100644 (file)
index 0000000..d450cec
--- /dev/null
@@ -0,0 +1,8 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>TERMINATION</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas09.html" title="TRANSLATION"><link rel="next" href="apas11.html" title="FUTURE REVISIONS OF THIS LICENSE"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">TERMINATION</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas09.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas11.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037642"></a>TERMINATION</h2></div></div><div></div></div><p>You may not copy, modify, sublicense, or distribute the
+    Document except as expressly provided for under this License.  Any
+    other attempt to copy, modify, sublicense or distribute the
+    Document 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.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas09.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas11.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">TRANSLATION </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> FUTURE REVISIONS OF THIS LICENSE</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas11.html b/neon/doc/html/apas11.html
new file mode 100644 (file)
index 0000000..0f1859c
--- /dev/null
@@ -0,0 +1,13 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>FUTURE REVISIONS OF THIS LICENSE</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas10.html" title="TERMINATION"><link rel="next" href="apas12.html" title="How to use this License for your documents"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">FUTURE REVISIONS OF THIS LICENSE</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas10.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> <a accesskey="n" href="apas12.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037662"></a>FUTURE REVISIONS OF THIS LICENSE</h2></div></div><div></div></div><p>The Free Software Foundation may publish new, revised
+    versions of the GNU Free Documentation 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.  See <a href="http://www.gnu.org/copyleft/" target="_top">http://www.gnu.org/copyleft/</a>.</p><p>Each version of the License is given a distinguishing
+    version number.  If the Document specifies that a particular
+    numbered version of this License "or any later version" applies to
+    it, you have the option of following the terms and conditions
+    either of that specified version or of any later version that has
+    been published (not as a draft) by the Free Software Foundation.
+    If the Document does not specify a version number of this License,
+    you may choose any version ever published (not as a draft) by the
+    Free Software Foundation.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas10.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas12.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">TERMINATION </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> How to use this License for your documents</td></tr></table></div></body></html>
diff --git a/neon/doc/html/apas12.html b/neon/doc/html/apas12.html
new file mode 100644 (file)
index 0000000..3c07c65
--- /dev/null
@@ -0,0 +1,19 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>How to use this License for your documents</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="gfdl.html" title="Appendix A. GNU Free Documentation License"><link rel="previous" href="apas11.html" title="FUTURE REVISIONS OF THIS LICENSE"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">How to use this License for your documents</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="apas11.html">Prev</a> </td><th width="60%" align="center">Appendix A. GNU Free Documentation License</th><td width="20%" align="right"> </td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id3037689"></a>How to use this License for your documents</h2></div></div><div></div></div><p>To use this License in a document you have written, include
+    a copy of the License in the document and put the following
+    copyright and license notices just after the title page:</p><div class="blockquote"><blockquote class="blockquote"><p>
+      Copyright (c)  YEAR  YOUR NAME.
+      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 the Invariant Sections being LIST THEIR TITLES, with the
+      Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+      A copy of the license is included in the section entitled "GNU
+      Free Documentation License".
+</p></blockquote></div><p>If you have no Invariant Sections, write "with no Invariant
+    Sections" instead of saying which ones are invariant.  If you have
+    no Front-Cover Texts, write "no Front-Cover Texts" instead of
+    "Front-Cover Texts being LIST"; likewise for Back-Cover
+    Texts.</p><p>If your document contains nontrivial examples of program
+    code, we recommend releasing these examples in parallel under your
+    choice of free software license, such as the GNU General Public
+    License, to permit their use in free software.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="apas11.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="gfdl.html">Up</a></td><td width="40%" align="right"> </td></tr><tr><td width="40%" align="left" valign="top">FUTURE REVISIONS OF THIS LICENSE </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> </td></tr></table></div></body></html>
diff --git a/neon/doc/html/api.html b/neon/doc/html/api.html
new file mode 100644 (file)
index 0000000..e4d8c9e
--- /dev/null
@@ -0,0 +1,4 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Chapter 2. The neon C language interface</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="index.html" title="neon HTTP/WebDAV client library"><link rel="previous" href="compliance.html" title="Protocol compliance"><link rel="next" href="xml.html" title="Parsing XML"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 2. The neon C language interface</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="compliance.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="xml.html">Next</a></td></tr></table><hr></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="api"></a>Chapter 2. The neon C language interface</h2></div></div><div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="xml.html">Parsing XML</a></span></dt><dd><dl><dt><span class="sect2"><a href="xml.html#xml-sax">Introduction to SAX</a></span></dt><dt><span class="sect2"><a href="xml.html#xml-stacked">Stacked SAX handlers</a></span></dt><dt><span class="sect2"><a href="xml.html#xml-state">Maintaining state</a></span></dt><dt><span class="sect2"><a href="xml.html#xml-ns">XML namespaces</a></span></dt></dl></dd></dl></div><p>The documentation for the neon interface is split between
+    this chapter, which gives a broad introduction to the abstractions
+    exposed by the library, and <a href="ref.html" title="neon API reference">neon API reference</a>, which gives a
+    function-by-function breakdown of the interface.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="compliance.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="index.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="xml.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Protocol compliance </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Parsing XML</td></tr></table></div></body></html>
diff --git a/neon/doc/html/biblio.html b/neon/doc/html/biblio.html
new file mode 100644 (file)
index 0000000..a487cec
--- /dev/null
@@ -0,0 +1,6 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Bibliography</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="index.html" title="neon HTTP/WebDAV client library"><link rel="previous" href="refvers.html" title="ne_version_match"><link rel="next" href="gfdl.html" title="Appendix A. GNU Free Documentation License"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Bibliography</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refvers.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="gfdl.html">Next</a></td></tr></table><hr></div><div class="bibliography"><div class="titlepage"><div><div><h2 class="title"><a name="biblio"></a>Bibliography</h2></div></div><div></div></div><div class="biblioentry"><a name="bib.ssltls"></a><p>[<span class="abbrev">SSL-and-TLS</span>] <span class="title"><i><a href="http://www.rtfm.com/sslbook/" target="_top">SSL and
+  TLS: Designing and Building Secure Systems</a></i>. </span><span class="author"><span class="firstname">Eric</span> <span class="surname">Rescorla</span>. </span><span class="isbn">0-201-62598-3. </span><span class="publisher"><span class="publishername">Addison-Wesley. </span></span><span class="pubdate">March 2001. </span></p></div><div class="biblioentry"><a name="bib.xsltrec"></a><p>[<span class="abbrev">REC-XML-names</span>] <span class="editor"><span class="firstname">James</span> <span class="surname">Clark</span>. </span><span class="title"><i><a href="http://www.w3.org/TR/xslt" target="_top">XSL Transformations
+  (XSLT) Version 1.0</a></i>. </span><span class="publishername">W3C
+  Recommendation. </span><span class="pubdate">16 November 1999. </span></p></div><div class="biblioentry"><a name="bib.rfc2616"></a><p>[<span class="abbrev">RFC2616</span>] <span class="title"><i><a href="http://www.ietf.org/rfc/rfc2616.txt" target="_top">Hypertext Transfer 
+  Protocol&#8212;HTTP/1.1</a></i>. </span><span class="authorgroup"><span class="firstname">Roy</span> <span class="surname">Fielding</span>, <span class="firstname">Jim</span> <span class="surname">Gettys</span>, <span class="firstname">Jeff</span> <span class="surname">Mogul</span>, <span class="firstname">Henrik</span> <span class="surname">Frystyk</span>, <span class="firstname">Larry</span> <span class="surname">Masinter</span>, <span class="firstname">Paul</span> <span class="surname">Leach</span>, and <span class="firstname">Tim</span> <span class="surname">Berners-Lee</span>. </span><span class="publishername">IETF. </span><span class="pubdate">June 1999. </span></p></div><div class="biblioentry"><a name="bib.rfc2518"></a><p>[<span class="abbrev">RFC2518</span>] <span class="title"><i><a href="http://www.ietf.org/rfc/rfc2518.txt" target="_top">HTTP Extensions for Distributed Authoring&#8212;WEBDAV</a></i>. </span><span class="authorgroup"><span class="firstname">Yaron</span> <span class="surname">Goland</span>, <span class="firstname">Jim</span> <span class="surname">Whitehead</span>, <span class="firstname">Asad</span> <span class="surname">Faizi</span>, <span class="firstname">Steve</span> <span class="surname">Carter</span>, and <span class="firstname">Del</span> <span class="surname">Jensen</span>. </span><span class="publishername">IETF. </span><span class="pubdate">February 1999. </span></p></div><div class="biblioentry"><a name="bib.rfc3280"></a><p>[<span class="abbrev">RFC3280</span>] <span class="title"><i><a href="http://www.ietf.org/rfc/rfc3280.txt" target="_top">Internet X.509 Public Key Infrastructure
+       Certificate and Certificate Revocation List (CRL) Profile</a></i>. </span><span class="authorgroup"><span class="firstname">Russel</span> <span class="surname">Housley</span>, <span class="firstname">Warwick</span> <span class="surname">Ford</span>, <span class="firstname">Tim</span> <span class="surname">Polk</span>, and <span class="firstname">David</span> <span class="surname">Solo</span>. </span><span class="publishername">IETF. </span><span class="pubdate">April 2002. </span></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refvers.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="index.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="gfdl.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_version_match </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Appendix A. GNU Free Documentation License</td></tr></table></div></body></html>
diff --git a/neon/doc/html/compliance.html b/neon/doc/html/compliance.html
new file mode 100644 (file)
index 0000000..5870067
--- /dev/null
@@ -0,0 +1,25 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Protocol compliance</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="intro.html" title="Chapter 1. Introduction"><link rel="previous" href="using.html" title="How to use neon from your application"><link rel="next" href="api.html" title="Chapter 2. The neon C language interface"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Protocol compliance</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="using.html">Prev</a> </td><th width="60%" align="center">Chapter 1. Introduction</th><td width="20%" align="right"> <a accesskey="n" href="api.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="compliance"></a>Protocol compliance</h2></div></div><div></div></div><p>neon is intended to be compliant with the IETF
+       protocol standards it implements, with a few exceptions where
+       real-world use has necessitated minor deviations.  These
+       exceptions are documented in this section.</p><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="id2957718"></a>RFC 2518, HTTP Extensions for Distributed Authoring&#8212;WebDAV</h3></div></div><div></div></div><p>neon is deliberately not compliant with section
+       23.4.2, and treats property names as a (namespace-URI, name)
+       pair.  This is <a href="http://lists.w3.org/Archives/Public/w3c-dist-auth/1999OctDec/0343.html" target="_top">generally
+       considered</a> to be correct behaviour by the WebDAV
+       working group, and is likely to formally adopted in a future
+       revision of the specification.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="id3018315"></a>RFC 2616, Hypertext Transfer Protocol&#8212;HTTP/1.1</h3></div></div><div></div></div><p>There is some confusion in this specification about the
+        use of the &#8220;<span class="quote">identity</span>&#8221;
+        <i class="firstterm">transfer-coding</i>.  neon treats the
+        presence of <span class="emphasis"><em>any</em></span>
+        <tt class="literal">Transfer-Encoding</tt> response header as an
+        indication that the response message uses the
+        &#8220;<span class="quote">chunked</span>&#8221; transfer-coding.  This was the
+        suggested resolution <a href="http://lists.w3.org/Archives/Public/ietf-http-wg-old/2001SepDec/0018.html" target="_top">proposed
+        by Larry Masinter</a>.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="id3018354"></a>RFC 2617, HTTP Authentication: Basic and Digest Access Authentication</h3></div></div><div></div></div><p>neon is not strictly compliant with the quoting rules
+        given in the grammar for the <tt class="literal">Authorization</tt>
+        header.  The grammar requires that the <tt class="literal">qop</tt>
+        and <tt class="literal">algorithm</tt> parameters are not quoted,
+        however one widely deployed server implementation
+        (Microsoft® IIS 5) rejects the request if these parameters
+        are not quoted.  neon sends these parameters with
+        quotes&#8212;this is not known to cause any problems with
+        other server implementations.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="using.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="intro.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="api.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">How to use neon from your application </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 2. The neon C language interface</td></tr></table></div></body></html>
diff --git a/neon/doc/html/features.html b/neon/doc/html/features.html
new file mode 100644 (file)
index 0000000..67af25a
--- /dev/null
@@ -0,0 +1,33 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Feature list</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="intro.html" title="Chapter 1. Introduction"><link rel="previous" href="intro.html" title="Chapter 1. Introduction"><link rel="next" href="using.html" title="How to use neon from your application"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Feature list</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="intro.html">Prev</a> </td><th width="60%" align="center">Chapter 1. Introduction</th><td width="20%" align="right"> <a accesskey="n" href="using.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="features"></a>Feature list</h2></div></div><div></div></div><p>The major features of the neon library are as follows:</p><div class="itemizedlist"><ul type="disc"><li><p>A high-level interface to common HTTP and WebDAV
+methods.  This allows you to easily dispatch a GET or a MKCOL request
+against a resource with a single function call.</p></li><li><p>A low-level interface for HTTP request
+handling; allowing you to implement requests using arbitrary methods
+and request headers, capture arbitrary response headers, and so
+on.</p></li><li><p>Persistent connection support; neon groups a
+set of requests to a server into a "session"; requests within that
+session can use a persistent (also known as "keep-alive")
+connection.</p></li><li><p>Modern HTTP authentication support: a complete
+implementation of the new authentication standard, RFC2617,
+supporting the Digest (MD5) and Basic schemes, including integrity
+checking.  Credentials are supplied by an application-defined
+callback.</p></li><li><p>Proxy server support; a session can be set to
+use a proxy server.  Authentication is supported for the Proxy as well
+as the origin server.</p></li><li><p>Complete SSL support; a simple interface for
+enabling SSL, hiding the complexity of using an SSL library directly.
+Client certificate support, callback-based server certificate
+verification, along with functions to load trusted CA
+certificates.</p></li><li><p>Generic XML parsing interface for handling XML
+response bodies using SAX-like callbacks.  Both the expat and libxml
+XML parser libraries are supported.</p></li><li><p>WebDAV metadata support; set and remove
+properties, query properties (PROPFIND); simple interface for
+retrieving "flat" byte-string properties, more advanced support for
+parsing "complex" XML structured properties.</p></li><li><p>Build environment support: the neon source
+tree is designed so that it can be embedded in your application's
+build tree; autoconf macros are supplied for integration.  To get
+started quickly a <a href="refconfig.html" title="neon-config"><span class="refentrytitle">neon-config</span></a> script is included,
+to easily determine how to compile and link against an installed copy
+of neon</p></li><li><p>Complete test suite: the neon test suite
+comprises half as many lines of source code as the library itself,
+including many tests for protocol compliance in network behaviour, and
+that the library implementation meets the guarantees made by the
+API.</p></li></ul></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="intro.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="intro.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="using.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 1. Introduction </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> How to use neon from your application</td></tr></table></div></body></html>
diff --git a/neon/doc/html/gfdl.html b/neon/doc/html/gfdl.html
new file mode 100644 (file)
index 0000000..d6b91a9
--- /dev/null
@@ -0,0 +1,4 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Appendix A. GNU Free Documentation License</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="index.html" title="neon HTTP/WebDAV client library"><link rel="previous" href="biblio.html" title="Bibliography"><link rel="next" href="apas01.html" title="PREAMBLE"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Appendix A. GNU Free Documentation License</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="biblio.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="apas01.html">Next</a></td></tr></table><hr></div><div class="appendix" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="gfdl"></a>Appendix A. GNU Free Documentation License</h2></div></div><div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="apas01.html">0. PREAMBLE</a></span></dt><dt><span class="sect1"><a href="apas02.html">1. APPLICABILITY AND DEFINITIONS</a></span></dt><dt><span class="sect1"><a href="apas03.html">2. VERBATIM COPYING</a></span></dt><dt><span class="sect1"><a href="apas04.html">3. COPYING IN QUANTITY</a></span></dt><dt><span class="sect1"><a href="apas05.html">4. MODIFICATIONS</a></span></dt><dt><span class="sect1"><a href="apas06.html">5. COMBINING DOCUMENTS</a></span></dt><dt><span class="sect1"><a href="apas07.html">6. COLLECTIONS OF DOCUMENTS</a></span></dt><dt><span class="sect1"><a href="apas08.html">7. AGGREGATION WITH INDEPENDENT WORKS</a></span></dt><dt><span class="sect1"><a href="apas09.html">8. TRANSLATION</a></span></dt><dt><span class="sect1"><a href="apas10.html">9. TERMINATION</a></span></dt><dt><span class="sect1"><a href="apas11.html">10. FUTURE REVISIONS OF THIS LICENSE</a></span></dt><dt><span class="sect1"><a href="apas12.html">How to use this License for your documents</a></span></dt></dl></div><p>Version 1.1, March 2000</p><div class="blockquote"><blockquote class="blockquote"><p>Copyright (C) 2000  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.</p></blockquote></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="biblio.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="index.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="apas01.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Bibliography </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> PREAMBLE</td></tr></table></div></body></html>
diff --git a/neon/doc/html/index.html b/neon/doc/html/index.html
new file mode 100644 (file)
index 0000000..98b045e
--- /dev/null
@@ -0,0 +1,8 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>neon HTTP/WebDAV client library</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="next" href="intro.html" title="Chapter 1. Introduction"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">neon HTTP/WebDAV client library</th></tr><tr><td width="20%" align="left"> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="intro.html">Next</a></td></tr></table><hr></div><div class="book" lang="en"><div class="titlepage"><div><div><h1 class="title"><a name="id2956954"></a>neon HTTP/WebDAV client library</h1></div><div><div class="author"><h3 class="author"><span class="firstname">Joe</span> <span class="surname">Orton</span></h3><div class="affiliation"><div class="address"><p><tt class="email">&lt;<a href="mailto:neon@webdav.org">neon@webdav.org</a>&gt;</tt></p></div></div></div></div><div><p class="copyright">Copyright Â© 2001-2003 Joe Orton</p></div><div><div class="legalnotice"><p>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".</p></div></div></div><div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="intro.html">1. Introduction</a></span></dt><dd><dl><dt><span class="sect1"><a href="features.html">Feature list</a></span></dt><dt><span class="sect1"><a href="using.html">How to use neon from your application</a></span></dt><dt><span class="sect1"><a href="compliance.html">Protocol compliance</a></span></dt><dd><dl><dt><span class="sect2"><a href="compliance.html#id2957718">RFC 2518, HTTP Extensions for Distributed Authoring&#8212;WebDAV</a></span></dt><dt><span class="sect2"><a href="compliance.html#id3018315">RFC 2616, Hypertext Transfer Protocol&#8212;HTTP/1.1</a></span></dt><dt><span class="sect2"><a href="compliance.html#id3018354">RFC 2617, HTTP Authentication: Basic and Digest Access Authentication</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="api.html">2. The neon C language interface</a></span></dt><dd><dl><dt><span class="sect1"><a href="xml.html">Parsing XML</a></span></dt><dd><dl><dt><span class="sect2"><a href="xml.html#xml-sax">Introduction to SAX</a></span></dt><dt><span class="sect2"><a href="xml.html#xml-stacked">Stacked SAX handlers</a></span></dt><dt><span class="sect2"><a href="xml.html#xml-state">Maintaining state</a></span></dt><dt><span class="sect2"><a href="xml.html#xml-ns">XML namespaces</a></span></dt></dl></dd></dl></dd><dt><span class="reference"><a href="ref.html">I. neon API reference</a></span></dt><dd><dl><dt><a href="refneon.html">neon</a> - HTTP and WebDAV client library</dt><dt><a href="refconfig.html">neon-config</a> - script providing information about installed copy
+       of neon library</dt><dt><a href="refreqhdr.html">ne_add_request_header</a> - add headers to a request</dt><dt><a href="refresolve.html">ne_addr_resolve</a> - functions to resolve hostnames to addresses</dt><dt><a href="refbuf.html">ne_buffer</a> - string buffer handling</dt><dt><a href="refbufapp.html">ne_buffer_append</a> - append data to a string buffer</dt><dt><a href="refbufutil.html">ne_buffer_clear</a> - general purpose of group of functions</dt><dt><a href="refbufcr.html">ne_buffer_create</a> - general purpose of group of functions</dt><dt><a href="refbufdest.html">ne_buffer_destroy</a> - destroy a buffer object</dt><dt><a href="referr.html">ne_get_error</a> - error handling for HTTP sessions</dt><dt><a href="refgetst.html">ne_get_status</a> - retrieve HTTP response status for request</dt><dt><a href="refiaddr.html">ne_iaddr_make</a> - functions to manipulate and compare network addresses</dt><dt><a href="refalloc.html">ne_malloc</a> - memory allocation wrappers</dt><dt><a href="refreq.html">ne_request_create</a> - low-level HTTP request handling</dt><dt><a href="refsess.html">ne_session_create</a> - set up HTTP sessions</dt><dt><a href="refopts.html">ne_set_useragent</a> - common settings for HTTP sessions</dt><dt><a href="refreqbody.html">ne_set_request_body_buffer</a> - include a message body with a request</dt><dt><a href="refauth.html">ne_set_server_auth</a> - register authentication callbacks</dt><dt><a href="refshave.html">ne_shave</a> - trim whitespace from a string</dt><dt><a href="refsockinit.html">ne_sock_init</a> - perform library initialization</dt><dt><a href="refcert.html">ne_ssl_cert_identity</a> - functions to access certificate properties</dt><dt><a href="refsslcert2.html">ne_ssl_cert_cmp</a> - functions to operate on certificate objects</dt><dt><a href="refsslcertio.html">ne_ssl_cert_read</a> - functions to read or write certificates to and from files or strings</dt><dt><a href="refssldname.html">ne_ssl_dname</a> - SSL distinguished name handling</dt><dt><a href="refsslca.html">ne_ssl_trust_cert</a> - functions to indicate that certificates are trusted</dt><dt><a href="refsslvfy.html">ne_ssl_set_verify</a> - register an SSL certificate verification callback</dt><dt><a href="refclicert.html">ne_ssl_client_cert</a> - SSL client certificate handling</dt><dt><a href="refstatus.html">ne_status</a> - HTTP status structure</dt><dt><a href="reftok.html">ne_token</a> - string tokenizers</dt><dt><a href="refvers.html">ne_version_match</a> - library versioning</dt></dl></dd><dt><span class="bibliography"><a href="biblio.html">Bibliography</a></span></dt><dt><span class="appendix"><a href="gfdl.html">A. GNU Free Documentation License</a></span></dt><dd><dl><dt><span class="sect1"><a href="apas01.html">0. PREAMBLE</a></span></dt><dt><span class="sect1"><a href="apas02.html">1. APPLICABILITY AND DEFINITIONS</a></span></dt><dt><span class="sect1"><a href="apas03.html">2. VERBATIM COPYING</a></span></dt><dt><span class="sect1"><a href="apas04.html">3. COPYING IN QUANTITY</a></span></dt><dt><span class="sect1"><a href="apas05.html">4. MODIFICATIONS</a></span></dt><dt><span class="sect1"><a href="apas06.html">5. COMBINING DOCUMENTS</a></span></dt><dt><span class="sect1"><a href="apas07.html">6. COLLECTIONS OF DOCUMENTS</a></span></dt><dt><span class="sect1"><a href="apas08.html">7. AGGREGATION WITH INDEPENDENT WORKS</a></span></dt><dt><span class="sect1"><a href="apas09.html">8. TRANSLATION</a></span></dt><dt><span class="sect1"><a href="apas10.html">9. TERMINATION</a></span></dt><dt><span class="sect1"><a href="apas11.html">10. FUTURE REVISIONS OF THIS LICENSE</a></span></dt><dt><span class="sect1"><a href="apas12.html">How to use this License for your documents</a></span></dt></dl></dd></dl></div><div class="list-of-examples"><p><b>List of Examples</b></p><dl><dt>2.1. <a href="xml.html#xml-example">An example XML document</a></dt></dl></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="intro.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top"> </td><td width="20%" align="center"> </td><td width="40%" align="right" valign="top"> Chapter 1. Introduction</td></tr></table></div></body></html>
diff --git a/neon/doc/html/intro.html b/neon/doc/html/intro.html
new file mode 100644 (file)
index 0000000..82bdf2f
--- /dev/null
@@ -0,0 +1,7 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Chapter 1. Introduction</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="index.html" title="neon HTTP/WebDAV client library"><link rel="previous" href="index.html" title="neon HTTP/WebDAV client library"><link rel="next" href="features.html" title="Feature list"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 1. Introduction</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="features.html">Next</a></td></tr></table><hr></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div><div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="features.html">Feature list</a></span></dt><dt><span class="sect1"><a href="using.html">How to use neon from your application</a></span></dt><dt><span class="sect1"><a href="compliance.html">Protocol compliance</a></span></dt><dd><dl><dt><span class="sect2"><a href="compliance.html#id2957718">RFC 2518, HTTP Extensions for Distributed Authoring&#8212;WebDAV</a></span></dt><dt><span class="sect2"><a href="compliance.html#id3018315">RFC 2616, Hypertext Transfer Protocol&#8212;HTTP/1.1</a></span></dt><dt><span class="sect2"><a href="compliance.html#id3018354">RFC 2617, HTTP Authentication: Basic and Digest Access Authentication</a></span></dt></dl></dd></dl></div><p>This chapter provides an introduction to neon, giving an
+overview of the range of features offered, and some general guidelines
+for using the neon API.</p><p>neon aims to provide a modern, flexible, and simple API
+in the C programming language for implementing HTTP and WebDAV
+support.  The WebDAV functionality is entirely separate from the basic
+HTTP functionality; neon can be used simply as an HTTP client library,
+ignoring the WebDAV support if desired.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="index.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="features.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">neon HTTP/WebDAV client library </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Feature list</td></tr></table></div></body></html>
diff --git a/neon/doc/html/ref.html b/neon/doc/html/ref.html
new file mode 100644 (file)
index 0000000..804b56e
--- /dev/null
@@ -0,0 +1,2 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>neon API reference</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="index.html" title="neon HTTP/WebDAV client library"><link rel="previous" href="xml.html" title="Parsing XML"><link rel="next" href="refneon.html" title="neon"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">neon API reference</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="xml.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="refneon.html">Next</a></td></tr></table><hr></div><div class="reference" lang="en"><a name="ref"></a><div class="titlepage"><div><div><h1 class="title"><a name="ref"></a>neon API reference</h1></div></div><div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><a href="refneon.html">neon</a> - HTTP and WebDAV client library</dt><dt><a href="refconfig.html">neon-config</a> - script providing information about installed copy
+       of neon library</dt><dt><a href="refreqhdr.html">ne_add_request_header</a> - add headers to a request</dt><dt><a href="refresolve.html">ne_addr_resolve</a> - functions to resolve hostnames to addresses</dt><dt><a href="refbuf.html">ne_buffer</a> - string buffer handling</dt><dt><a href="refbufapp.html">ne_buffer_append</a> - append data to a string buffer</dt><dt><a href="refbufutil.html">ne_buffer_clear</a> - general purpose of group of functions</dt><dt><a href="refbufcr.html">ne_buffer_create</a> - general purpose of group of functions</dt><dt><a href="refbufdest.html">ne_buffer_destroy</a> - destroy a buffer object</dt><dt><a href="referr.html">ne_get_error</a> - error handling for HTTP sessions</dt><dt><a href="refgetst.html">ne_get_status</a> - retrieve HTTP response status for request</dt><dt><a href="refiaddr.html">ne_iaddr_make</a> - functions to manipulate and compare network addresses</dt><dt><a href="refalloc.html">ne_malloc</a> - memory allocation wrappers</dt><dt><a href="refreq.html">ne_request_create</a> - low-level HTTP request handling</dt><dt><a href="refsess.html">ne_session_create</a> - set up HTTP sessions</dt><dt><a href="refopts.html">ne_set_useragent</a> - common settings for HTTP sessions</dt><dt><a href="refreqbody.html">ne_set_request_body_buffer</a> - include a message body with a request</dt><dt><a href="refauth.html">ne_set_server_auth</a> - register authentication callbacks</dt><dt><a href="refshave.html">ne_shave</a> - trim whitespace from a string</dt><dt><a href="refsockinit.html">ne_sock_init</a> - perform library initialization</dt><dt><a href="refcert.html">ne_ssl_cert_identity</a> - functions to access certificate properties</dt><dt><a href="refsslcert2.html">ne_ssl_cert_cmp</a> - functions to operate on certificate objects</dt><dt><a href="refsslcertio.html">ne_ssl_cert_read</a> - functions to read or write certificates to and from files or strings</dt><dt><a href="refssldname.html">ne_ssl_dname</a> - SSL distinguished name handling</dt><dt><a href="refsslca.html">ne_ssl_trust_cert</a> - functions to indicate that certificates are trusted</dt><dt><a href="refsslvfy.html">ne_ssl_set_verify</a> - register an SSL certificate verification callback</dt><dt><a href="refclicert.html">ne_ssl_client_cert</a> - SSL client certificate handling</dt><dt><a href="refstatus.html">ne_status</a> - HTTP status structure</dt><dt><a href="reftok.html">ne_token</a> - string tokenizers</dt><dt><a href="refvers.html">ne_version_match</a> - library versioning</dt></dl></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="xml.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="index.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refneon.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Parsing XML </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> neon</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refalloc.html b/neon/doc/html/refalloc.html
new file mode 100644 (file)
index 0000000..abb3eee
--- /dev/null
@@ -0,0 +1,12 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_malloc</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refiaddr.html" title="ne_iaddr_make"><link rel="next" href="refreq.html" title="ne_request_create"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_malloc</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refiaddr.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refreq.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refalloc"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_malloc, ne_calloc, ne_realloc, ne_strdup, ne_strndup, ne_oom_callback &#8212; memory allocation wrappers</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_alloc.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void *<b class="fsfunc">ne_malloc</b>(</code>size_t <var xmlns="" class="pdparam">size</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void *<b class="fsfunc">ne_calloc</b>(</code>size_t <var xmlns="" class="pdparam">size</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void *<b class="fsfunc">ne_realloc</b>(</code>void *<var xmlns="" class="pdparam">size</var>, size_t <var xmlns="" class="pdparam">len</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_strdup</b>(</code>const char *<var xmlns="" class="pdparam">s</var>, size_t <var xmlns="" class="pdparam">size</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_strndup</b>(</code>const char *<var xmlns="" class="pdparam">s</var>, size_t <var xmlns="" class="pdparam">size</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_oom_callback</b>(</code>void (*<var xmlns="" class="pdparam">callback</var>)(void)<code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3026855"></a><h2>Description</h2><p>The functions <tt class="function">ne_malloc</tt>,
+<tt class="function">ne_calloc</tt>, <tt class="function">ne_realloc</tt>,
+<tt class="function">ne_strdup</tt> and <tt class="function">ne_strdnup</tt>
+provide wrappers for the equivalent functions in the standard C
+library.  The wrappers provide the extra guarantee that if the C
+library equivalent returns <tt class="literal">NULL</tt> when no memory is available, an
+optional callback will be called, and the library will then call
+<tt class="function">abort</tt>().</p><p><tt class="function">ne_oom_callback</tt> registers a callback
+which will be invoked if an out of memory error is detected.</p></div><div class="refsect1" lang="en"><a name="id3026920"></a><h2>Notes</h2><p>If the operating system uses optimistic memory
+allocation, the C library memory allocation routines will not return
+<tt class="literal">NULL</tt>, so it is not possible to gracefully handle memory allocation
+failures.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refiaddr.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refreq.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_iaddr_make </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_request_create</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refauth.html b/neon/doc/html/refauth.html
new file mode 100644 (file)
index 0000000..11162b2
--- /dev/null
@@ -0,0 +1,38 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_set_server_auth</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refreqbody.html" title="ne_set_request_body_buffer"><link rel="next" href="refshave.html" title="ne_shave"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_set_server_auth</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refreqbody.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refshave.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refauth"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_set_server_auth, ne_set_proxy_auth, ne_forget_auth &#8212; register authentication callbacks</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_auth.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">typedef int (*<b class="fsfunc">ne_request_auth</b>)(</code>void *<var xmlns="" class="pdparam">userdata</var>, const char *<var xmlns="" class="pdparam">realm</var>, int <var xmlns="" class="pdparam">attempt</var>, char *<var xmlns="" class="pdparam">username</var>, char *<var xmlns="" class="pdparam">password</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_server_auth</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, ne_request_auth <var xmlns="" class="pdparam">callback</var>, void *<var xmlns="" class="pdparam">userdata</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_proxy_auth</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, ne_request_auth <var xmlns="" class="pdparam">callback</var>, void *<var xmlns="" class="pdparam">userdata</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_forget_auth</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3030604"></a><h2>Description</h2><p>The <i class="type">ne_request_auth</i> function type defines a
+callback which is invoked when a server or proxy server requires user
+authentication for a particular request.  The
+<tt class="parameter">realm</tt> string is supplied by the server.  The <tt class="parameter">attempt</tt> 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, <tt class="parameter">attempt</tt> will be zero.</p><p>To retry the request using new authentication
+credentials, the callback should return zero, and the
+<tt class="parameter">username</tt> and <tt class="parameter">password</tt>
+buffers must contain <tt class="literal">NUL</tt>-terminated strings.  The
+<tt class="literal">NE_ABUFSIZ</tt> constant gives the size of these
+buffers.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Tip</h3><p>If you only wish to allow the user one attempt to enter
+credentials, use the value of the <tt class="parameter">attempt</tt>
+parameter as the return value of the callback.</p></div><p>To abort the request, the callback should return a
+non-zero value; in which case the contents of the
+<tt class="parameter">username</tt> and <tt class="parameter">password</tt>
+buffers are ignored.</p><p>The <tt class="function">ne_forget_auth</tt> function can be
+used to discard the cached authentication credentials.</p></div><div class="refsect1" lang="en"><a name="id3030718"></a><h2>Examples</h2><pre class="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(...)
+{
+   ne_session *sess = ne_session_create(...);
+
+   ne_set_server_auth(sess, my_auth, NULL);
+
+   /* ... */
+}</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refreqbody.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refshave.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_set_request_body_buffer </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_shave</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refbuf.html b/neon/doc/html/refbuf.html
new file mode 100644 (file)
index 0000000..c8897da
--- /dev/null
@@ -0,0 +1,20 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_buffer</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refresolve.html" title="ne_addr_resolve"><link rel="next" href="refbufapp.html" title="ne_buffer_append"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_buffer</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refresolve.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refbufapp.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refbuf"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_buffer &#8212; string buffer handling</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_string.h&gt;
+
+typedef struct {
+    char *data;
+    size_t used;
+    size_t length;
+} <i class="type">ne_buffer</i>;</pre></div></div><div class="refsect1" lang="en"><a name="id3023049"></a><h2>Description</h2><p>The <i class="type">ne_buffer</i> type represents an expandable
+memory buffer for holding <tt class="literal">NUL</tt>-terminated strings.  The
+<i class="structfield"><tt>data</tt></i> field points to the beginnning of the
+string, the length of which is given by the
+<i class="structfield"><tt>used</tt></i> field.  The current size of memory
+allocated is given by the <i class="structfield"><tt>length</tt></i> field.  It
+is not recommended that the fields of a buffer are manipulated
+directly.  The <i class="structfield"><tt>data</tt></i> pointer may change when
+the buffer is modified.</p><p>A buffer is created using <a href="refbufcr.html#ne_buffer_create">ne_buffer_create</a> or <a href="refbufcr.html#ne_buffer_create_sized">ne_buffer_ncreate</a>, and destroyed using <a href="refbufdest.html#ne_buffer_destroy">ne_buffer_destroy</a> or <a href="refbufdest.html#ne_buffer_finish">ne_buffer_finish</a>.
+The functions <a href="refbufapp.html#ne_buffer_append">ne_buffer_append</a>, <a href="refbufapp.html#ne_buffer_zappend">ne_buffer_zappend</a> and <a href="refbufapp.html#ne_buffer_concat">ne_buffer_concat</a> are
+used to append data to a buffer.</p><p>If the string referenced by the
+<i class="structfield"><tt>data</tt></i> pointer is modified directly (rather
+than using one of the functions listed above),
+<tt class="function">ne_buffer_altered</tt> must be called.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refresolve.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refbufapp.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_addr_resolve </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_buffer_append</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refbufapp.html b/neon/doc/html/refbufapp.html
new file mode 100644 (file)
index 0000000..6a0a105
--- /dev/null
@@ -0,0 +1,22 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_buffer_append</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refbuf.html" title="ne_buffer"><link rel="next" href="refbufutil.html" title="ne_buffer_clear"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_buffer_append</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refbuf.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refbufutil.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refbufapp"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_buffer_append, ne_buffer_zappend, ne_buffer_concat &#8212; append data to a string buffer</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_string.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_buffer_append</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var>, const char *<var xmlns="" class="pdparam">string</var>, size_t <var xmlns="" class="pdparam">len</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_buffer_zappend</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var>, const char *<var xmlns="" class="pdparam">string</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_buffer_concat</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var>, const char *<var xmlns="" class="pdparam">str</var>, ...<code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3023469"></a><h2>Description</h2><p>The <tt class="function">ne_buffer_append</tt> and
+<tt class="function">ne_buffer_zappend</tt> functions append a string to
+the end of a buffer; extending the buffer as necessary.  The
+<tt class="parameter">len</tt> passed to
+<tt class="function">ne_buffer_append</tt> specifies the length of the
+string to append; there must be no <tt class="literal">NUL</tt> terminator in the first
+<tt class="parameter">len</tt> bytes of the string.
+<tt class="function">ne_buffer_zappend</tt> must be passed a
+<tt class="literal">NUL</tt>-terminated string.</p><p>The <tt class="function">ne_buffer_concat</tt> function takes
+a variable-length argument list following <tt class="parameter">str</tt>;
+each argument must be a <i class="type">char *</i> pointer to a
+<tt class="literal">NUL</tt>-terminated string.  A <tt class="literal">NULL</tt> pointer must be given as the last
+argument to mark the end of the list.  The strings (including
+<tt class="parameter">str</tt>) are appended to the buffer in the order
+given. None of the strings passed to
+<tt class="function">ne_buffer_concat</tt> are modified.</p></div><div class="refsect1" lang="en"><a name="id3023579"></a><h2>Examples</h2><p>The following code will output "<tt class="literal">Hello, world.
+And goodbye.</tt>".</p><pre class="programlisting">ne_buffer *buf = ne_buffer_create();
+ne_buffer_zappend(buf, "Hello");
+ne_buffer_concat(buf, ", world. ", "And ", "goodbye.", NULL);
+puts(buf-&gt;data);
+ne_buffer_destroy(buf);</pre></div><div class="refsect1" lang="en"><a name="id3023604"></a><h2>See also</h2><p><a href="refbuf.html#ne_buffer">ne_buffer</a>, <a href="refbufcr.html#ne_buffer_create">ne_buffer_create</a>,
+<a href="refbufdest.html#ne_buffer_destroy">ne_buffer_destroy</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refbuf.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refbufutil.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_buffer </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_buffer_clear</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refbufcr.html b/neon/doc/html/refbufcr.html
new file mode 100644 (file)
index 0000000..aa79875
--- /dev/null
@@ -0,0 +1,7 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_buffer_create</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refbufutil.html" title="ne_buffer_clear"><link rel="next" href="refbufdest.html" title="ne_buffer_destroy"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_buffer_create</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refbufutil.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refbufdest.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refbufcr"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_buffer_create, ne_buffer_ncreate &#8212; general purpose of group of functions</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_alloc.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_buffer *<b class="fsfunc">ne_buffer_create</b>(</code><code xmlns="">void)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_buffer *<b class="fsfunc">ne_buffer_ncreate</b>(</code>size_t <var xmlns="" class="pdparam">size</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3024503"></a><h2>Description</h2><p><tt class="function">ne_buffer_create</tt> creates a new
+buffer object, with an implementation-defined initial size.
+<tt class="function">ne_buffer_ncreate</tt> creates an
+<i class="type">ne_buffer</i> where the minimum initial size is given in the
+<tt class="parameter">size</tt> parameter.  The buffer created will
+contain the empty string (<tt class="literal">""</tt>).</p></div><div class="refsect1" lang="en"><a name="id3024542"></a><h2>Return value</h2><p>Both functions return a pointer to a new buffer object,
+and never <tt class="literal">NULL</tt>.</p></div><div class="refsect1" lang="en"><a name="id3024559"></a><h2>See also</h2><p><a href="refbuf.html#ne_buffer">ne_buffer</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refbufutil.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refbufdest.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_buffer_clear </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_buffer_destroy</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refbufdest.html b/neon/doc/html/refbufdest.html
new file mode 100644 (file)
index 0000000..e053a9a
--- /dev/null
@@ -0,0 +1,17 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_buffer_destroy</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refbufcr.html" title="ne_buffer_create"><link rel="next" href="referr.html" title="ne_get_error"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_buffer_destroy</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refbufcr.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="referr.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refbufdest"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_buffer_destroy, ne_buffer_finish &#8212; destroy a buffer object</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_string.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_buffer_destroy</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_buffer_finish</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3024841"></a><h2>Description</h2><p><tt class="function">ne_buffer_destroy</tt> frees all memory
+associated with the buffer.  <tt class="function">ne_buffer_finish</tt>
+frees the buffer structure, but not the actual string stored in the
+buffer, which is returned and must be <tt class="function">free</tt>()d by
+the caller.</p><p>Any use of the buffer object after calling either of these
+functions gives undefined behaviour.</p></div><div class="refsect1" lang="en"><a name="id3024874"></a><h2>Return value</h2><p><tt class="function">ne_buffer_finish</tt> returns the
+<tt class="function">malloc</tt>-allocated string stored in the buffer.</p></div><div class="refsect1" lang="en"><a name="id3024896"></a><h2>Examples</h2><p>An example use of <tt class="function">ne_buffer_finish</tt>;
+the <tt class="function">duplicate</tt> function returns a string made up of 
+<tt class="parameter">n</tt> copies of <tt class="parameter">str</tt>:</p><pre class="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);
+}</pre></div><div class="refsect1" lang="en"><a name="id3024939"></a><h2>See also</h2><p><a href="refbuf.html#ne_buffer">ne_buffer</a>, <a href="refbufcr.html#ne_buffer_create">ne_buffer_create</a>,
+<a href="refbufapp.html#ne_buffer_zappend">ne_buffer_zappend</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refbufcr.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="referr.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_buffer_create </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_get_error</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refbufutil.html b/neon/doc/html/refbufutil.html
new file mode 100644 (file)
index 0000000..c37d040
--- /dev/null
@@ -0,0 +1,11 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_buffer_clear</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refbufapp.html" title="ne_buffer_append"><link rel="next" href="refbufcr.html" title="ne_buffer_create"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_buffer_clear</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refbufapp.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refbufcr.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refbufutil"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_buffer_clear, ne_buffer_grow, ne_buffer_altered &#8212; general purpose of group of functions</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_string.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_buffer_clear</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_buffer_altered</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_buffer_grow</b>(</code>ne_buffer *<var xmlns="" class="pdparam">buf</var>, size_t <var xmlns="" class="pdparam">size</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3024105"></a><h2>Description</h2><p>The <tt class="function">ne_buffer_clear</tt> function sets
+the string stored in <tt class="parameter">buf</tt> to be the empty string
+(<tt class="literal">""</tt>).</p><p>The <tt class="function">ne_buffer_altered</tt> function must
+be used after the string stored in the buffer
+<tt class="parameter">buf</tt> is modified by directly rather than using
+<a href="refbufapp.html#ne_buffer_append">ne_buffer_append</a>, <a href="refbufapp.html#ne_buffer_zappend">ne_buffer_zappend</a> 
+or <a href="refbufapp.html#ne_buffer_concat">ne_buffer_concat</a>.</p><p>The <tt class="function">ne_buffer_grow</tt> function
+ensures that at least <tt class="parameter">size</tt> 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.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refbufapp.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refbufcr.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_buffer_append </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_buffer_create</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refcert.html b/neon/doc/html/refcert.html
new file mode 100644 (file)
index 0000000..2a6562a
--- /dev/null
@@ -0,0 +1,33 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_ssl_cert_identity</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refsockinit.html" title="ne_sock_init"><link rel="next" href="refsslcert2.html" title="ne_ssl_cert_cmp"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_ssl_cert_identity</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refsockinit.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refsslcert2.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refcert"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_ssl_cert_identity, ne_ssl_cert_signedby, ne_ssl_cert_issuer, ne_ssl_cert_subject &#8212; functions to access certificate properties</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_ssl.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const char *<b class="fsfunc">ne_ssl_cert_identity</b>(</code>const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const ne_ssl_certificate *<b class="fsfunc">ne_ssl_cert_signedby</b>(</code>const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const ne_ssl_dname *<b class="fsfunc">ne_ssl_cert_subject</b>(</code>const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const ne_ssl_dname *<b class="fsfunc">ne_ssl_cert_issuer</b>(</code>const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3031787"></a><h2>Description</h2><p>The function <tt class="function">ne_ssl_cert_identity</tt>
+    retrieves the &#8220;<span class="quote">identity</span>&#8221; of a certificate; for an
+    SSL server certificate, this will be the hostname for which the
+    certificate was issued.  In PKI parlance, the identity is the
+    <span class="emphasis"><em>common name</em></span> attribute of the distinguished name of
+    the certificate subject.</p><p>The functions <tt class="function">ne_ssl_cert_subject</tt> and
+    <tt class="function">ne_ssl_cert_issuer</tt> can be used to access the
+    objects representing the distinguished name of the subject and of
+    the issuer of a certificate, respectively.</p><p>If a certificate object is part of a certificate chain, then
+    <tt class="function">ne_ssl_cert_signedby</tt> can be used to find the
+    certificate which signed a particular certificate. For a
+    self-signed certificate or a certificate for which the full chain
+    is not available, this function will return <tt class="literal">NULL</tt>.</p></div><div class="refsect1" lang="en"><a name="id3031853"></a><h2>Return value</h2><p><tt class="function">ne_ssl_cert_issuer</tt> and
+    <tt class="function">ne_ssl_cert_subject</tt> are guaranteed to never
+    return <tt class="literal">NULL</tt>. <tt class="function">ne_ssl_cert_identity</tt> may
+    return <tt class="literal">NULL</tt> if the certificate has no specific
+    &#8220;<span class="quote">identity</span>&#8221;. <tt class="function">ne_ssl_cert_signedby</tt>
+    may return <tt class="literal">NULL</tt> as covered above.</p></div><div class="refsect1" lang="en"><a name="id3031911"></a><h2>Examples</h2><p>The following function could be used to display information
+    about a given certificate:</p><pre class="programlisting">void dump_cert(const ne_ssl_certificate *cert) {
+  const char *id = ne_ssl_cert_identity(cert);
+  char *dn;
+
+  if (id) 
+    printf("Certificate was issued for '%s'.\n", id);
+
+  dn = ne_ssl_readable_dname(ne_ssl_cert_subject(cert));
+  printf("Subject: %s\n", dn);
+  free(dn);
+
+  dn = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
+  printf("Issuer: %s\n", dn);
+  free(dn);
+}</pre></div><div class="refsect1" lang="en"><a name="id3031935"></a><h2>See also</h2><p><a href="refsslcert2.html#ne_ssl_cert_cmp">ne_ssl_cert_cmp</a>, <a href="refssldname.html#ne_ssl_readable_dname">ne_ssl_readable_dname</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refsockinit.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refsslcert2.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_sock_init </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_ssl_cert_cmp</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refclicert.html b/neon/doc/html/refclicert.html
new file mode 100644 (file)
index 0000000..0b267f1
--- /dev/null
@@ -0,0 +1,55 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_ssl_client_cert</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refsslvfy.html" title="ne_ssl_set_verify"><link rel="next" href="refstatus.html" title="ne_status"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_ssl_client_cert</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refsslvfy.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refstatus.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refclicert"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_ssl_clicert_read, ne_ssl_clicert_name, ne_ssl_clicert_encrypted, ne_ssl_clicert_decrypt, ne_ssl_clicert_owner, ne_ssl_clicert_free &#8212; SSL client certificate handling</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_ssl.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_ssl_client_cert *<b class="fsfunc">ne_ssl_clicert_read</b>(</code>const char *<var xmlns="" class="pdparam">filename</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const char *<b class="fsfunc">ne_ssl_clicert_name</b>(</code>const ne_ssl_client_cert *<var xmlns="" class="pdparam">ccert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_ssl_clicert_encrypted</b>(</code>const ne_ssl_client_cert *<var xmlns="" class="pdparam">ccert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_ssl_clicert_decrypt</b>(</code>ne_ssl_client_cert *<var xmlns="" class="pdparam">ccert</var>, const char *<var xmlns="" class="pdparam">password</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const ne_ssl_certificate *<b class="fsfunc">ne_ssl_clicert_owner</b>(</code>const ne_ssl_client_cert *<var xmlns="" class="pdparam">ccert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_ssl_clicert_free</b>(</code>ne_ssl_client_cert *<var xmlns="" class="pdparam">ccert</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3035077"></a><h2>Description</h2><p>The <tt class="function">ne_ssl_clicert_read</tt> function reads
+    a <i class="firstterm">client certificate</i> from a
+    PKCS#12-formatted file, and returns an
+    <i class="type">ne_ssl_client_certificate</i> object.  If the client
+    certificate is encrypted, it must be decrypted before it is used.
+    An <i class="type">ne_ssl_client_certificate</i> object holds a client
+    certificate and the associated private key, not just a
+    certificate; the term "<i class="glossterm">client certificate</i>"
+    will used to refer to this pair.</p><p>A client certificate can be in one of two states:
+    <span class="emphasis"><em>encrypted</em></span> or <span class="emphasis"><em>decrypted</em></span>.
+    The <tt class="function">ne_ssl_clicert_encrypted</tt> function will
+    return non-zero if the client certificate is in the
+    <span class="emphasis"><em>encrypted</em></span> state.  A client certificate object
+    returned by <tt class="function">ne_ssl_clicert_read</tt> may be
+    initially in either state, depending on whether the file was
+    encrypted or not.</p><p><tt class="function">ne_ssl_clicert_decrypt</tt> can be used to
+    decrypt a client certificate using the appropriate password.  This
+    function must only be called if the object is in the
+    <span class="emphasis"><em>encrypted</em></span> state; if decryption fails, the
+    certificate state does not change, so decryption can be attempted
+    more than once using different passwords.</p><p>A client certificate can be given a "friendly name" when it
+    is created; <tt class="function">ne_ssl_clicert_name</tt> will return
+    this name (or <tt class="literal">NULL</tt> if no friendly name was specified).
+    <tt class="function">ne_ssl_clicert_name</tt> can be used when the
+    client certificate is in either the encrypted or decrypted state,
+    and will return the same string for the lifetime of the
+    object.</p><p>The function <tt class="function">ne_ssl_clicert_owner</tt>
+    returns the certificate part of the client certificate; it must
+    only be called if the client certificate is in the
+    <span class="emphasis"><em>decrypted</em></span> state.</p><p>When the client certificate is no longer needed, the
+    <tt class="function">ne_ssl_clicert_free</tt> function should be used
+    to destroy the object.</p></div><div class="refsect1" lang="en"><a name="id3035215"></a><h2>Return value</h2><p><tt class="function">ne_ssl_clicert_read</tt> returns a client
+    certificate object, or <tt class="literal">NULL</tt> if the file could not be read.
+    <tt class="function">ne_ssl_clicert_encrypted</tt> returns zero if the
+    object is in the decrypted state, or non-zero if it is in the
+    encrypted state. <tt class="function">ne_ssl_clicert_name</tt> returns
+    a <tt class="literal">NUL</tt>-terminated friendly name string, or <tt class="literal">NULL</tt>.
+    <tt class="function">ne_ssl_clicert_owner</tt> returns a certificate
+    object.</p></div><div class="refsect1" lang="en"><a name="id3035273"></a><h2>Examples</h2><p>The following code reads a client certificate and decrypts
+    it if necessary, then loads it into an HTTP session.</p><pre class="programlisting">ne_ssl_client_certificate *ccert;
+
+ccert = ne_ssl_clicert_read("/path/to/client.p12");
+
+if (ccert == NULL) {
+   /* handle error... */
+} else if (ne_ssl_clicert_encrypted(ccert)) {
+   char *password = prompt_for_password();
+
+   if (ne_ssl_clicert_decrypt(ccert, password)) {
+      /* could not decrypt! handle error... */
+   }
+}
+
+ne_ssl_set_clicert(sess, ccert);
+</pre></div><div class="refsect1" lang="en"><a name="id3035296"></a><h2>See also</h2><p><a href="refsslcertio.html#ne_ssl_cert_read">ne_ssl_cert_read</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refsslvfy.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refstatus.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_ssl_set_verify </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_status</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refconfig.html b/neon/doc/html/refconfig.html
new file mode 100644 (file)
index 0000000..d114018
--- /dev/null
@@ -0,0 +1,32 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>neon-config</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refneon.html" title="neon"><link rel="next" href="refreqhdr.html" title="ne_add_request_header"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">neon-config</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refneon.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refreqhdr.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refconfig"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>neon-config &#8212; script providing information about installed copy
+       of neon library</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="cmdsynopsis"><p><tt class="command">neon-config</tt>  [<tt class="option">--prefix</tt>] [[<tt class="option">--cflags</tt>] |  [<tt class="option">--libs</tt>] |  [<tt class="option">--la-file</tt>] |  [<tt class="option">--support</tt> <i class="replaceable"><tt>feature</tt></i>] |  [<tt class="option">--help</tt>] |  [<tt class="option">--version</tt>]]</p></div></div><div class="refsect1" lang="en"><a name="id3020952"></a><h2>Description</h2><p>The <span><b class="command">neon-config</b></span> script provides
+information about an installed copy of the neon library.  The
+<tt class="option">--cflags</tt> and <tt class="option">--libs</tt> options instruct
+how to compile and link an application against the library; the
+<tt class="option">--version</tt> and <tt class="option">--support</tt> options can
+help determine whether the library meets the applications
+requirements.</p></div><div class="refsect1" lang="en"><a name="id3020986"></a><h2>Options</h2><div class="variablelist"><table border="0"><col align="left" valign="top"><tbody><tr><td><span class="term"><tt class="option">--cflags</tt></span></td><td>Print the flags which should be passed to
+the C compiler when compiling object files, when the object files use
+neon header files.</td></tr><tr><td><span class="term"><tt class="option">--libs</tt></span></td><td>Print the flags which should be passed to
+the linker when linking an application which uses the neon
+library</td></tr><tr><td><span class="term"><tt class="option">--la-file</tt></span></td><td>Print the location of the libtool library
+script, <tt class="filename">libneon.la</tt>, which can be used to link against
+neon by applications using libtool.</td></tr><tr><td><span class="term"><tt class="option">--version</tt></span></td><td>Print the version of the library</td></tr><tr><td><span class="term"><tt class="option">--prefix</tt> <i class="replaceable"><tt>dir</tt></i></span></td><td>If <i class="replaceable"><tt>dir</tt></i> is given; relocate output of
+<tt class="option">--cflags</tt> and <tt class="option">--libs</tt> as if neon was
+installed in given prefix directory.  Otherwise, print the
+installation prefix of the library.</td></tr><tr><td><span class="term"><tt class="option">--support</tt> <i class="replaceable"><tt>feature</tt></i></span></td><td>The script exits with success if
+<i class="replaceable"><tt>feature</tt></i> is supported by the
+library.</td></tr><tr><td><span class="term"><tt class="option">--help</tt></span></td><td>Print help message; includes list of known
+           features and whether they are supported or not.</td></tr></tbody></table></div></div><div class="refsect1" lang="en"><a name="example"></a><h2>Example</h2><p>Below is a Makefile fragment which could be used to
+build an application against an installed neon library, when the
+<span><b class="command">neon-config</b></span> script can be found in
+<tt class="envar">$PATH</tt>.</p><pre class="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</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refneon.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refreqhdr.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">neon </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_add_request_header</td></tr></table></div></body></html>
diff --git a/neon/doc/html/referr.html b/neon/doc/html/referr.html
new file mode 100644 (file)
index 0000000..6835719
--- /dev/null
@@ -0,0 +1,11 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_get_error</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refbufdest.html" title="ne_buffer_destroy"><link rel="next" href="refgetst.html" title="ne_get_status"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_get_error</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refbufdest.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refgetst.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="referr"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_get_error, ne_set_error &#8212; error handling for HTTP sessions</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_session.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const char *<b class="fsfunc">ne_get_error</b>(</code>ne_sesssion *<var xmlns="" class="pdparam">session</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_error</b>(</code>ne_sesssion *<var xmlns="" class="pdparam">session</var>, const char *<var xmlns="" class="pdparam">format</var>, ...<code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3025309"></a><h2>Description</h2><p>The session error string is used to store any
+human-readable error information associated with any errors which
+occur whilst using the HTTP session.</p><p>The <tt class="function">ne_get_error</tt> function returns the current
+session error string.  This string persists only until it is changed by a
+subsequent operation on the session.</p><p>The <tt class="function">ne_set_error</tt> function can be
+used to set a new session error string, using a
+<tt class="function">printf</tt>-style format string interface.</p></div><div class="refsect1" lang="en"><a name="id3025350"></a><h2>Examples</h2><p>Retrieve the current error string:</p><pre class="programlisting">ne_session *sess = ne_session_create(...);
+...
+printf("Error was: %s\n", ne_get_error(sess));</pre><p>Set a new error string:</p><pre class="programlisting">ne_session *sess = ne_session_create(...);
+...
+ne_set_error(sess, "Response missing header %s", "somestring");</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refbufdest.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refgetst.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_buffer_destroy </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_get_status</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refgetst.html b/neon/doc/html/refgetst.html
new file mode 100644 (file)
index 0000000..51d68b3
--- /dev/null
@@ -0,0 +1,13 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_get_status</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="referr.html" title="ne_get_error"><link rel="next" href="refiaddr.html" title="ne_iaddr_make"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_get_status</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="referr.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refiaddr.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refgetst"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_get_status &#8212; retrieve HTTP response status for request</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_request.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const ne_status *<b class="fsfunc">ne_get_status</b>(</code>const ne_request *<var xmlns="" class="pdparam">request</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3023988"></a><h2>Description</h2><p>The <tt class="function">ne_get_status</tt> function returns
+a pointer to the HTTP status object giving the result of a request.
+The object returned only becomes valid once the request has been
+<span class="emphasis"><em>successfully</em></span> dispatched (the return value of
+<tt class="function">ne_request_dispatch</tt> or
+<tt class="function">ne_begin_request</tt> was zero).  The object remains
+valid until the associated request object is destroyed.</p></div><div class="refsect1" lang="en"><a name="id3024024"></a><h2>See also</h2><p><a href="refstatus.html#ne_status">ne_status</a>, <a href="refreq.html#ne_request_create">ne_request_create</a></p></div><div class="refsect1" lang="en"><a name="id3025702"></a><h2>Example</h2><p>Display the response status code of applying the
+<tt class="literal">HEAD</tt> method to some resource.</p><pre class="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)-&gt;code);
+ne_request_destroy(req);</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="referr.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refiaddr.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_get_error </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_iaddr_make</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refiaddr.html b/neon/doc/html/refiaddr.html
new file mode 100644 (file)
index 0000000..1cd9f64
--- /dev/null
@@ -0,0 +1,36 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_iaddr_make</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refgetst.html" title="ne_get_status"><link rel="next" href="refalloc.html" title="ne_malloc"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_iaddr_make</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refgetst.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refalloc.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refiaddr"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_iaddr_make, ne_iaddr_cmp, ne_iaddr_print, ne_iaddr_free &#8212; functions to manipulate and compare network addresses</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_socket.h&gt;
+
+typedef enum {
+    ne_iaddr_ipv4 = 0,
+    ne_iaddr_ipv6
+} <i class="type">ne_iaddr_type</i>;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_inet_addr *<b class="fsfunc">ne_iaddr_make</b>(</code>ne_iaddr_type <var xmlns="" class="pdparam">type</var>, const unsigned char *<var xmlns="" class="pdparam">raw</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_iaddr_cmp</b>(</code>const ne_inet_addr *<var xmlns="" class="pdparam">i1</var>, const ne_inet_addr *<var xmlns="" class="pdparam">i2</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_iaddr_print</b>(</code>const ne_inet_addr *<var xmlns="" class="pdparam">ia</var>, char *<var xmlns="" class="pdparam">buffer</var>, size_t <var xmlns="" class="pdparam">bufsiz</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_iaddr_free</b>(</code>const ne_inet_addr *<var xmlns="" class="pdparam">addr</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3026080"></a><h2>Description</h2><p><tt class="function">ne_iaddr_make</tt> creates an
+    <i class="type">ne_inet_addr</i> object from a raw binary network
+    address; for instance the four bytes <tt class="literal">0x7f 0x00 0x00
+    0x01</tt> represent the IPv4 address
+    <tt class="literal">127.0.0.1</tt>.  The object returned is suitable for
+    passing to <tt class="function">ne_sock_connect</tt>.  A binary IPv4
+    address contains four bytes; a binary IPv6 address contains
+    sixteen bytes; addresses passed must be in network byte
+    order.</p><p><tt class="function">ne_iaddr_cmp</tt> can be used to compare two
+    network addresses; returning zero only if they are identical.  The
+    addresses need not be of the same address type; if the addresses
+    are not of the same type, the return value is guaranteed to be
+    non-zero.</p><p><tt class="function">ne_iaddr_print</tt> can be used to print the
+    human-readable string representation of a network address into a
+    buffer, for instance the string
+    <tt class="literal">"127.0.0.1"</tt>.</p><p><tt class="function">ne_iaddr_free</tt> releases the memory
+    associated with a network address object.</p></div><div class="refsect1" lang="en"><a name="id3026159"></a><h2>Return value</h2><p><tt class="function">ne_iaddr_make</tt> returns <tt class="literal">NULL</tt> if the
+    address type passed is not supported (for instance on a platform
+    which does not support IPv6).</p><p><tt class="function">ne_iaddr_print</tt> returns the
+    <tt class="parameter">buffer</tt> pointer, and never <tt class="literal">NULL</tt>.</p></div><div class="refsect1" lang="en"><a name="id3026204"></a><h2>Examples</h2><p>The following example connects a socket to port 80 at the
+    address <tt class="literal">127.0.0.1</tt>.</p><pre class="programlisting">unsigned char addr[] = "\0x7f\0x00\0x00\0x01";
+ne_inet_addr *ia;
+
+ia = ne_iaddr_make(ne_iaddr_ipv4, addr);
+if (ia != NULL) {
+    ne_socket *sock = ne_sock_connect(ia, 80);
+    ne_iaddr_free(ia);
+    /* ... */
+} else {
+    /* ... */
+}</pre></div><div class="refsect1" lang="en"><a name="id3026230"></a><h2>See also</h2><p><a href="refresolve.html#ne_addr_resolve">ne_addr_resolve</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refgetst.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refalloc.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_get_status </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_malloc</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refneon.html b/neon/doc/html/refneon.html
new file mode 100644 (file)
index 0000000..1279a3a
--- /dev/null
@@ -0,0 +1,68 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>neon</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="ref.html" title="neon API reference"><link rel="next" href="refconfig.html" title="neon-config"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">neon</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ref.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refconfig.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refneon"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>neon &#8212; HTTP and WebDAV client library</p></div><div class="refsect1" lang="en"><a name="id3019464"></a><h2>Description</h2><p>neon is an HTTP and WebDAV client library.  The major
+    abstractions exposed are the HTTP <span class="emphasis"><em>session</em></span>,
+    created by <a href="refsess.html#ne_session_create">ne_session_create</a>; and the HTTP
+    <span class="emphasis"><em>request</em></span>, created by <a href="refreq.html#ne_request_create">ne_request_create</a>.  HTTP authentication is handled
+    transparently for server and proxy servers, see <a href="refauth.html#ne_set_server_auth">ne_set_server_auth</a>; complete SSL/TLS support is also
+    included, see <a href="refsslvfy.html#ne_ssl_set_verify">ne_ssl_set_verify</a>.</p></div><div class="refsect1" lang="en"><a name="id3019953"></a><h2>Conventions</h2><p>Some conventions are used throughout the neon API, to
+    provide a consistent and simple interface; these are documented
+    below.</p><div class="refsect2" lang="en"><a name="id3019964"></a><h3>Thread-safeness and global initialization</h3><p>neon itself is implemented to be thread-safe (avoiding any
+    use of global state), but relies on the operating system providing
+    a thread-safe resolver interface.  Modern operating systems offer
+    the thread-safe <tt class="function">getaddrinfo</tt> interface, which
+    neon supports; some others implement
+    <tt class="function">gethostbyname</tt> using thread-local
+    storage.</p><p>To allow thread-safe use of the OpenSSL library, the
+    application must register some locking callbacks in accordance
+    with the <a href="http://www.openssl.org/docs/crypto/threads.html" target="_top">OpenSSL
+    documentation</a>.</p><p>Some platforms and libraries used by neon require global
+    initialization before use; notably:
+
+    </p><div class="itemizedlist"><ul type="disc"><li>OpenSSL requires global initialization to
+      load shared lookup tables.</li><li>The SOCKS library requires initialization
+      before use.</li><li>The Win32 socket library requires
+      initialization before use.</li></ul></div><p>
+
+    The <a href="refsockinit.html#ne_sock_init">ne_sock_init</a> function should be called
+    before any other use of neon to perform any necessary
+    initialization needed for the particular platform.</p></div><div class="refsect2" lang="en"><a name="id3020179"></a><h3>Namespaces</h3><p>To avoid possible collisions between names used for symbols
+    and preprocessor macros by an application and the libraries it
+    uses, it is good practice for each library to reserve a particular
+    <span class="emphasis"><em>namespace prefix</em></span>.  An application which
+    ensures it uses no names with these prefixes is then guaranteed to
+    avoid such collisions.</p><p>The neon library reserves the use of the namespace
+    prefixes <tt class="literal">ne_</tt> and <tt class="literal">NE_</tt>.  The
+    libraries used by neon may also reserve certain namespaces;
+    collisions between these libraries and a neon-based application
+    will not be detected at compile time, since the underlying library
+    interfaces are not exposed through the neon header files.  Such
+    collisions can only be detected at link time, when the linker
+    attempts to resolve symbols.  The following list documents some of
+    the namespaces claimed by libraries used by neon; this list may
+    be incomplete.</p><div class="variablelist"><table border="0"><col align="left" valign="top"><tbody><tr><td><span class="term">SSL, ssl, TLS, tls, ERR_, BIO_, d2i_, i2d_, ASN1_</span></td><td>Some of the many prefixes used by the OpenSSL
+       library; little attempt has been made to keep exported symbols
+       within any particular prefixes for this
+       library.</td></tr><tr><td><span class="term">XML_, Xml[A-Z]</span></td><td>Namespaces
+       used by the expat library.</td></tr><tr><td><span class="term">xml[A-Z], html[A-Z], docb[A-Z]</span></td><td>Namespaces used by the libxml2 library; a
+       relatively small number of symbols are used without these
+       prefixes.</td></tr></tbody></table></div></div><div class="refsect2" lang="en"><a name="id3020264"></a><h3>Argument validation</h3><p>neon does not attempt to validate that the parameters
+    passed to functions conform to the API (for instance, checking
+    that pointer arguments are not <tt class="literal">NULL</tt>).  Any use of the neon API
+    which is not documented to produce a certain behaviour results is
+    said to produce <span class="emphasis"><em>undefined behaviour</em></span>; it is
+    likely that neon will segfault under these conditions.</p></div><div class="refsect2" lang="en"><a name="id3020280"></a><h3>URI paths, WebDAV metadata</h3><p>The path strings passed to any function must be
+    <span class="emphasis"><em>URI-encoded</em></span> by the application; neon never
+    performs any URI encoding or decoding internally.  WebDAV property
+    names and values must be valid UTF-8 encoded Unicode
+    strings.</p></div><div class="refsect2" lang="en"><a name="id3020308"></a><h3>User interaction</h3><p>As a pure library interface, neon will never produce
+    output on <tt class="constant">stdout</tt> or
+    <tt class="constant">stderr</tt>; all user interaction is the
+    responsibilty of the application.</p></div><div class="refsect2" lang="en"><a name="id3020332"></a><h3>Memory handling</h3><p>neon does not attempt to cope gracefully with an
+    out-of-memory situation; instead, by default, the
+    <tt class="function">abort</tt> function is called to immediately
+    terminate the process.  An application may register a custom
+    function which will be called before <tt class="function">abort</tt> in
+    such a situation; see <a href="refalloc.html#ne_oom_callback">ne_oom_callback</a>.</p></div><div class="refsect2" lang="en"><a name="id3020364"></a><h3>Callbacks and userdata</h3><p>Whenever a callback is registered, a
+    <tt class="literal">userdata</tt> pointer is also used to allow the
+    application to associate a context with the callback.  The
+    userdata is of type <i class="type">void *</i>, allowing any pointer to
+    be used.</p></div></div><div class="refsect1" lang="en"><a name="id3020386"></a><h2>See also</h2><p><a href="refsess.html" title="ne_session_create"><span class="refentrytitle">ne_session_create</span></a>, <a href="refalloc.html#ne_oom_callback">ne_oom_callback</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ref.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refconfig.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">neon API reference </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> neon-config</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refopts.html b/neon/doc/html/refopts.html
new file mode 100644 (file)
index 0000000..6ed39d2
--- /dev/null
@@ -0,0 +1,35 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_set_useragent</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refsess.html" title="ne_session_create"><link rel="next" href="refreqbody.html" title="ne_set_request_body_buffer"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_set_useragent</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refsess.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refreqbody.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refopts"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_set_useragent, ne_set_persist, ne_set_read_timeout, ne_set_expect100 &#8212; common settings for HTTP sessions</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_session.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_useragent</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, const char *<var xmlns="" class="pdparam">product</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_persist</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, int <var xmlns="" class="pdparam">flag</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_read_timeout</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, int <var xmlns="" class="pdparam">timeout</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_expect100</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, int <var xmlns="" class="pdparam">flag</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3029476"></a><h2>Description</h2><p>The <tt class="literal">User-Agent</tt> request header is used
+to identify the software which generated the request for statistical
+or debugging purposes.  neon does not send a
+<tt class="literal">User-Agent</tt> header unless a call is made to the
+<tt class="function">ne_set_useragent</tt>.
+<tt class="function">ne_set_useragent</tt> must be passed a product string
+conforming to RFC2616's product token grammar; of the form
+<tt class="literal">"Product/Version"</tt>.</p><p>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 <tt class="function">ne_set_persist</tt> function will disable
+persistent connections if passed a <tt class="parameter">flag</tt>
+parameter of <tt class="literal">0</tt>, and will enable them
+otherwise.</p><p>When neon reads from a socket, by default the read
+operation will time out after 60 seconds, and the request will fail
+giving an <span class="errorcode">NE_TIMEOUT</span> error.  To configure this
+timeout interval, call <tt class="function">ne_set_read_timeout</tt> giving
+the desired number of seconds as the <tt class="parameter">timeout</tt>
+parameter.</p><p>An extension introduced in the HTTP/1.1 specification
+was the use of the <tt class="literal">Expect: 100-continue</tt> 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 <tt class="function">ne_set_expect100</tt> function can be used to
+enable this feature by passing the <tt class="parameter">flag</tt>
+parameter as any non-zero integer.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Warning</h3><p>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
+<tt class="literal">Expect: 100-continue</tt> support.</p></div></div><div class="refsect1" lang="en"><a name="id3029626"></a><h2>Examples</h2><p>Set a user-agent string:</p><pre class="programlisting">ne_session *sess = ne_session_create(...);
+ne_set_useragent(sess, "MyApplication/2.1");</pre><p>Disable use of persistent connections:</p><pre class="programlisting">ne_session *sess = ne_session_create(...);
+ne_set_persist(sess, 0);</pre><p>Set a 30 second read timeout:</p><pre class="programlisting">ne_session *sess = ne_session_create(...);
+ne_set_read_timeout(sess, 30);</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refsess.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refreqbody.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_session_create </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_set_request_body_buffer</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refreq.html b/neon/doc/html/refreq.html
new file mode 100644 (file)
index 0000000..b578f20
--- /dev/null
@@ -0,0 +1,42 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_request_create</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refalloc.html" title="ne_malloc"><link rel="next" href="refsess.html" title="ne_session_create"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_request_create</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refalloc.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refsess.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refreq"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_request_create, ne_request_dispatch, ne_request_destroy &#8212; low-level HTTP request handling</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_request.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_request *<b class="fsfunc">ne_request_create</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, const char *<var xmlns="" class="pdparam">method</var>, const char *<var xmlns="" class="pdparam">path</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_request_dispatch</b>(</code>ne_request *<var xmlns="" class="pdparam">req</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_request_destroy</b>(</code>ne_request *<var xmlns="" class="pdparam">req</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3027419"></a><h2>Description</h2><p>An HTTP request, represented by the
+<i class="type">ne_request</i> type, specifies that some operation is to be
+performed on some resource.  The
+<tt class="function">ne_request_create</tt> function creates a request
+object, specifying the operation in the <tt class="parameter">method</tt>
+parameter. The location of the resource is determined by the server in
+use for the session given by the <tt class="parameter">sess</tt>
+parameter, combined with the <tt class="parameter">path</tt> parameter.</p><p>The <tt class="parameter">path</tt> string used must conform to the
+<tt class="literal">abs_path</tt> definition given in RFC2396, with an
+optional "?query" part, and must be URI-escaped by the caller (for
+instance, using <tt class="function">ne_path_escape</tt>).  If the string
+comes from an untrusted source, failure to perform URI-escaping
+results in a security vulnerability.</p><p>To dispatch a request, and process the response, the
+<tt class="function">ne_request_dispatch</tt> function can be used.  An
+alternative is to use the (more complex, but more flexible)
+combination of the <tt class="function">ne_begin_request</tt>,
+<tt class="function">ne_end_request</tt>, and
+<tt class="function">ne_read_response_block</tt> functions; see
+<tt class="function">ne_begin_request</tt>.</p><p>To add extra headers in the request, the functions <a href="refreqhdr.html#ne_add_request_header">ne_add_request_header</a> and <a href="refreqhdr.html#ne_print_request_header">ne_print_request_header</a> can be used.  To include a message
+body with the request, one of the functions
+<tt class="function">ne_set_request_body_buffer</tt>, <a href="refreqbody.html#ne_set_request_body_fd">ne_set_request_body_fd</a>, or
+<tt class="function">ne_set_request_body_provider</tt> can be used.</p><p>The return value of
+<tt class="function">ne_request_dispatch</tt> indicates merely whether the
+request was sent and the response read successfully.  To discover the
+result of the operation, <a href="refgetst.html#ne_get_status">ne_get_status</a>, along with
+any processing of the response headers and message body.</p><p>A request can only be dispatched once: calling
+<tt class="function">ne_request_dispatch</tt> more than once on a single
+<i class="type">ne_request</i> object produces undefined behaviour.  Once all
+processing associated with the request object is complete, use the
+<tt class="function">ne_request_destroy</tt> function to destroy the
+resources associated with it.  Any subsequent use of the request
+object produces undefined behaviour.</p></div><div class="refsect1" lang="en"><a name="id3027607"></a><h2>Return value</h2><p>The <tt class="function">ne_request_create</tt> function
+returns a pointer to a request object (and never <tt class="literal">NULL</tt>).</p><p>The <tt class="function">ne_request_dispatch</tt> function
+returns zero if the request was dispatched successfully, and a
+non-zero error code otherwise.</p></div><div class="refsect1" lang="en"><a name="id3027646"></a><h2>Errors</h2><div class="variablelist"><table border="0"><col align="left" valign="top"><tbody><tr><td><span class="term"><span class="errorcode">NE_ERROR</span></span></td><td>Request failed (see session error string)</td></tr><tr><td><span class="term"><span class="errorcode">NE_LOOKUP</span></span></td><td>The DNS lookup for the server (or proxy server) failed.</td></tr><tr><td><span class="term"><span class="errorcode">NE_AUTH</span></span></td><td>Authentication failed on the server.</td></tr><tr><td><span class="term"><span class="errorcode">NE_PROXYAUTH</span></span></td><td>Authentication failed on the proxy server.</td></tr><tr><td><span class="term"><span class="errorcode">NE_CONNECT</span></span></td><td>A connection to the server could not be established.</td></tr><tr><td><span class="term"><span class="errorcode">NE_TIMEOUT</span></span></td><td>A timeout occurred while waiting for the server to respond.</td></tr></tbody></table></div></div><div class="refsect1" lang="en"><a name="id3027753"></a><h2>Example</h2><p>An example of applying a <tt class="literal">MKCOL</tt>
+       operation to the resource at the location 
+       <tt class="literal">http://www.example.com/foo/bar/</tt>:</p><pre class="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);</pre></div><div class="refsect1" lang="en"><a name="id3027786"></a><h2>See also</h2><p><a href="referr.html#ne_get_error">ne_get_error</a>, <a href="referr.html#ne_set_error">ne_set_error</a>, <a href="refgetst.html#ne_get_status">ne_get_status</a>, <a href="refreqhdr.html#ne_add_request_header">ne_add_request_header</a>, <a href="refreqbody.html#ne_set_request_body_buffer">ne_set_request_body_buffer</a>.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refalloc.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refsess.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_malloc </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_session_create</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refreqbody.html b/neon/doc/html/refreqbody.html
new file mode 100644 (file)
index 0000000..5bead72
--- /dev/null
@@ -0,0 +1,4 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_set_request_body_buffer</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refopts.html" title="ne_set_useragent"><link rel="next" href="refauth.html" title="ne_set_server_auth"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_set_request_body_buffer</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refopts.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refauth.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refreqbody"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_set_request_body_buffer, ne_set_request_body_fd &#8212; include a message body with a request</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_request.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_set_request_body_buffer</b>(</code>ne_request *<var xmlns="" class="pdparam">req</var>, const char *<var xmlns="" class="pdparam">buf</var>, size_t <var xmlns="" class="pdparam">count</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3029363"></a><h2>Description</h2><p>The <tt class="function">ne_set_request_body_buffer</tt>
+function specifies that a message body should be included with the
+body, which is stored in the <tt class="parameter">count</tt> bytes buffer
+<tt class="parameter">buf</tt>.</p></div><div class="refsect1" lang="en"><a name="id3030206"></a><h2>See also</h2><p><a href="refreq.html#ne_request_create">ne_request_create</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refopts.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refauth.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_set_useragent </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_set_server_auth</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refreqhdr.html b/neon/doc/html/refreqhdr.html
new file mode 100644 (file)
index 0000000..5c3ea31
--- /dev/null
@@ -0,0 +1,8 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_add_request_header</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refconfig.html" title="neon-config"><link rel="next" href="refresolve.html" title="ne_addr_resolve"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_add_request_header</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refconfig.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refresolve.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refreqhdr"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_add_request_header, ne_print_request_header &#8212; add headers to a request</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_request.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_add_request_header</b>(</code>ne_request *<var xmlns="" class="pdparam">request</var>, const char *<var xmlns="" class="pdparam">name</var>, const char *<var xmlns="" class="pdparam">value</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_print_request_header</b>(</code>ne_request *<var xmlns="" class="pdparam">request</var>, const char *<var xmlns="" class="pdparam">name</var>, const char *<var xmlns="" class="pdparam">format</var>, ...<code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3021663"></a><h2>Description</h2><p>The functions <tt class="function">ne_add_request_header</tt>
+and <tt class="function">ne_print_request_header</tt> can be used to add
+headers to a request, before it is sent.</p><p><tt class="function">ne_add_request_header</tt> simply adds a
+header of given <tt class="parameter">name</tt>, with given
+<tt class="parameter">value</tt>.</p><p><tt class="function">ne_print_request_header</tt> adds a
+header of given <tt class="parameter">name</tt>, taking the value from the
+<tt class="function">printf</tt>-like <tt class="parameter">format</tt> string
+parameter and subsequent variable-length argument list.</p></div><div class="refsect1" lang="en"><a name="id3021735"></a><h2>See also</h2><p><a href="refreq.html#ne_request_create">ne_request_create</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refconfig.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refresolve.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">neon-config </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_addr_resolve</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refresolve.html b/neon/doc/html/refresolve.html
new file mode 100644 (file)
index 0000000..4273b11
--- /dev/null
@@ -0,0 +1,48 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_addr_resolve</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refreqhdr.html" title="ne_add_request_header"><link rel="next" href="refbuf.html" title="ne_buffer"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_addr_resolve</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refreqhdr.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refbuf.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refresolve"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_addr_resolve, ne_addr_result, ne_addr_first, ne_addr_next, ne_addr_error, ne_addr_destroy &#8212; functions to resolve hostnames to addresses</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_socket.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_sock_addr *<b class="fsfunc">ne_addr_resolve</b>(</code>const char *<var xmlns="" class="pdparam">hostname</var>, int <var xmlns="" class="pdparam">flags</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_addr_result</b>(</code>const ne_sock_addr *<var xmlns="" class="pdparam">addr</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const ne_inet_addr *<b class="fsfunc">ne_addr_first</b>(</code>ne_sock_addr *<var xmlns="" class="pdparam">addr</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const ne_inet_addr *<b class="fsfunc">ne_addr_next</b>(</code>ne_sock_addr *<var xmlns="" class="pdparam">addr</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_addr_error</b>(</code>const ne_sock_addr *<var xmlns="" class="pdparam">addr</var>, char *<var xmlns="" class="pdparam">buffer</var>, size_t <var xmlns="" class="pdparam">bufsiz</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_addr_destroy</b>(</code>ne_sock_addr *<var xmlns="" class="pdparam">addr</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3022216"></a><h2>Description</h2><p>The <tt class="function">ne_addr_resolve</tt> function resolves
+    the given <tt class="parameter">hostname</tt>, returning an
+    <i class="type">ne_sock_addr</i> object representing the address (or
+    addresses) associated with the hostname.  The
+    <tt class="parameter">flags</tt> parameter is currently unused, and
+    must be passed as 0.</p><p>The <tt class="parameter">hostname</tt> passed to
+    <tt class="function">ne_addr_resolve</tt> can be a DNS hostname
+    (e.g. <tt class="literal">"www.example.com"</tt>) or an IPv4 dotted quad
+    (e.g. <tt class="literal">"192.0.34.72"</tt>); or, on systems which
+    support IPv6, an IPv6 hex address, which may be enclosed in
+    brackets, e.g. <tt class="literal">"[::1]"</tt>.</p><p>To determine whether the hostname was successfully resolved,
+    the <tt class="function">ne_addr_result</tt> function is used, which
+    returns non-zero if an error occurred.  If an error did occur, the
+    <tt class="function">ne_addr_error</tt> function can be used, which
+    will copy the error string into a given
+    <tt class="parameter">buffer</tt> (of size
+    <tt class="parameter">bufsiz</tt>).</p><p>The functions <tt class="function">ne_addr_first</tt> and
+    <tt class="function">ne_addr_next</tt> are used to retrieve the
+    Internet addresses associated with an address object which has
+    been successfully resolved.  <tt class="function">ne_addr_first</tt>
+    returns the first address; <tt class="function">ne_addr_next</tt>
+    returns the next address after the most recent call to
+    <tt class="function">ne_addr_next</tt> or
+    <tt class="function">ne_addr_first</tt>, or <tt class="literal">NULL</tt> if there are no more
+    addresses.  The <i class="type">ne_inet_addr</i> pointer returned by
+    these functions can be passed to
+    <tt class="function">ne_sock_connect</tt> to connect a socket.</p><p>After the address object has been used, it should be
+    destroyed using <tt class="function">ne_addr_destroy</tt>.</p></div><div class="refsect1" lang="en"><a name="id3022391"></a><h2>Return value</h2><p><tt class="function">ne_addr_resolve</tt> returns a pointer to an
+    address object, and never <tt class="literal">NULL</tt>.
+    <tt class="function">ne_addr_error</tt> returns the
+    <tt class="parameter">buffer</tt> parameter .</p></div><div class="refsect1" lang="en"><a name="id3022426"></a><h2>Examples</h2><p>The code below prints out the set of addresses associated
+    with the hostname <tt class="literal">www.google.com</tt>.</p><pre class="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 {
+    const ne_inet_addr *ia;
+    printf("www.google.com:");
+    for (ia = ne_addr_first(addr); ia != NULL; ia = ne_addr_next(addr)) {
+        printf(" %s", ne_iaddr_print(ia, buf, sizeof buf));
+    }
+    putchar('\n');
+}
+ne_addr_destroy(addr);
+</pre></div><div class="refsect1" lang="en"><a name="id3022456"></a><h2>See also</h2><p><a href="refiaddr.html#ne_iaddr_print">ne_iaddr_print</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refreqhdr.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refbuf.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_add_request_header </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_buffer</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refsess.html b/neon/doc/html/refsess.html
new file mode 100644 (file)
index 0000000..cb1eece
--- /dev/null
@@ -0,0 +1,35 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_session_create</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refreq.html" title="ne_request_create"><link rel="next" href="refopts.html" title="ne_set_useragent"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_session_create</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refreq.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refopts.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refsess"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_session_create, ne_close_connection, ne_session_proxy, ne_session_destroy &#8212; set up HTTP sessions</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_session.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_session *<b class="fsfunc">ne_session_create</b>(</code>const char *<var xmlns="" class="pdparam">scheme</var>, const char *<var xmlns="" class="pdparam">hostname</var>, unsigned int <var xmlns="" class="pdparam">port</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_session_proxy</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, const char *<var xmlns="" class="pdparam">hostname</var>, unsigned int <var xmlns="" class="pdparam">port</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_close_connection</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_session_destroy</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3028602"></a><h2>Description</h2><p>An <i class="type">ne_session</i> object represents an HTTP
+session - a logical grouping of a sequence of HTTP requests made to a
+certain server. Any requests made using the session can use a
+persistent connection, share cached authentication credentials and any
+other common attributes.</p><p>A new HTTP session is created using
+<tt class="function">ne_session_create</tt>, giving the
+<tt class="parameter">hostname</tt> and <tt class="parameter">port</tt> of the
+server to use, along with the <tt class="parameter">scheme</tt> used to
+contact the server (usually <tt class="literal">"http"</tt>).  Before the
+first use of <tt class="function">ne_session_create</tt> in a process,
+<a href="refsockinit.html#ne_sock_init">ne_sock_init</a> must have been called to perform any
+global initialization needed by any libraries used by neon.</p><p>To enable SSL/TLS for the session, pass the string
+<tt class="literal">"https"</tt> as the <tt class="parameter">scheme</tt>
+parameter, and either register a certificate verification function
+(see <a href="refsslvfy.html#ne_ssl_set_verify">ne_ssl_set_verify</a>) or trust the appropriate 
+certificate (see <a href="refsslca.html#ne_ssl_trust_cert">ne_ssl_trust_cert</a>, <a href="refsslca.html#ne_ssl_trust_default_ca">ne_ssl_trust_default_ca</a>).</p><p>If an HTTP proxy server should be used for the session,
+<tt class="function">ne_session_proxy</tt> must be called giving the
+hostname and port on which to contact the proxy.</p><p>If it is known that the session will not be used for a
+significant period of time, <tt class="function">ne_close_connection</tt>
+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.</p><p>Once a session has been completed,
+<tt class="function">ne_session_destroy</tt> must be called to destroy the
+resources associated with the session.  Any subsequent use of the
+session pointer produces undefined behaviour.</p></div><div class="refsect1" lang="en"><a name="id3028744"></a><h2>Notes</h2><p>The hostname passed to
+<tt class="function">ne_session_create</tt> is resolved when the first
+request using the session is dispatched; a DNS resolution failure can
+only be detected at that time (using the <tt class="literal">NE_LOOKUP</tt>
+error code); see <a href="refreq.html#ne_request_dispatch">ne_request_dispatch</a> for
+details.</p></div><div class="refsect1" lang="en"><a name="id3028775"></a><h2>Return Values</h2><p><tt class="function">ne_session_create</tt> will return
+       a pointer to a new session object (and never <tt class="literal">NULL</tt>).</p></div><div class="refsect1" lang="en"><a name="id3028797"></a><h2>Examples</h2><p>Create and destroy a session:</p><pre class="programlisting">ne_session *sess;
+sess = ne_session_create("http", "host.example.com", 80);
+/* ... use sess ... */
+ne_session_destroy(sess);
+</pre></div><div class="refsect1" lang="en"><a name="id3028816"></a><h2>See Also</h2><p><a href="refsslvfy.html#ne_ssl_set_verify">ne_ssl_set_verify</a>, <a href="refsslca.html#ne_ssl_trust_cert">ne_ssl_trust_cert</a>, <a href="refsockinit.html#ne_sock_init">ne_sock_init</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refreq.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refopts.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_request_create </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_set_useragent</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refshave.html b/neon/doc/html/refshave.html
new file mode 100644 (file)
index 0000000..4292965
--- /dev/null
@@ -0,0 +1,7 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_shave</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refauth.html" title="ne_set_server_auth"><link rel="next" href="refsockinit.html" title="ne_sock_init"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_shave</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refauth.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refsockinit.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refshave"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_shave &#8212; trim whitespace from a string</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_string.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_shave</b>(</code>char *<var xmlns="" class="pdparam">str</var>, const char *<var xmlns="" class="pdparam">whitespace</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3031186"></a><h2>Description</h2><p><tt class="function">ne_shave</tt> returns a portion of
+<tt class="parameter">str</tt> with any leading or trailing characters in
+the <tt class="parameter">whitespace</tt> array removed.
+<tt class="parameter">str</tt> may be modified.  Note that the return
+value may not be equal to <tt class="parameter">str</tt>.</p></div><div class="refsect1" lang="en"><a name="id3031226"></a><h2>Examples</h2><p>The following code segment will output
+       <tt class="literal">"fish"</tt>:</p><pre class="programlisting">char s[] = ".!.fish!.!";
+puts(ne_shave(s, ".!"));</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refauth.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refsockinit.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_set_server_auth </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_sock_init</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refsockinit.html b/neon/doc/html/refsockinit.html
new file mode 100644 (file)
index 0000000..b8ac9d4
--- /dev/null
@@ -0,0 +1,7 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_sock_init</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refshave.html" title="ne_shave"><link rel="next" href="refcert.html" title="ne_ssl_cert_identity"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_sock_init</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refshave.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refcert.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refsockinit"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_sock_init &#8212; perform library initialization</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_socket.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_sock_init</b>(</code><code xmlns="">void)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3031110"></a><h2>Description</h2><p>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
+    <tt class="function">ne_sock_init</tt> function must be called once
+    before any other library functions are used.</p></div><div class="refsect1" lang="en"><a name="id3031131"></a><h2>Return value</h2><p><tt class="function">ne_sock_init</tt> returns zero on success,
+    or non-zero on error.  If an error occurs, no further use of the
+    neon library should be attempted.</p></div><div class="refsect1" lang="en"><a name="id3031149"></a><h2>See also</h2><p><a href="refneon.html" title="neon"><span class="refentrytitle">neon</span></a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refshave.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refcert.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_shave </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_ssl_cert_identity</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refsslca.html b/neon/doc/html/refsslca.html
new file mode 100644 (file)
index 0000000..525be40
--- /dev/null
@@ -0,0 +1,15 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_ssl_trust_cert</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refssldname.html" title="ne_ssl_dname"><link rel="next" href="refsslvfy.html" title="ne_ssl_set_verify"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_ssl_trust_cert</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refssldname.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refsslvfy.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refsslca"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_ssl_trust_cert, ne_ssl_trust_default_ca &#8212; functions to indicate that certificates are trusted</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_session.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_ssl_trust_cert</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_ssl_trust_default_ca</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3033806"></a><h2>Description</h2><p>To indicate that a given certificate is trusted by the
+user, the certificate object can be passed to
+<tt class="function">ne_ssl_trust_cert</tt>.  The certificate object is
+duplicated internally and can subequently be destroyed.</p><p>The SSL library in use by neon may include a default
+set of CA certificates; calling the
+<tt class="function">ne_ssl_trust_default_ca</tt> function will indicate
+that these CAs are trusted by the user.</p></div><div class="refsect1" lang="en"><a name="id3033837"></a><h2>Examples</h2><p>Load the CA certificate stored in <tt class="filename">/path/to/cacert.pem</tt>:</p><pre class="programlisting">ne_session *sess = ne_session_create(...);
+ne_ssl_certificate *cert = ne_ssl_cert_read("/path/to/cacert.pem");
+
+if (cert) {
+   ne_ssl_trust_cert(sess, cert);
+   ne_ssl_cert_free(cert);
+} else {
+   printf("Could not load CA cert: %s\n", ne_get_error(sess));
+}</pre></div><div class="refsect1" lang="en"><a name="id3033864"></a><h2>See also</h2><p><a href="refsslcertio.html#ne_ssl_cert_read">ne_ssl_cert_read</a>, <a href="refsslcertio.html#ne_ssl_cert_import">ne_ssl_cert_import</a>, <a href="refsslcert2.html#ne_ssl_cert_free">ne_ssl_cert_free</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refssldname.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refsslvfy.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_ssl_dname </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_ssl_set_verify</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refsslcert2.html b/neon/doc/html/refsslcert2.html
new file mode 100644 (file)
index 0000000..d25dbec
--- /dev/null
@@ -0,0 +1,5 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_ssl_cert_cmp</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refcert.html" title="ne_ssl_cert_identity"><link rel="next" href="refsslcertio.html" title="ne_ssl_cert_read"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_ssl_cert_cmp</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refcert.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refsslcertio.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refsslcert2"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_ssl_cert_cmp, ne_ssl_cert_free &#8212; functions to operate on certificate objects</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_header.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_ssl_cert_cmp</b>(</code>const ne_ssl_certificate *<var xmlns="" class="pdparam">c1</var>, const ne_ssl_certificate *<var xmlns="" class="pdparam">c2</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_ssl_cert_free</b>(</code>ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3032404"></a><h2>Description</h2><p>The <tt class="function">ne_ssl_cert_cmp</tt> function can be
+    used to compare two certificate objects; it returns zero if they
+    refer to the same certificate, and non-zero otherwise.</p><p>The <tt class="function">ne_ssl_cert_free</tt> function can be
+    used to destroy a certificate object when it is no longer
+    needed.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refcert.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refsslcertio.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_ssl_cert_identity </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_ssl_cert_read</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refsslcertio.html b/neon/doc/html/refsslcertio.html
new file mode 100644 (file)
index 0000000..23c8109
--- /dev/null
@@ -0,0 +1,24 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_ssl_cert_read</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refsslcert2.html" title="ne_ssl_cert_cmp"><link rel="next" href="refssldname.html" title="ne_ssl_dname"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_ssl_cert_read</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refsslcert2.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refssldname.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refsslcertio"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_ssl_cert_read, ne_ssl_cert_write, ne_ssl_cert_import, ne_ssl_cert_export &#8212; functions to read or write certificates to and from files or strings</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_ssl.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_ssl_certificate *<b class="fsfunc">ne_ssl_cert_read</b>(</code>const char *<var xmlns="" class="pdparam">filename</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_ssl_cert_write</b>(</code>const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var>, const char *<var xmlns="" class="pdparam">filename</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">ne_ssl_certificate *<b class="fsfunc">ne_ssl_cert_import</b>(</code>const char *<var xmlns="" class="pdparam">data</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_ssl_cert_export</b>(</code>const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3032737"></a><h2>Description</h2><p>The <tt class="function">ne_ssl_cert_write</tt> function writes a
+    certificate to a file using the PEM encoding.  The
+    <tt class="function">ne_ssl_cert_export</tt> function returns a
+    base64-encoded <tt class="literal">NUL</tt>-terminated string representing the
+    certificate.  This string is malloc-allocated and should be
+    destroyed using <tt class="function">free</tt> by the caller.</p><p>The <tt class="function">ne_ssl_cert_read</tt> function reads a
+    certificate from a PEM-encoded file, and returns a certificate
+    object.  The <tt class="function">ne_ssl_cert_import</tt> function
+    returns a certificate object from a base64-encoded string,
+    <tt class="parameter">data</tt>, as returned by
+    <tt class="function">ne_ssl_cert_export</tt>.  The certificate object
+    returned by these functions should be destroyed using <a href="refsslcert2.html#ne_ssl_cert_free">ne_ssl_cert_free</a> after use.</p></div><div class="refsect1" lang="en"><a name="id3032815"></a><h2>Return value</h2><p><tt class="function">ne_ssl_cert_read</tt> returns <tt class="literal">NULL</tt> if a
+    certificate could not be read from the file.
+    <tt class="function">ne_ssl_cert_write</tt> returns non-zero if the
+    certificate could not be written to the file.
+    <tt class="function">ne_ssl_cert_export</tt> always returns a
+    <tt class="literal">NUL</tt>-terminated string, and never <tt class="literal">NULL</tt>.
+    <tt class="function">ne_ssl_cert_import</tt> returns <tt class="literal">NULL</tt> if the
+    string was not a valid base64-encoded certificate.</p></div><div class="refsect1" lang="en"><a name="id3032878"></a><h2>Encoding Formats</h2><p>The string produced by
+    <tt class="function">ne_ssl_cert_export</tt> is the base64 encoding of
+    the DER representation of the certificate.  The file written by
+    <tt class="function">ne_ssl_cert_write</tt> uses the PEM format: this
+    is the base64 encoding of the DER representation with newlines
+    every 64 characters, and start and end marker lines.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refsslcert2.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refssldname.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_ssl_cert_cmp </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_ssl_dname</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refssldname.html b/neon/doc/html/refssldname.html
new file mode 100644 (file)
index 0000000..9fd2169
--- /dev/null
@@ -0,0 +1,9 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_ssl_dname</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refsslcertio.html" title="ne_ssl_cert_read"><link rel="next" href="refsslca.html" title="ne_ssl_trust_cert"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_ssl_dname</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refsslcertio.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refsslca.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refssldname"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_ssl_readable_dname, ne_ssl_dname_cmp &#8212; SSL distinguished name handling</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_ssl.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const char *<b class="fsfunc">ne_ssl_readable_dname</b>(</code>const ne_ssl_dname *<var xmlns="" class="pdparam">dname</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_ssl_dname_cmp</b>(</code>const ne_ssl_dname *<var xmlns="" class="pdparam">dn1</var>, const ne_ssl_dname *<var xmlns="" class="pdparam">dn2</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3033358"></a><h2>Description</h2><p>The <tt class="function">ne_ssl_readable_dname</tt> function
+creates a single-line, human-readable string out of an
+<i class="type">ne_ssl_dname</i> object.  The returned string is
+<tt class="function">malloc</tt>()-allocated, and must be
+<tt class="function">free</tt>()d by the caller.</p><p>The <tt class="function">ne_ssl_dname_cmp</tt> function
+       compares two distinguished names, and returns zero if they are
+       equal, or non-zero otherwise.</p></div><div class="refsect1" lang="en"><a name="id3033404"></a><h2>Return value</h2><p><tt class="function">ne_ssl_readable_dname</tt> returns a <tt class="function">malloc</tt>-allocated
+        string, and never <tt class="literal">NULL</tt>.</p></div><div class="refsect1" lang="en"><a name="id3033432"></a><h2>Examples</h2><p>See <a href="refcert.html#ne_ssl_cert_subject">ne_ssl_cert_subject</a> for an example
+        use of <tt class="function">ne_ssl_readable_dname</tt>.</p></div><div class="refsect1" lang="en"><a name="id3033455"></a><h2>See also</h2><p><a href="refcert.html#ne_ssl_cert_subject">ne_ssl_cert_subject</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refsslcertio.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refsslca.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_ssl_cert_read </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_ssl_trust_cert</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refsslvfy.html b/neon/doc/html/refsslvfy.html
new file mode 100644 (file)
index 0000000..21fd174
--- /dev/null
@@ -0,0 +1,66 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_ssl_set_verify</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refsslca.html" title="ne_ssl_trust_cert"><link rel="next" href="refclicert.html" title="ne_ssl_client_cert"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_ssl_set_verify</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refsslca.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refclicert.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refsslvfy"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_ssl_set_verify &#8212; register an SSL certificate verification callback</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_session.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">typedef int <b class="fsfunc">ne_ssl_verify_fn</b>(</code>void *<var xmlns="" class="pdparam">userdata</var>, int <var xmlns="" class="pdparam">failures</var>, const ne_ssl_certificate *<var xmlns="" class="pdparam">cert</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">void <b class="fsfunc">ne_ssl_set_verify</b>(</code>ne_session *<var xmlns="" class="pdparam">session</var>, ne_ssl_verify_fn <var xmlns="" class="pdparam">verify_fn</var>, void *<var xmlns="" class="pdparam">userdata</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3034219"></a><h2>Description</h2><p>To enable manual SSL certificate verification, a
+callback can be registered using
+<tt class="function">ne_ssl_set_verify</tt>.  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 <a href="refsslca.html#ne_ssl_trust_cert">ne_ssl_trust_cert</a>), or if the certificate presented is invalid in
+some way, the connection will fail.</p><p>When the callback is invoked, the
+<tt class="parameter">failures</tt> 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):</p><div class="variablelist"><table border="0"><col align="left" valign="top"><tbody><tr><td><span class="term"><tt class="constant">NE_SSL_NOTYETVALID</tt></span></td><td>The certificate is not yet valid.</td></tr><tr><td><span class="term"><tt class="constant">NE_SSL_EXPIRED</tt></span></td><td>The certificate has expired.</td></tr><tr><td><span class="term"><tt class="constant">NE_SSL_IDMISMATCH</tt></span></td><td>The hostname used for the session does not match
+the hostname to which the certificate was issued.</td></tr><tr><td><span class="term"><tt class="constant">NE_SSL_UNTRUSTED</tt></span></td><td>The Certificate Authority which signed the certificate
+is not trusted.</td></tr></tbody></table></div><p>Note that if either of the
+       <tt class="constant">NE_SSL_IDMISMATCH</tt> or
+       <tt class="constant">NE_SSL_UNTRUSTED</tt> failures is given, the
+       connection may have been intercepted by a third party, and
+       must not be presumed to be &#8220;<span class="quote">secure</span>&#8221;.</p><p>The <tt class="parameter">cert</tt> parameter passed to the
+callback represents the certificate which was presented by the server.
+If the server presented a chain of certificates, the chain can be
+accessed using <a href="refcert.html#ne_ssl_cert_signedby">ne_ssl_cert_signedby</a>.  The
+<tt class="parameter">cert</tt> object given is not valid after the
+callback returns.</p></div><div class="refsect1" lang="en"><a name="id3034364"></a><h2>Return value</h2><p>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).</p></div><div class="refsect1" lang="en"><a name="id3034377"></a><h2>Examples</h2><p>The following code implements an example verification
+       callback, using the <tt class="function">dump_cert</tt> function
+       from <a href="refcert.html#ne_ssl_cert_subject">ne_ssl_cert_subject</a> to display
+       certification information.  Notice that the hostname of the
+       server used for the session is passed as the
+       <tt class="parameter">userdata</tt> parameter to the
+       callback.</p><pre class="programlisting">
+static int
+my_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
+{
+  const char *hostname = userdata;
+
+  dump_cert(cert);
+
+  puts("Certificate verification failed - the connection may have been "
+       "intercepted by a third party!");
+
+  if (failures &amp; NE_SSL_IDMISMATCH) { 
+    const char *id = ne_ssl_cert_identity(cert);
+    if (id) 
+      printf("Server certificate was issued to '%s' not '%s'.\n",
+             id, hostname);
+    else
+      printf("The certificate was not issued for '%s'\n", hostname);
+  }
+
+  if (failures &amp; NE_SSL_UNTRUSTED)
+    puts("The certificate is not signed by a trusted Certificate Authority.");
+
+  /* ... check for validity failures ... */
+
+  if (prompt_user())
+    return 1; /* fail verification */
+  else
+    return 0; /* trust the certificate anyway */
+}
+
+int
+main(...)
+{
+  ne_session *sess = ne_session_create("https", "some.host.name", 443);
+  ne_ssl_set_verify(sess, my_verify, "some.host.name");
+  ...
+}</pre></div><div class="refsect1" lang="en"><a name="id3034441"></a><h2>See also</h2><p><a href="refsslca.html#ne_ssl_trust_cert">ne_ssl_trust_cert</a>, <a href="refssldname.html#ne_ssl_readable_dname">ne_ssl_readable_dname</a>, <a href="refcert.html#ne_ssl_cert_subject">ne_ssl_cert_subject</a></p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refsslca.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refclicert.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_ssl_trust_cert </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_ssl_client_cert</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refstatus.html b/neon/doc/html/refstatus.html
new file mode 100644 (file)
index 0000000..54ffa91
--- /dev/null
@@ -0,0 +1,22 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_status</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refclicert.html" title="ne_ssl_client_cert"><link rel="next" href="reftok.html" title="ne_token"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_status</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refclicert.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="reftok.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refstatus"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_status &#8212; HTTP status structure</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_utils.h&gt;
+
+typedef struct {
+    int major_version, minor_version;
+    int code, klass;
+    const char *reason_phrase;
+} <i class="type">ne_status</i>;</pre></div></div><div class="refsect1" lang="en"><a name="id3035848"></a><h2>Description</h2><p>An <i class="type">ne_status</i> type represents an HTTP
+response status; used in response messages giving a result of request.
+The <i class="structfield"><tt>major_version</tt></i> and
+<i class="structfield"><tt>minor_version</tt></i> fields give the HTTP version
+supported by the server issuing the response.  The
+<i class="structfield"><tt>code</tt></i> field gives the status code of the
+result (lying between 100 and 999 inclusive), and the
+<i class="structfield"><tt>klass</tt></i> field gives the
+class<sup>[<a name="id3035878" href="#ftn.id3035878">2</a>]</sup>, which is equal to the most significant digit
+of the status.</p><p>There are five classes of HTTP status code defined by
+       RFC2616:</p><div class="variablelist"><table border="0"><col align="left" valign="top"><tbody><tr><td><span class="term"><tt class="literal">1xx</tt></span></td><td>Informational response.</td></tr><tr><td><span class="term"><tt class="literal">2xx</tt></span></td><td>Success: the operation was successful</td></tr><tr><td><span class="term"><tt class="literal">3xx</tt></span></td><td>Redirection</td></tr><tr><td><span class="term"><tt class="literal">4xx</tt></span></td><td>Client
+           error: the request made was incorrect in some
+           manner.</td></tr><tr><td><span class="term"><tt class="literal">5xx</tt></span></td><td>Server error</td></tr></tbody></table></div></div><div class="refsect1" lang="en"><a name="id3035979"></a><h2>See also</h2><p><a href="refgetst.html#ne_get_status">ne_get_status</a>.</p></div><div class="footnotes"><br><hr width="100" align="left"><div class="footnote"><p><sup>[<a name="ftn.id3035878" href="#id3035878">2</a>] </sup>the field is named &#8220;<span class="quote">klass</span>&#8221; not
+&#8220;<span class="quote">class</span>&#8221; so that the header can be used from a C++
+program, in which &#8220;<span class="quote">class</span>&#8221; is a reserved
+word)</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refclicert.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="reftok.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_ssl_client_cert </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_token</td></tr></table></div></body></html>
diff --git a/neon/doc/html/reftok.html b/neon/doc/html/reftok.html
new file mode 100644 (file)
index 0000000..3403e98
--- /dev/null
@@ -0,0 +1,20 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_token</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="refstatus.html" title="ne_status"><link rel="next" href="refvers.html" title="ne_version_match"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_token</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="refstatus.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="refvers.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="reftok"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_token, ne_qtoken &#8212; string tokenizers</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_string.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_token</b>(</code>char **<var xmlns="" class="pdparam">str</var>, char <var xmlns="" class="pdparam">sep</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">char *<b class="fsfunc">ne_qtoken</b>(</code>char **<var xmlns="" class="pdparam">str</var>, char <var xmlns="" class="pdparam">sep</var>, const char *<var xmlns="" class="pdparam">quotes</var><code xmlns="">)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3036326"></a><h2>Description</h2><p><tt class="function">ne_token</tt> and
+<tt class="function">ne_qtoken</tt> tokenize the string at the location
+stored in the pointer <tt class="parameter">str</tt>.  Each time the
+function is called, it returns the next token, and modifies the
+<tt class="parameter">str</tt> pointer to point to the remainer of the
+string, or <tt class="literal">NULL</tt> if there are no more tokens in the string.  A token
+is delimited by the separator character <tt class="parameter">sep</tt>; if
+<tt class="function">ne_qtoken</tt> 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
+<tt class="parameter">quotes</tt> string.</p><p>The string being tokenized is modified each time
+the tokenizing function is called; replacing the next separator
+character with a <tt class="literal">NUL</tt> terminator.</p></div><div class="refsect1" lang="en"><a name="id3036405"></a><h2>Examples</h2><p>The following function prints out each token in a
+comma-separated string <tt class="parameter">list</tt>, which is
+modified in-place:</p><pre class="programlisting">static void splitter(char *list)
+{
+  do {
+    printf("Token: %s\n", ne_token(&amp;list, ','));
+  while (list);
+}</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="refstatus.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="refvers.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_status </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> ne_version_match</td></tr></table></div></body></html>
diff --git a/neon/doc/html/refvers.html b/neon/doc/html/refvers.html
new file mode 100644 (file)
index 0000000..5ae7103
--- /dev/null
@@ -0,0 +1,11 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>ne_version_match</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="ref.html" title="neon API reference"><link rel="previous" href="reftok.html" title="ne_token"><link rel="next" href="biblio.html" title="Bibliography"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">ne_version_match</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="reftok.html">Prev</a> </td><th width="60%" align="center">neon API reference</th><td width="20%" align="right"> <a accesskey="n" href="biblio.html">Next</a></td></tr></table><hr></div><div class="refentry" lang="en"><a name="refvers"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2>Name</h2><p>ne_version_match, ne_version_string &#8212; library versioning</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="funcsynopsis"><pre class="funcsynopsisinfo">#include &lt;ne_utils.h&gt;</pre><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">int <b class="fsfunc">ne_version_match</b>(</code>int <var xmlns="" class="pdparam">major</var>, int <var xmlns="" class="pdparam">minor</var><code xmlns="">)</code>;</code></div><div xmlns="http://www.w3.org/TR/xhtml1/transitional" class="funcprototype"><code><code xmlns="" class="funcdef">const char *<b class="fsfunc">ne_version_string</b>(</code><code xmlns="">void)</code>;</code></div></div></div><div class="refsect1" lang="en"><a name="id3035751"></a><h2>Description</h2><p>The <tt class="function">ne_version_match</tt> function returns
+    non-zero if the library version is not of major version
+    <tt class="parameter">major</tt>, or the minor version is less than
+    <tt class="parameter">minor</tt>.  For neon versions 0.x, every
+    minor version is assumed to be incompatible with every other minor
+    version.</p><p>The <tt class="function">ne_version_string</tt> function returns
+    a string giving the library version.</p></div><div class="refsect1" lang="en"><a name="id3035796"></a><h2>Examples</h2><p>To require neon 1.x, version 1.2 or later:</p><pre class="programlisting">if (ne_version_match(1, 2)) {
+    printf("Library version out of date: 1.2 required, found %s.",
+           ne_version_string());
+    exit(1);
+}</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="reftok.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ref.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="biblio.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ne_token </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Bibliography</td></tr></table></div></body></html>
diff --git a/neon/doc/html/using.html b/neon/doc/html/using.html
new file mode 100644 (file)
index 0000000..65eb615
--- /dev/null
@@ -0,0 +1,40 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>How to use neon from your application</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="intro.html" title="Chapter 1. Introduction"><link rel="previous" href="features.html" title="Feature list"><link rel="next" href="compliance.html" title="Protocol compliance"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">How to use neon from your application</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="features.html">Prev</a> </td><th width="60%" align="center">Chapter 1. Introduction</th><td width="20%" align="right"> <a accesskey="n" href="compliance.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="using"></a>How to use neon from your application</h2></div></div><div></div></div><p>This section describes how to add neon support to an
+      application.  If you just want to quickly try out neon, use
+      the <a href="refconfig.html" title="neon-config"><span class="refentrytitle">neon-config</span></a> script.</p><p>The neon source code is designed to be easily embedded
+      into an application source tree.  neon has no dependencies on
+      libraries other than an SSL toolkit and XML parser, though the
+      source tree can be configured to have no support for SSL or XML
+      if desired.  To configure the neon source code some <a href="http://www.gnu.org/software/autoconf/" target="_top">GNU autoconf</a>
+      macros are supplied, which can be used in a number of ways, as
+      follows:</p><div class="itemizedlist"><ul type="disc"><li><p>autoconf macros are distributed in the 'macros'
+         subdirectory of the neon distribution.  Use the NEON_LIBRARY
+         macro 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).</p></li><li><p>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':</p><pre class="programlisting">NEON_BUNDLED(libneon, ...)</pre><p>If your application supports builds where srcdir != builddir,
+         you should use the NEON_VPATH_BUNDLED macro like this:</p><pre class="programlisting">NEON_VPATH_BUNDLED(${srcdir}/libneon, libneon, ...)</pre><p>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.</p></li><li><p>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.</p></li></ul></div><p>A full fragment might be:</p><pre class="programlisting">NEON_BUNDLED(libneon, [
+  NEON_NORMAL_BUILD
+  NEON_XML_PARSER
+  SUBDIRS="libneon $SUBDIRS"
+])</pre><p>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.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="features.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="intro.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="compliance.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Feature list </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Protocol compliance</td></tr></table></div></body></html>
diff --git a/neon/doc/html/xml.html b/neon/doc/html/xml.html
new file mode 100644 (file)
index 0000000..e79a7ae
--- /dev/null
@@ -0,0 +1,90 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Parsing XML</title><link rel="stylesheet" href="../manual.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="neon HTTP/WebDAV client library"><link rel="up" href="api.html" title="Chapter 2. The neon C language interface"><link rel="previous" href="api.html" title="Chapter 2. The neon C language interface"><link rel="next" href="ref.html" title="neon API reference"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Parsing XML</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="api.html">Prev</a> </td><th width="60%" align="center">Chapter 2. The neon C language interface</th><td width="20%" align="right"> <a accesskey="n" href="ref.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="xml"></a>Parsing XML</h2></div></div><div></div></div><p>The neon XML interface is exposed by the
+  <tt class="filename">ne_xml.h</tt> header file.  This interface gives a
+  wrapper around the standard <a href="http://www.saxproject.org/" target="_top">SAX</a> API used by XML
+  parsers, with an additional abstraction, <i class="firstterm">stacked SAX
+  handlers</i>, and also giving consistent <a href="http://www.w3.org/TR/REC-xml-names" target="_top">XML Namespace</a> support.</p><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="xml-sax"></a>Introduction to SAX</h3></div></div><div></div></div><p>A SAX-based parser works by emitting a sequence of
+  <i class="firstterm">events</i> to reflect the tokens being parsed
+  from the XML document.  For example, parsing the following document
+  fragment:
+
+</p><pre class="programlisting">
+&lt;hello&gt;world&lt;/hello&gt;
+</pre><p>
+
+  results in the following events:
+
+  </p><div class="orderedlist"><ol type="1"><li><span class="emphasis"><em>start-element</em></span> "hello"</li><li><span class="emphasis"><em>character-data</em></span> "world"</li><li><span class="emphasis"><em>end-element</em></span> "hello"</li></ol></div><p>
+
+  This example demonstrates the three event types used used in the
+  subset of SAX exposed by the neon XML interface: <span class="emphasis"><em>start-element</em></span>,
+  <span class="emphasis"><em>character-data</em></span> and <span class="emphasis"><em>end-element</em></span>.  In a C API, an &#8220;<span class="quote">event</span>&#8221; is
+  implemented as a function callback; three callback types are used in
+  neon, one for each type of event.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="xml-stacked"></a>Stacked SAX handlers</h3></div></div><div></div></div><p>WebDAV property values are represented as fragments of XML,
+  transmitted as parts of larger XML documents over HTTP (notably in
+  the body of the response to a <tt class="literal">PROPFIND</tt> request).
+  When neon parses such documents, the SAX events generated for
+  these property value fragments may need to be handled by the
+  application, since neon has no knowledge of the structure of
+  properties used by the application.</p><p>To solve this problem<sup>[<a name="id3018824" href="#ftn.id3018824">1</a>]</sup> the neon XML interface introduces
+  the concept of a <i class="firstterm">SAX handler</i>.  A SAX handler
+  comprises a <span class="emphasis"><em>start-element</em></span>, <span class="emphasis"><em>character-data</em></span> and <span class="emphasis"><em>end-element</em></span> callback; the
+  <span class="emphasis"><em>start-element</em></span> callback being defined such that each handler may
+  <span class="emphasis"><em>accept</em></span> or <span class="emphasis"><em>decline</em></span> the
+  <span class="emphasis"><em>start-element</em></span> event.  Handlers are composed into a <i class="firstterm">handler
+  stack</i> before parsing a document.  When a new <span class="emphasis"><em>start-element</em></span>
+  event is generated by the XML parser, neon invokes each <span class="emphasis"><em>start-element</em></span>
+  callback in the handler stack in turn until one accepts the event.
+  The handler which accepts the event will then be subsequently be
+  passed <span class="emphasis"><em>character-data</em></span> events if the element contains character data,
+  followed by an <span class="emphasis"><em>end-element</em></span> event when the element is closed.  If no
+  handler in the stack accepts a <span class="emphasis"><em>start-element</em></span> event, the branch of the
+  tree is ignored.</p><p>To illustrate, given a handler A, which accepts the
+  <tt class="literal">cat</tt> and <tt class="literal">age</tt> elements, and a
+  handler B, which accepts the <tt class="literal">name</tt> element, the
+  following document:
+
+</p><div class="example"><a name="xml-example"></a><p class="title"><b>Example 2.1. An example XML document</b></p><pre class="programlisting">
+&lt;cat&gt;
+  &lt;age&gt;3&lt;/age&gt;    
+  &lt;name&gt;Bob&lt;/name&gt;
+&lt;/cat&gt;
+</pre></div><p>
+
+  would be parsed as follows:
+  
+  </p><div class="orderedlist"><ol type="1"><li>A <span class="emphasis"><em>start-element</em></span> "cat" &#8594; <span class="emphasis"><em>accept</em></span></li><li>A <span class="emphasis"><em>start-element</em></span> "age" &#8594; <span class="emphasis"><em>accept</em></span></li><li>A <span class="emphasis"><em>character-data</em></span> "3"</li><li>A <span class="emphasis"><em>end-element</em></span> "age"</li><li>A <span class="emphasis"><em>start-element</em></span> "name" &#8594; <span class="emphasis"><em>decline</em></span></li><li>B <span class="emphasis"><em>start-element</em></span> "name" &#8594; <span class="emphasis"><em>accept</em></span></li><li>B <span class="emphasis"><em>character-data</em></span> "Bob"</li><li>B <span class="emphasis"><em>end-element</em></span> "name"</li><li>A <span class="emphasis"><em>end-element</em></span> "cat"</li></ol></div><p>The search for a handler which will accept a <span class="emphasis"><em>start-element</em></span> event
+  begins at the handler of the parent element and continues toward the
+  top of the stack.  For the root element, it begins at the base of
+  the stack.  In the above example, handler A is at the base, and
+  handler B at the top; if the <tt class="literal">name</tt> element had any
+  children, only B's <span class="emphasis"><em>start-element</em></span> would be invoked to accept
+  them.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="xml-state"></a>Maintaining state</h3></div></div><div></div></div><p>To facilitate communication between independent handlers, a
+  <i class="firstterm">state integer</i> is associated with each element
+  being parsed.  This integer is returned by <span class="emphasis"><em>start-element</em></span> callback and
+  is passed to the subsequent <span class="emphasis"><em>character-data</em></span> and <span class="emphasis"><em>end-element</em></span> callbacks
+  associated with the element.  The state integer of the parent
+  element is also passed to each <span class="emphasis"><em>start-element</em></span> callback, the value zero
+  used for the root element (which by definition has no
+  parent).</p><p>To further extend <a href="xml.html#xml-example" title="Example 2.1. An example XML document">Example 2.1, &#8220;An example XML document&#8221;</a>: if handler A
+  defines that the state of the root element <tt class="sgmltag-element">cat</tt>
+  will be <tt class="literal">42</tt>, the event trace would be as
+  follows:
+
+  </p><div class="orderedlist"><ol type="1"><li>A <span class="emphasis"><em>start-element</em></span> (parent = 0, "cat") &#8594;
+      <span class="emphasis"><em>accept</em></span>, state = 42
+      </li><li>A <span class="emphasis"><em>start-element</em></span> (parent = 42, "age") &#8594; 
+      <span class="emphasis"><em>accept</em></span>, state = 50
+      </li><li>A <span class="emphasis"><em>character-data</em></span> (state = 50, "3")</li><li>A <span class="emphasis"><em>end-element</em></span> (state = 50, "age")</li><li>A <span class="emphasis"><em>start-element</em></span> (parent = 42, "name") &#8594; 
+      <span class="emphasis"><em>decline</em></span></li><li>B <span class="emphasis"><em>start-element</em></span> (parent = 42, "name") &#8594;
+      <span class="emphasis"><em>accept</em></span>, state = 99</li><li>B <span class="emphasis"><em>character-data</em></span> (state = 99, "Bob")</li><li>B <span class="emphasis"><em>end-element</em></span> (state = 99, "name")</li><li>A <span class="emphasis"><em>end-element</em></span> (state = 42, "cat")</li></ol></div><p>To avoid collisions between state integers used by different
+  handlers, the interface definition of any handler includes the range
+  of integers it will use.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="xml-ns"></a>XML namespaces</h3></div></div><div></div></div><p>To support XML namespaces, every element name is represented
+  as a <span class="emphasis"><em>(namespace, name)</em></span> pair.  The <span class="emphasis"><em>start-element</em></span>
+  and <span class="emphasis"><em>end-element</em></span> callbacks are passed namespace and name strings
+  accordingly.  If an element in the XML document has no declared
+  namespace, the namespace given will be the empty string,
+  <tt class="literal">""</tt>.</p></div><div class="footnotes"><br><hr width="100" align="left"><div class="footnote"><p><sup>[<a name="ftn.id3018824" href="#id3018824">1</a>] </sup>This
+  &#8220;<span class="quote">problem</span>&#8221; only needs solving because the SAX interface
+  is so inflexible when implemented as C function callbacks; a better
+  approach would be to use an XML parser interface which is not based
+  on callbacks.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="api.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="api.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ref.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 2. The neon C language interface </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> neon API reference</td></tr></table></div></body></html>
diff --git a/neon/doc/man/ne_add_request_header.3 b/neon/doc/man/ne_add_request_header.3
new file mode 100644 (file)
index 0000000..31fab73
--- /dev/null
@@ -0,0 +1,53 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_ADD_REQUEST_HEA" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_add_request_header, ne_print_request_header \- add headers to a request
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_request\&.h>
+.sp
+.HP 28
+void\ \fBne_add_request_header\fR\ (ne_request\ *\fIrequest\fR, const\ char\ *\fIname\fR, const\ char\ *\fIvalue\fR);
+.HP 30
+void\ \fBne_print_request_header\fR\ (ne_request\ *\fIrequest\fR, const\ char\ *\fIname\fR, const\ char\ *\fIformat\fR, \&.\&.\&.);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The functions \fBne_add_request_header\fR and \fBne_print_request_header\fR can be used to add headers to a request, before it is sent\&.
+
+.PP
+\fBne_add_request_header\fR simply adds a header of given \fIname\fR, with given \fIvalue\fR\&.
+
+.PP
+\fBne_print_request_header\fR adds a header of given \fIname\fR, taking the value from the \fBprintf\fR\-like \fIformat\fR string parameter and subsequent variable\-length argument list\&.
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_request_create\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_addr_destroy.3 b/neon/doc/man/ne_addr_destroy.3
new file mode 100644 (file)
index 0000000..ecf8e86
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_addr_resolve.3
diff --git a/neon/doc/man/ne_addr_error.3 b/neon/doc/man/ne_addr_error.3
new file mode 100644 (file)
index 0000000..ecf8e86
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_addr_resolve.3
diff --git a/neon/doc/man/ne_addr_first.3 b/neon/doc/man/ne_addr_first.3
new file mode 100644 (file)
index 0000000..ecf8e86
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_addr_resolve.3
diff --git a/neon/doc/man/ne_addr_next.3 b/neon/doc/man/ne_addr_next.3
new file mode 100644 (file)
index 0000000..ecf8e86
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_addr_resolve.3
diff --git a/neon/doc/man/ne_addr_resolve.3 b/neon/doc/man/ne_addr_resolve.3
new file mode 100644 (file)
index 0000000..c0b4c33
--- /dev/null
@@ -0,0 +1,97 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_ADDR_RESOLVE" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_addr_resolve, ne_addr_result, ne_addr_first, ne_addr_next, ne_addr_error, ne_addr_destroy \- functions to resolve hostnames to addresses
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_socket\&.h>
+.sp
+.HP 31
+ne_sock_addr\ *\fBne_addr_resolve\fR\ (const\ char\ *\fIhostname\fR, int\ \fIflags\fR);
+.HP 20
+int\ \fBne_addr_result\fR\ (const\ ne_sock_addr\ *\fIaddr\fR);
+.HP 36
+const\ ne_inet_addr\ *\fBne_addr_first\fR\ (ne_sock_addr\ *\fIaddr\fR);
+.HP 35
+const\ ne_inet_addr\ *\fBne_addr_next\fR\ (ne_sock_addr\ *\fIaddr\fR);
+.HP 21
+char\ *\fBne_addr_error\fR\ (const\ ne_sock_addr\ *\fIaddr\fR, char\ *\fIbuffer\fR, size_t\ \fIbufsiz\fR);
+.HP 22
+void\ \fBne_addr_destroy\fR\ (ne_sock_addr\ *\fIaddr\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_addr_resolve\fR function resolves the given \fIhostname\fR, returning an \fBne_sock_addr\fR object representing the address (or addresses) associated with the hostname\&. The \fIflags\fR parameter is currently unused, and must be passed as 0\&.
+
+.PP
+The \fIhostname\fR passed to \fBne_addr_resolve\fR can be a DNS hostname (e\&.g\&. "www\&.example\&.com") or an IPv4 dotted quad (e\&.g\&. "192\&.0\&.34\&.72"); or, on systems which support IPv6, an IPv6 hex address, which may be enclosed in brackets, e\&.g\&. "[::1]"\&.
+
+.PP
+To determine whether the hostname was successfully resolved, the \fBne_addr_result\fR function is used, which returns non\-zero if an error occurred\&. If an error did occur, the \fBne_addr_error\fR function can be used, which will copy the error string into a given \fIbuffer\fR (of size \fIbufsiz\fR)\&.
+
+.PP
+The functions \fBne_addr_first\fR and \fBne_addr_next\fR are used to retrieve the Internet addresses associated with an address object which has been successfully resolved\&. \fBne_addr_first\fR returns the first address; \fBne_addr_next\fR returns the next address after the most recent call to \fBne_addr_next\fR or \fBne_addr_first\fR, or NULL if there are no more addresses\&. The \fBne_inet_addr\fR pointer returned by these functions can be passed to \fBne_sock_connect\fR to connect a socket\&.
+
+.PP
+After the address object has been used, it should be destroyed using \fBne_addr_destroy\fR\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_addr_resolve\fR returns a pointer to an address object, and never NULL\&. \fBne_addr_error\fR returns the \fIbuffer\fR parameter \&.
+
+.SH "EXAMPLES"
+
+.PP
+The code below prints out the set of addresses associated with the hostname www\&.google\&.com\&.
+
+.nf
+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 {
+    const ne_inet_addr *ia;
+    printf("www\&.google\&.com:");
+    for (ia = ne_addr_first(addr); ia != NULL; ia = ne_addr_next(addr)) {
+        printf(" %s", ne_iaddr_print(ia, buf, sizeof buf));
+    }
+    putchar('\\n');
+}
+ne_addr_destroy(addr);
+
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_iaddr_print\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_addr_result.3 b/neon/doc/man/ne_addr_result.3
new file mode 100644 (file)
index 0000000..ecf8e86
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_addr_resolve.3
diff --git a/neon/doc/man/ne_buffer.3 b/neon/doc/man/ne_buffer.3
new file mode 100644 (file)
index 0000000..b0461c5
--- /dev/null
@@ -0,0 +1,50 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_BUFFER" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_buffer \- string buffer handling
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_string\&.h>
+
+typedef struct {
+    char *data;
+    size_t used;
+    size_t length;
+} \fBne_buffer\fR;
+.sp
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_buffer\fR type represents an expandable memory buffer for holding NUL\-terminated strings\&. The data field points to the beginnning of the string, the length of which is given by the used field\&. The current size of memory allocated is given by the length field\&. It is not recommended that the fields of a buffer are manipulated directly\&. The data pointer may change when the buffer is modified\&.
+
+.PP
+A buffer is created using \fBne_buffer_create\fR(3) or \fBne_buffer_ncreate\fR(3), and destroyed using \fBne_buffer_destroy\fR(3) or \fBne_buffer_finish\fR(3)\&. The functions \fBne_buffer_append\fR(3), \fBne_buffer_zappend\fR(3) and \fBne_buffer_concat\fR(3) are used to append data to a buffer\&.
+
+.PP
+If the string referenced by the data pointer is modified directly (rather than using one of the functions listed above), \fBne_buffer_altered\fR must be called\&.
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_buffer_altered.3 b/neon/doc/man/ne_buffer_altered.3
new file mode 100644 (file)
index 0000000..b0bbe5e
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_buffer_clear.3
diff --git a/neon/doc/man/ne_buffer_append.3 b/neon/doc/man/ne_buffer_append.3
new file mode 100644 (file)
index 0000000..41f5fbc
--- /dev/null
@@ -0,0 +1,65 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_BUFFER_APPEND" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_buffer_append, ne_buffer_zappend, ne_buffer_concat \- append data to a string buffer
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_string\&.h>
+.sp
+.HP 23
+void\ \fBne_buffer_append\fR\ (ne_buffer\ *\fIbuf\fR, const\ char\ *\fIstring\fR, size_t\ \fIlen\fR);
+.HP 24
+void\ \fBne_buffer_zappend\fR\ (ne_buffer\ *\fIbuf\fR, const\ char\ *\fIstring\fR);
+.HP 23
+void\ \fBne_buffer_concat\fR\ (ne_buffer\ *\fIbuf\fR, const\ char\ *\fIstr\fR, \&.\&.\&.);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_buffer_append\fR and \fBne_buffer_zappend\fR functions append a string to the end of a buffer; extending the buffer as necessary\&. The \fIlen\fR passed to \fBne_buffer_append\fR specifies the length of the string to append; there must be no NUL terminator in the first \fIlen\fR bytes of the string\&. \fBne_buffer_zappend\fR must be passed a NUL\-terminated string\&.
+
+.PP
+The \fBne_buffer_concat\fR function takes a variable\-length argument list following \fIstr\fR; each argument must be a \fBchar *\fR 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 \fIstr\fR) are appended to the buffer in the order given\&. None of the strings passed to \fBne_buffer_concat\fR are modified\&.
+
+.SH "EXAMPLES"
+
+.PP
+The following code will output "Hello, world\&. And goodbye\&."\&.
+
+.nf
+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);
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_buffer\fR(3), \fBne_buffer_create\fR(3), \fBne_buffer_destroy\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_buffer_clear.3 b/neon/doc/man/ne_buffer_clear.3
new file mode 100644 (file)
index 0000000..fa612a2
--- /dev/null
@@ -0,0 +1,50 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_BUFFER_CLEAR" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_buffer_clear, ne_buffer_grow, ne_buffer_altered \- general purpose of group of functions
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_string\&.h>
+.sp
+.HP 22
+void\ \fBne_buffer_clear\fR\ (ne_buffer\ *\fIbuf\fR);
+.HP 24
+void\ \fBne_buffer_altered\fR\ (ne_buffer\ *\fIbuf\fR);
+.HP 21
+void\ \fBne_buffer_grow\fR\ (ne_buffer\ *\fIbuf\fR, size_t\ \fIsize\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_buffer_clear\fR function sets the string stored in \fIbuf\fR to be the empty string ("")\&.
+
+.PP
+The \fBne_buffer_altered\fR function must be used after the string stored in the buffer \fIbuf\fR is modified by directly rather than using \fBne_buffer_append\fR(3), \fBne_buffer_zappend\fR(3) or \fBne_buffer_concat\fR(3)\&.
+
+.PP
+The \fBne_buffer_grow\fR function ensures that at least \fIsize\fR 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\&.
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_buffer_concat.3 b/neon/doc/man/ne_buffer_concat.3
new file mode 100644 (file)
index 0000000..d0f5b8f
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_buffer_append.3
diff --git a/neon/doc/man/ne_buffer_create.3 b/neon/doc/man/ne_buffer_create.3
new file mode 100644 (file)
index 0000000..3079470
--- /dev/null
@@ -0,0 +1,52 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_BUFFER_CREATE" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_buffer_create, ne_buffer_ncreate \- general purpose of group of functions
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_alloc\&.h>
+.sp
+.HP 29
+ne_buffer\ *\fBne_buffer_create\fR\ (void);
+.HP 30
+ne_buffer\ *\fBne_buffer_ncreate\fR\ (size_t\ \fIsize\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+\fBne_buffer_create\fR creates a new buffer object, with an implementation\-defined initial size\&. \fBne_buffer_ncreate\fR creates an \fBne_buffer\fR where the minimum initial size is given in the \fIsize\fR parameter\&. The buffer created will contain the empty string ("")\&.
+
+.SH "RETURN VALUE"
+
+.PP
+Both functions return a pointer to a new buffer object, and never NULL\&.
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_buffer\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_buffer_destroy.3 b/neon/doc/man/ne_buffer_destroy.3
new file mode 100644 (file)
index 0000000..7efd143
--- /dev/null
@@ -0,0 +1,71 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_BUFFER_DESTROY" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_buffer_destroy, ne_buffer_finish \- destroy a buffer object
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_string\&.h>
+.sp
+.HP 24
+void\ \fBne_buffer_destroy\fR\ (ne_buffer\ *\fIbuf\fR);
+.HP 24
+char\ *\fBne_buffer_finish\fR\ (ne_buffer\ *\fIbuf\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+\fBne_buffer_destroy\fR frees all memory associated with the buffer\&. \fBne_buffer_finish\fR frees the buffer structure, but not the actual string stored in the buffer, which is returned and must be \fBfree\fR()d by the caller\&.
+
+.PP
+Any use of the buffer object after calling either of these functions gives undefined behaviour\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_buffer_finish\fR returns the \fBmalloc\fR\-allocated string stored in the buffer\&.
+
+.SH "EXAMPLES"
+
+.PP
+An example use of \fBne_buffer_finish\fR; the \fBduplicate\fR function returns a string made up of \fIn\fR copies of \fIstr\fR:
+
+.nf
+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);
+}
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_buffer\fR(3), \fBne_buffer_create\fR(3), \fBne_buffer_zappend\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_buffer_finish.3 b/neon/doc/man/ne_buffer_finish.3
new file mode 100644 (file)
index 0000000..5aac296
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_buffer_destroy.3
diff --git a/neon/doc/man/ne_buffer_grow.3 b/neon/doc/man/ne_buffer_grow.3
new file mode 100644 (file)
index 0000000..b0bbe5e
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_buffer_clear.3
diff --git a/neon/doc/man/ne_buffer_ncreate.3 b/neon/doc/man/ne_buffer_ncreate.3
new file mode 100644 (file)
index 0000000..628991b
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_buffer_create.3
diff --git a/neon/doc/man/ne_buffer_zappend.3 b/neon/doc/man/ne_buffer_zappend.3
new file mode 100644 (file)
index 0000000..d0f5b8f
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_buffer_append.3
diff --git a/neon/doc/man/ne_calloc.3 b/neon/doc/man/ne_calloc.3
new file mode 100644 (file)
index 0000000..8564fc1
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_malloc.3
diff --git a/neon/doc/man/ne_close_connection.3 b/neon/doc/man/ne_close_connection.3
new file mode 100644 (file)
index 0000000..2a71e06
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_session_create.3
diff --git a/neon/doc/man/ne_forget_auth.3 b/neon/doc/man/ne_forget_auth.3
new file mode 100644 (file)
index 0000000..a6f0398
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_set_server_auth.3
diff --git a/neon/doc/man/ne_get_error.3 b/neon/doc/man/ne_get_error.3
new file mode 100644 (file)
index 0000000..677a5c8
--- /dev/null
@@ -0,0 +1,68 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_GET_ERROR" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_get_error, ne_set_error \- error handling for HTTP sessions
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_session\&.h>
+.sp
+.HP 27
+const\ char\ *\fBne_get_error\fR\ (ne_sesssion\ *\fIsession\fR);
+.HP 19
+void\ \fBne_set_error\fR\ (ne_sesssion\ *\fIsession\fR, const\ char\ *\fIformat\fR, \&.\&.\&.);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The session error string is used to store any human\-readable error information associated with any errors which occur whilst using the HTTP session\&.
+
+.PP
+The \fBne_get_error\fR function returns the current session error string\&. This string persists only until it is changed by a subsequent operation on the session\&.
+
+.PP
+The \fBne_set_error\fR function can be used to set a new session error string, using a \fBprintf\fR\-style format string interface\&.
+
+.SH "EXAMPLES"
+
+.PP
+Retrieve the current error string:
+
+.nf
+ne_session *sess = ne_session_create(\&.\&.\&.);
+\&.\&.\&.
+printf("Error was: %s\\n", ne_get_error(sess));
+.fi
+
+.PP
+Set a new error string:
+
+.nf
+ne_session *sess = ne_session_create(\&.\&.\&.);
+\&.\&.\&.
+ne_set_error(sess, "Response missing header %s", "somestring");
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_get_status.3 b/neon/doc/man/ne_get_status.3
new file mode 100644 (file)
index 0000000..fd0233b
--- /dev/null
@@ -0,0 +1,59 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_GET_STATUS" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_get_status \- retrieve HTTP response status for request
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_request\&.h>
+.sp
+.HP 33
+const\ ne_status\ *\fBne_get_status\fR\ (const\ ne_request\ *\fIrequest\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_get_status\fR function returns a pointer to the HTTP status object giving the result of a request\&. The object returned only becomes valid once the request has been successfully dispatched (the return value of \fBne_request_dispatch\fR or \fBne_begin_request\fR was zero)\&. The object remains valid until the associated request object is destroyed\&.
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_status\fR(3), \fBne_request_create\fR(3)
+
+.SH "EXAMPLE"
+
+.PP
+Display the response status code of applying the HEAD method to some resource\&.
+
+.nf
+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);
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_iaddr_cmp.3 b/neon/doc/man/ne_iaddr_cmp.3
new file mode 100644 (file)
index 0000000..0964908
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_iaddr_make.3
diff --git a/neon/doc/man/ne_iaddr_free.3 b/neon/doc/man/ne_iaddr_free.3
new file mode 100644 (file)
index 0000000..0964908
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_iaddr_make.3
diff --git a/neon/doc/man/ne_iaddr_make.3 b/neon/doc/man/ne_iaddr_make.3
new file mode 100644 (file)
index 0000000..5565d8b
--- /dev/null
@@ -0,0 +1,92 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_IADDR_MAKE" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_iaddr_make, ne_iaddr_cmp, ne_iaddr_print, ne_iaddr_free \- functions to manipulate and compare network addresses
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_socket\&.h>
+
+typedef enum {
+    ne_iaddr_ipv4 = 0,
+    ne_iaddr_ipv6
+} \fBne_iaddr_type\fR;
+.sp
+.HP 29
+ne_inet_addr\ *\fBne_iaddr_make\fR\ (ne_iaddr_type\ \fItype\fR, const\ unsigned\ char\ *\fIraw\fR);
+.HP 18
+int\ \fBne_iaddr_cmp\fR\ (const\ ne_inet_addr\ *\fIi1\fR, const\ ne_inet_addr\ *\fIi2\fR);
+.HP 22
+char\ *\fBne_iaddr_print\fR\ (const\ ne_inet_addr\ *\fIia\fR, char\ *\fIbuffer\fR, size_t\ \fIbufsiz\fR);
+.HP 20
+void\ \fBne_iaddr_free\fR\ (const\ ne_inet_addr\ *\fIaddr\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+\fBne_iaddr_make\fR creates an \fBne_inet_addr\fR object from a raw binary network address; for instance the four bytes 0x7f 0x00 0x00 0x01 represent the IPv4 address 127\&.0\&.0\&.1\&. The object returned is suitable for passing to \fBne_sock_connect\fR\&. A binary IPv4 address contains four bytes; a binary IPv6 address contains sixteen bytes; addresses passed must be in network byte order\&.
+
+.PP
+\fBne_iaddr_cmp\fR can be used to compare two network addresses; returning zero only if they are identical\&. The addresses need not be of the same address type; if the addresses are not of the same type, the return value is guaranteed to be non\-zero\&.
+
+.PP
+\fBne_iaddr_print\fR can be used to print the human\-readable string representation of a network address into a buffer, for instance the string "127\&.0\&.0\&.1"\&.
+
+.PP
+\fBne_iaddr_free\fR releases the memory associated with a network address object\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_iaddr_make\fR returns NULL if the address type passed is not supported (for instance on a platform which does not support IPv6)\&.
+
+.PP
+\fBne_iaddr_print\fR returns the \fIbuffer\fR pointer, and never NULL\&.
+
+.SH "EXAMPLES"
+
+.PP
+The following example connects a socket to port 80 at the address 127\&.0\&.0\&.1\&.
+
+.nf
+unsigned char addr[] = "\\0x7f\\0x00\\0x00\\0x01";
+ne_inet_addr *ia;
+
+ia = ne_iaddr_make(ne_iaddr_ipv4, addr);
+if (ia != NULL) {
+    ne_socket *sock = ne_sock_connect(ia, 80);
+    ne_iaddr_free(ia);
+    /* \&.\&.\&. */
+} else {
+    /* \&.\&.\&. */
+}
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_addr_resolve\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_iaddr_print.3 b/neon/doc/man/ne_iaddr_print.3
new file mode 100644 (file)
index 0000000..0964908
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_iaddr_make.3
diff --git a/neon/doc/man/ne_malloc.3 b/neon/doc/man/ne_malloc.3
new file mode 100644 (file)
index 0000000..7402786
--- /dev/null
@@ -0,0 +1,58 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_MALLOC" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_malloc, ne_calloc, ne_realloc, ne_strdup, ne_strndup, ne_oom_callback \- memory allocation wrappers
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_alloc\&.h>
+.sp
+.HP 17
+void\ *\fBne_malloc\fR\ (size_t\ \fIsize\fR);
+.HP 17
+void\ *\fBne_calloc\fR\ (size_t\ \fIsize\fR);
+.HP 18
+void\ *\fBne_realloc\fR\ (void\ *\fIsize\fR, size_t\ \fIlen\fR);
+.HP 17
+char\ *\fBne_strdup\fR\ (const\ char\ *\fIs\fR, size_t\ \fIsize\fR);
+.HP 18
+char\ *\fBne_strndup\fR\ (const\ char\ *\fIs\fR, size_t\ \fIsize\fR);
+.HP 22
+void\ \fBne_oom_callback\fR\ (void\ (*\fIcallback\fR)(void));
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The functions \fBne_malloc\fR, \fBne_calloc\fR, \fBne_realloc\fR, \fBne_strdup\fR and \fBne_strdnup\fR 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 \fBabort\fR()\&.
+
+.PP
+\fBne_oom_callback\fR registers a callback which will be invoked if an out of memory error is detected\&.
+
+.SH "NOTES"
+
+.PP
+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\&.
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_oom_callback.3 b/neon/doc/man/ne_oom_callback.3
new file mode 100644 (file)
index 0000000..8564fc1
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_malloc.3
diff --git a/neon/doc/man/ne_print_request_header.3 b/neon/doc/man/ne_print_request_header.3
new file mode 100644 (file)
index 0000000..a9ed2f7
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_add_request_header.3
diff --git a/neon/doc/man/ne_qtoken.3 b/neon/doc/man/ne_qtoken.3
new file mode 100644 (file)
index 0000000..b24c574
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_token.3
diff --git a/neon/doc/man/ne_realloc.3 b/neon/doc/man/ne_realloc.3
new file mode 100644 (file)
index 0000000..8564fc1
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_malloc.3
diff --git a/neon/doc/man/ne_request_create.3 b/neon/doc/man/ne_request_create.3
new file mode 100644 (file)
index 0000000..ba152ce
--- /dev/null
@@ -0,0 +1,112 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_REQUEST_CREATE" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_request_create, ne_request_dispatch, ne_request_destroy \- low-level HTTP request handling
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_request\&.h>
+.sp
+.HP 31
+ne_request\ *\fBne_request_create\fR\ (ne_session\ *\fIsession\fR, const\ char\ *\fImethod\fR, const\ char\ *\fIpath\fR);
+.HP 25
+int\ \fBne_request_dispatch\fR\ (ne_request\ *\fIreq\fR);
+.HP 25
+void\ \fBne_request_destroy\fR\ (ne_request\ *\fIreq\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+An HTTP request, represented by the \fBne_request\fR type, specifies that some operation is to be performed on some resource\&. The \fBne_request_create\fR function creates a request object, specifying the operation in the \fImethod\fR parameter\&. The location of the resource is determined by the server in use for the session given by the \fIsess\fR parameter, combined with the \fIpath\fR parameter\&.
+
+.PP
+The \fIpath\fR string used must conform to the abs_path definition given in RFC2396, with an optional "?query" part, and must be URI\-escaped by the caller (for instance, using \fBne_path_escape\fR)\&. If the string comes from an untrusted source, failure to perform URI\-escaping results in a security vulnerability\&.
+
+.PP
+To dispatch a request, and process the response, the \fBne_request_dispatch\fR function can be used\&. An alternative is to use the (more complex, but more flexible) combination of the \fBne_begin_request\fR, \fBne_end_request\fR, and \fBne_read_response_block\fR functions; see \fBne_begin_request\fR\&.
+
+.PP
+To add extra headers in the request, the functions \fBne_add_request_header\fR(3) and \fBne_print_request_header\fR(3) can be used\&. To include a message body with the request, one of the functions \fBne_set_request_body_buffer\fR, \fBne_set_request_body_fd\fR(3), or \fBne_set_request_body_provider\fR can be used\&.
+
+.PP
+The return value of \fBne_request_dispatch\fR indicates merely whether the request was sent and the response read successfully\&. To discover the result of the operation, \fBne_get_status\fR(3), along with any processing of the response headers and message body\&.
+
+.PP
+A request can only be dispatched once: calling \fBne_request_dispatch\fR more than once on a single \fBne_request\fR object produces undefined behaviour\&. Once all processing associated with the request object is complete, use the \fBne_request_destroy\fR function to destroy the resources associated with it\&. Any subsequent use of the request object produces undefined behaviour\&.
+
+.SH "RETURN VALUE"
+
+.PP
+The \fBne_request_create\fR function returns a pointer to a request object (and never NULL)\&.
+
+.PP
+The \fBne_request_dispatch\fR function returns zero if the request was dispatched successfully, and a non\-zero error code otherwise\&.
+
+.SH "ERRORS"
+
+.TP
+\fBNE_ERROR\fR
+Request failed (see session error string)
+
+.TP
+\fBNE_LOOKUP\fR
+The DNS lookup for the server (or proxy server) failed\&.
+
+.TP
+\fBNE_AUTH\fR
+Authentication failed on the server\&.
+
+.TP
+\fBNE_PROXYAUTH\fR
+Authentication failed on the proxy server\&.
+
+.TP
+\fBNE_CONNECT\fR
+A connection to the server could not be established\&.
+
+.TP
+\fBNE_TIMEOUT\fR
+A timeout occurred while waiting for the server to respond\&.
+
+.SH "EXAMPLE"
+
+.PP
+An example of applying a MKCOL operation to the resource at the locationhttp://www\&.example\&.com/foo/bar/:
+
+.nf
+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);
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_get_error\fR(3), \fBne_set_error\fR(3), \fBne_get_status\fR(3), \fBne_add_request_header\fR(3), \fBne_set_request_body_buffer\fR(3)\&.
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_request_destroy.3 b/neon/doc/man/ne_request_destroy.3
new file mode 100644 (file)
index 0000000..9c211a4
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_request_create.3
diff --git a/neon/doc/man/ne_request_dispatch.3 b/neon/doc/man/ne_request_dispatch.3
new file mode 100644 (file)
index 0000000..9c211a4
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_request_create.3
diff --git a/neon/doc/man/ne_session_create.3 b/neon/doc/man/ne_session_create.3
new file mode 100644 (file)
index 0000000..c856076
--- /dev/null
@@ -0,0 +1,89 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SESSION_CREATE" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_session_create, ne_close_connection, ne_session_proxy, ne_session_destroy \- set up HTTP sessions
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_session\&.h>
+.sp
+.HP 31
+ne_session\ *\fBne_session_create\fR\ (const\ char\ *\fIscheme\fR, const\ char\ *\fIhostname\fR, unsigned\ int\ \fIport\fR);
+.HP 23
+void\ \fBne_session_proxy\fR\ (ne_session\ *\fIsession\fR, const\ char\ *\fIhostname\fR, unsigned\ int\ \fIport\fR);
+.HP 26
+void\ \fBne_close_connection\fR\ (ne_session\ *\fIsession\fR);
+.HP 25
+void\ \fBne_session_destroy\fR\ (ne_session\ *\fIsession\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+An \fBne_session\fR object represents an HTTP session \- a logical grouping of a sequence of HTTP requests made to a certain server\&. Any requests made using the session can use a persistent connection, share cached authentication credentials and any other common attributes\&.
+
+.PP
+A new HTTP session is created using \fBne_session_create\fR, giving the \fIhostname\fR and \fIport\fR of the server to use, along with the \fIscheme\fR used to contact the server (usually "http")\&. Before the first use of \fBne_session_create\fR in a process, \fBne_sock_init\fR(3) must have been called to perform any global initialization needed by any libraries used by neon\&.
+
+.PP
+To enable SSL/TLS for the session, pass the string "https" as the \fIscheme\fR parameter, and either register a certificate verification function (see \fBne_ssl_set_verify\fR(3)) or trust the appropriate certificate (see \fBne_ssl_trust_cert\fR(3), \fBne_ssl_trust_default_ca\fR(3))\&.
+
+.PP
+If an HTTP proxy server should be used for the session, \fBne_session_proxy\fR must be called giving the hostname and port on which to contact the proxy\&.
+
+.PP
+If it is known that the session will not be used for a significant period of time, \fBne_close_connection\fR 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\&.
+
+.PP
+Once a session has been completed, \fBne_session_destroy\fR must be called to destroy the resources associated with the session\&. Any subsequent use of the session pointer produces undefined behaviour\&.
+
+.SH "NOTES"
+
+.PP
+The hostname passed to \fBne_session_create\fR is resolved when the first request using the session is dispatched; a DNS resolution failure can only be detected at that time (using the NE_LOOKUP error code); see \fBne_request_dispatch\fR(3) for details\&.
+
+.SH "RETURN VALUES"
+
+.PP
+\fBne_session_create\fR will return a pointer to a new session object (and never NULL)\&.
+
+.SH "EXAMPLES"
+
+.PP
+Create and destroy a session:
+
+.nf
+ne_session *sess;
+sess = ne_session_create("http", "host\&.example\&.com", 80);
+/* \&.\&.\&. use sess \&.\&.\&. */
+ne_session_destroy(sess);
+
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_ssl_set_verify\fR(3), \fBne_ssl_trust_cert\fR(3), \fBne_sock_init\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_session_destroy.3 b/neon/doc/man/ne_session_destroy.3
new file mode 100644 (file)
index 0000000..2a71e06
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_session_create.3
diff --git a/neon/doc/man/ne_session_proxy.3 b/neon/doc/man/ne_session_proxy.3
new file mode 100644 (file)
index 0000000..2a71e06
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_session_create.3
diff --git a/neon/doc/man/ne_set_error.3 b/neon/doc/man/ne_set_error.3
new file mode 100644 (file)
index 0000000..c317579
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_get_error.3
diff --git a/neon/doc/man/ne_set_expect100.3 b/neon/doc/man/ne_set_expect100.3
new file mode 100644 (file)
index 0000000..06ef4ae
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_set_useragent.3
diff --git a/neon/doc/man/ne_set_persist.3 b/neon/doc/man/ne_set_persist.3
new file mode 100644 (file)
index 0000000..06ef4ae
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_set_useragent.3
diff --git a/neon/doc/man/ne_set_proxy_auth.3 b/neon/doc/man/ne_set_proxy_auth.3
new file mode 100644 (file)
index 0000000..a6f0398
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_set_server_auth.3
diff --git a/neon/doc/man/ne_set_read_timeout.3 b/neon/doc/man/ne_set_read_timeout.3
new file mode 100644 (file)
index 0000000..06ef4ae
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_set_useragent.3
diff --git a/neon/doc/man/ne_set_request_body_buffer.3 b/neon/doc/man/ne_set_request_body_buffer.3
new file mode 100644 (file)
index 0000000..4317ef8
--- /dev/null
@@ -0,0 +1,45 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SET_REQUEST_" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_set_request_body_buffer, ne_set_request_body_fd \- include a message body with a request
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_request\&.h>
+.sp
+.HP 33
+void\ \fBne_set_request_body_buffer\fR\ (ne_request\ *\fIreq\fR, const\ char\ *\fIbuf\fR, size_t\ \fIcount\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_set_request_body_buffer\fR function specifies that a message body should be included with the body, which is stored in the \fIcount\fR bytes buffer \fIbuf\fR\&.
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_request_create\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_set_request_body_fd.3 b/neon/doc/man/ne_set_request_body_fd.3
new file mode 100644 (file)
index 0000000..42452be
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_set_request_body_buffer.3
diff --git a/neon/doc/man/ne_set_server_auth.3 b/neon/doc/man/ne_set_server_auth.3
new file mode 100644 (file)
index 0000000..860d6aa
--- /dev/null
@@ -0,0 +1,89 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SET_SERVER_AUTH" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_set_server_auth, ne_set_proxy_auth, ne_forget_auth \- register authentication callbacks
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_auth\&.h>
+.sp
+.HP 33
+typedef\ int\ (*\fBne_request_auth\fR)\ (void\ *\fIuserdata\fR, const\ char\ *\fIrealm\fR, int\ \fIattempt\fR, char\ *\fIusername\fR, char\ *\fIpassword\fR);
+.HP 25
+void\ \fBne_set_server_auth\fR\ (ne_session\ *\fIsession\fR, ne_request_auth\ \fIcallback\fR, void\ *\fIuserdata\fR);
+.HP 24
+void\ \fBne_set_proxy_auth\fR\ (ne_session\ *\fIsession\fR, ne_request_auth\ \fIcallback\fR, void\ *\fIuserdata\fR);
+.HP 21
+void\ \fBne_forget_auth\fR\ (ne_session\ *\fIsession\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_request_auth\fR function type defines a callback which is invoked when a server or proxy server requires user authentication for a particular request\&. The \fIrealm\fR string is supplied by the server\&.  The \fIattempt\fR 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, \fIattempt\fR will be zero\&.
+
+.PP
+To retry the request using new authentication credentials, the callback should return zero, and the \fIusername\fR and \fIpassword\fR buffers must contain NUL\-terminated strings\&. The NE_ABUFSIZ constant gives the size of these buffers\&.
+
+.RS
+.Sh "Tip"
+
+.PP
+If you only wish to allow the user one attempt to enter credentials, use the value of the \fIattempt\fR parameter as the return value of the callback\&.
+
+.RE
+
+.PP
+To abort the request, the callback should return a non\-zero value; in which case the contents of the \fIusername\fR and \fIpassword\fR buffers are ignored\&.
+
+.PP
+The \fBne_forget_auth\fR function can be used to discard the cached authentication credentials\&.
+
+.SH "EXAMPLES"
+
+.nf
+
+/* 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(\&.\&.\&.)
+{
+   ne_session *sess = ne_session_create(\&.\&.\&.);
+
+   ne_set_server_auth(sess, my_auth, NULL);
+
+   /* \&.\&.\&. */
+}
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_set_useragent.3 b/neon/doc/man/ne_set_useragent.3
new file mode 100644 (file)
index 0000000..5695c30
--- /dev/null
@@ -0,0 +1,89 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SET_USERAGENT" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_set_useragent, ne_set_persist, ne_set_read_timeout, ne_set_expect100 \- common settings for HTTP sessions
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_session\&.h>
+.sp
+.HP 23
+void\ \fBne_set_useragent\fR\ (ne_session\ *\fIsession\fR, const\ char\ *\fIproduct\fR);
+.HP 21
+void\ \fBne_set_persist\fR\ (ne_session\ *\fIsession\fR, int\ \fIflag\fR);
+.HP 26
+void\ \fBne_set_read_timeout\fR\ (ne_session\ *\fIsession\fR, int\ \fItimeout\fR);
+.HP 23
+void\ \fBne_set_expect100\fR\ (ne_session\ *\fIsession\fR, int\ \fIflag\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The User\-Agent request header is used to identify the software which generated the request for statistical or debugging purposes\&. neon does not send a User\-Agent header unless a call is made to the \fBne_set_useragent\fR\&. \fBne_set_useragent\fR must be passed a product string conforming to RFC2616's product token grammar; of the form "Product/Version"\&.
+
+.PP
+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 \fBne_set_persist\fR function will disable persistent connections if passed a \fIflag\fR parameter of 0, and will enable them otherwise\&.
+
+.PP
+When neon reads from a socket, by default the read operation will time out after 60 seconds, and the request will fail giving an \fBNE_TIMEOUT\fR error\&. To configure this timeout interval, call \fBne_set_read_timeout\fR giving the desired number of seconds as the \fItimeout\fR parameter\&.
+
+.PP
+An extension introduced in the HTTP/1\&.1 specification was the use of the Expect: 100\-continue 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 \fBne_set_expect100\fR function can be used to enable this feature by passing the \fIflag\fR parameter as any non\-zero integer\&.
+
+.RS
+.Sh "Warning"
+
+.PP
+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 Expect: 100\-continue support\&.
+
+.RE
+
+.SH "EXAMPLES"
+
+.PP
+Set a user\-agent string:
+
+.nf
+ne_session *sess = ne_session_create(\&.\&.\&.);
+ne_set_useragent(sess, "MyApplication/2\&.1");
+.fi
+
+.PP
+Disable use of persistent connections:
+
+.nf
+ne_session *sess = ne_session_create(\&.\&.\&.);
+ne_set_persist(sess, 0);
+.fi
+
+.PP
+Set a 30 second read timeout:
+
+.nf
+ne_session *sess = ne_session_create(\&.\&.\&.);
+ne_set_read_timeout(sess, 30);
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_shave.3 b/neon/doc/man/ne_shave.3
new file mode 100644 (file)
index 0000000..e5c1721
--- /dev/null
@@ -0,0 +1,50 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SHAVE" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_shave \- trim whitespace from a string
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_string\&.h>
+.sp
+.HP 16
+char\ *\fBne_shave\fR\ (char\ *\fIstr\fR, const\ char\ *\fIwhitespace\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+\fBne_shave\fR returns a portion of \fIstr\fR with any leading or trailing characters in the \fIwhitespace\fR array removed\&. \fIstr\fR may be modified\&. Note that the return value may not be equal to \fIstr\fR\&.
+
+.SH "EXAMPLES"
+
+.PP
+The following code segment will output"fish":
+
+.nf
+char s[] = "\&.!\&.fish!\&.!";
+puts(ne_shave(s, "\&.!"));
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_sock_init.3 b/neon/doc/man/ne_sock_init.3
new file mode 100644 (file)
index 0000000..e81a573
--- /dev/null
@@ -0,0 +1,50 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SOCK_INIT" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_sock_init \- perform library initialization
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_socket\&.h>
+.sp
+.HP 18
+int\ \fBne_sock_init\fR\ (void);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+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 \fBne_sock_init\fR function must be called once before any other library functions are used\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_sock_init\fR returns zero on success, or non\-zero on error\&. If an error occurs, no further use of the neon library should be attempted\&.
+
+.SH "SEE ALSO"
+
+.PP
+\fBneon\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_cert_cmp.3 b/neon/doc/man/ne_ssl_cert_cmp.3
new file mode 100644 (file)
index 0000000..8145912
--- /dev/null
@@ -0,0 +1,45 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SSL_CERT_CMP" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_ssl_cert_cmp, ne_ssl_cert_free \- functions to operate on certificate objects
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_header\&.h>
+.sp
+.HP 21
+int\ \fBne_ssl_cert_cmp\fR\ (const\ ne_ssl_certificate\ *\fIc1\fR, const\ ne_ssl_certificate\ *\fIc2\fR);
+.HP 23
+void\ \fBne_ssl_cert_free\fR\ (ne_ssl_certificate\ *\fIcert\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_ssl_cert_cmp\fR function can be used to compare two certificate objects; it returns zero if they refer to the same certificate, and non\-zero otherwise\&.
+
+.PP
+The \fBne_ssl_cert_free\fR function can be used to destroy a certificate object when it is no longer needed\&.
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_cert_export.3 b/neon/doc/man/ne_ssl_cert_export.3
new file mode 100644 (file)
index 0000000..9c92144
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_cert_read.3
diff --git a/neon/doc/man/ne_ssl_cert_free.3 b/neon/doc/man/ne_ssl_cert_free.3
new file mode 100644 (file)
index 0000000..208e0df
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_cert_cmp.3
diff --git a/neon/doc/man/ne_ssl_cert_identity.3 b/neon/doc/man/ne_ssl_cert_identity.3
new file mode 100644 (file)
index 0000000..e3387a8
--- /dev/null
@@ -0,0 +1,85 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SSL_CERT_IDENTI" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_ssl_cert_identity, ne_ssl_cert_signedby, ne_ssl_cert_issuer, ne_ssl_cert_subject \- functions to access certificate properties
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_ssl\&.h>
+.sp
+.HP 35
+const\ char\ *\fBne_ssl_cert_identity\fR\ (const\ ne_ssl_certificate\ *\fIcert\fR);
+.HP 49
+const\ ne_ssl_certificate\ *\fBne_ssl_cert_signedby\fR\ (const\ ne_ssl_certificate\ *\fIcert\fR);
+.HP 42
+const\ ne_ssl_dname\ *\fBne_ssl_cert_subject\fR\ (const\ ne_ssl_certificate\ *\fIcert\fR);
+.HP 41
+const\ ne_ssl_dname\ *\fBne_ssl_cert_issuer\fR\ (const\ ne_ssl_certificate\ *\fIcert\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The function \fBne_ssl_cert_identity\fR retrieves the ``identity'' of a certificate; for an SSL server certificate, this will be the hostname for which the certificate was issued\&. In PKI parlance, the identity is the common name attribute of the distinguished name of the certificate subject\&.
+
+.PP
+The functions \fBne_ssl_cert_subject\fR and \fBne_ssl_cert_issuer\fR can be used to access the objects representing the distinguished name of the subject and of the issuer of a certificate, respectively\&.
+
+.PP
+If a certificate object is part of a certificate chain, then \fBne_ssl_cert_signedby\fR can be used to find the certificate which signed a particular certificate\&. For a self\-signed certificate or a certificate for which the full chain is not available, this function will return NULL\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_ssl_cert_issuer\fR and \fBne_ssl_cert_subject\fR are guaranteed to never return NULL\&. \fBne_ssl_cert_identity\fR may return NULL if the certificate has no specific ``identity''\&. \fBne_ssl_cert_signedby\fR may return NULL as covered above\&.
+
+.SH "EXAMPLES"
+
+.PP
+The following function could be used to display information about a given certificate:
+
+.nf
+void dump_cert(const ne_ssl_certificate *cert) {
+  const char *id = ne_ssl_cert_identity(cert);
+  char *dn;
+
+  if (id) 
+    printf("Certificate was issued for '%s'\&.\\n", id);
+
+  dn = ne_ssl_readable_dname(ne_ssl_cert_subject(cert));
+  printf("Subject: %s\\n", dn);
+  free(dn);
+
+  dn = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
+  printf("Issuer: %s\\n", dn);
+  free(dn);
+}
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_ssl_cert_cmp\fR(3), \fBne_ssl_readable_dname\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_cert_import.3 b/neon/doc/man/ne_ssl_cert_import.3
new file mode 100644 (file)
index 0000000..9c92144
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_cert_read.3
diff --git a/neon/doc/man/ne_ssl_cert_issuer.3 b/neon/doc/man/ne_ssl_cert_issuer.3
new file mode 100644 (file)
index 0000000..e752f38
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_cert_identity.3
diff --git a/neon/doc/man/ne_ssl_cert_read.3 b/neon/doc/man/ne_ssl_cert_read.3
new file mode 100644 (file)
index 0000000..79eaa5a
--- /dev/null
@@ -0,0 +1,59 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SSL_CERT_READ" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_ssl_cert_read, ne_ssl_cert_write, ne_ssl_cert_import, ne_ssl_cert_export \- functions to read or write certificates to and from files or strings
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_ssl\&.h>
+.sp
+.HP 38
+ne_ssl_certificate\ *\fBne_ssl_cert_read\fR\ (const\ char\ *\fIfilename\fR);
+.HP 23
+int\ \fBne_ssl_cert_write\fR\ (const\ ne_ssl_certificate\ *\fIcert\fR, const\ char\ *\fIfilename\fR);
+.HP 40
+ne_ssl_certificate\ *\fBne_ssl_cert_import\fR\ (const\ char\ *\fIdata\fR);
+.HP 26
+char\ *\fBne_ssl_cert_export\fR\ (const\ ne_ssl_certificate\ *\fIcert\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_ssl_cert_write\fR function writes a certificate to a file using the PEM encoding\&. The \fBne_ssl_cert_export\fR function returns a base64\-encoded NUL\-terminated string representing the certificate\&. This string is malloc\-allocated and should be destroyed using \fBfree\fR by the caller\&.
+
+.PP
+The \fBne_ssl_cert_read\fR function reads a certificate from a PEM\-encoded file, and returns a certificate object\&. The \fBne_ssl_cert_import\fR function returns a certificate object from a base64\-encoded string, \fIdata\fR, as returned by \fBne_ssl_cert_export\fR\&. The certificate object returned by these functions should be destroyed using \fBne_ssl_cert_free\fR(3) after use\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_ssl_cert_read\fR returns NULL if a certificate could not be read from the file\&. \fBne_ssl_cert_write\fR returns non\-zero if the certificate could not be written to the file\&. \fBne_ssl_cert_export\fR always returns a NUL\-terminated string, and never NULL\&. \fBne_ssl_cert_import\fR returns NULL if the string was not a valid base64\-encoded certificate\&.
+
+.SH "ENCODING FORMATS"
+
+.PP
+The string produced by \fBne_ssl_cert_export\fR is the base64 encoding of the DER representation of the certificate\&. The file written by \fBne_ssl_cert_write\fR uses the PEM format: this is the base64 encoding of the DER representation with newlines every 64 characters, and start and end marker lines\&.
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_cert_signedby.3 b/neon/doc/man/ne_ssl_cert_signedby.3
new file mode 100644 (file)
index 0000000..e752f38
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_cert_identity.3
diff --git a/neon/doc/man/ne_ssl_cert_subject.3 b/neon/doc/man/ne_ssl_cert_subject.3
new file mode 100644 (file)
index 0000000..e752f38
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_cert_identity.3
diff --git a/neon/doc/man/ne_ssl_cert_write.3 b/neon/doc/man/ne_ssl_cert_write.3
new file mode 100644 (file)
index 0000000..9c92144
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_cert_read.3
diff --git a/neon/doc/man/ne_ssl_clicert_decrypt.3 b/neon/doc/man/ne_ssl_clicert_decrypt.3
new file mode 100644 (file)
index 0000000..f9f7d9c
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_clicert_read.3
diff --git a/neon/doc/man/ne_ssl_clicert_encrypted.3 b/neon/doc/man/ne_ssl_clicert_encrypted.3
new file mode 100644 (file)
index 0000000..f9f7d9c
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_clicert_read.3
diff --git a/neon/doc/man/ne_ssl_clicert_free.3 b/neon/doc/man/ne_ssl_clicert_free.3
new file mode 100644 (file)
index 0000000..f9f7d9c
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_clicert_read.3
diff --git a/neon/doc/man/ne_ssl_clicert_name.3 b/neon/doc/man/ne_ssl_clicert_name.3
new file mode 100644 (file)
index 0000000..f9f7d9c
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_clicert_read.3
diff --git a/neon/doc/man/ne_ssl_clicert_owner.3 b/neon/doc/man/ne_ssl_clicert_owner.3
new file mode 100644 (file)
index 0000000..f9f7d9c
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_clicert_read.3
diff --git a/neon/doc/man/ne_ssl_clicert_read.3 b/neon/doc/man/ne_ssl_clicert_read.3
new file mode 100644 (file)
index 0000000..04d51d4
--- /dev/null
@@ -0,0 +1,99 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SSL_CLIENT_CERT" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_ssl_clicert_read, ne_ssl_clicert_name, ne_ssl_clicert_encrypted, ne_ssl_clicert_decrypt, ne_ssl_clicert_owner, ne_ssl_clicert_free \- SSL client certificate handling
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_ssl\&.h>
+.sp
+.HP 41
+ne_ssl_client_cert\ *\fBne_ssl_clicert_read\fR\ (const\ char\ *\fIfilename\fR);
+.HP 34
+const\ char\ *\fBne_ssl_clicert_name\fR\ (const\ ne_ssl_client_cert\ *\fIccert\fR);
+.HP 30
+int\ \fBne_ssl_clicert_encrypted\fR\ (const\ ne_ssl_client_cert\ *\fIccert\fR);
+.HP 28
+int\ \fBne_ssl_clicert_decrypt\fR\ (ne_ssl_client_cert\ *\fIccert\fR, const\ char\ *\fIpassword\fR);
+.HP 49
+const\ ne_ssl_certificate\ *\fBne_ssl_clicert_owner\fR\ (const\ ne_ssl_client_cert\ *\fIccert\fR);
+.HP 26
+void\ \fBne_ssl_clicert_free\fR\ (ne_ssl_client_cert\ *\fIccert\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_ssl_clicert_read\fR function reads a client certificate from a PKCS#12\-formatted file, and returns an \fBne_ssl_client_certificate\fR object\&. If the client certificate is encrypted, it must be decrypted before it is used\&. An \fBne_ssl_client_certificate\fR object holds a client certificate and the associated private key, not just a certificate; the term "client certificate" will used to refer to this pair\&.
+
+.PP
+A client certificate can be in one of two states: encrypted or decrypted\&. The \fBne_ssl_clicert_encrypted\fR function will return non\-zero if the client certificate is in the encrypted state\&. A client certificate object returned by \fBne_ssl_clicert_read\fR may be initially in either state, depending on whether the file was encrypted or not\&.
+
+.PP
+\fBne_ssl_clicert_decrypt\fR can be used to decrypt a client certificate using the appropriate password\&. This function must only be called if the object is in the encrypted state; if decryption fails, the certificate state does not change, so decryption can be attempted more than once using different passwords\&.
+
+.PP
+A client certificate can be given a "friendly name" when it is created; \fBne_ssl_clicert_name\fR will return this name (or NULL if no friendly name was specified)\&. \fBne_ssl_clicert_name\fR can be used when the client certificate is in either the encrypted or decrypted state, and will return the same string for the lifetime of the object\&.
+
+.PP
+The function \fBne_ssl_clicert_owner\fR returns the certificate part of the client certificate; it must only be called if the client certificate is in the decrypted state\&.
+
+.PP
+When the client certificate is no longer needed, the \fBne_ssl_clicert_free\fR function should be used to destroy the object\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_ssl_clicert_read\fR returns a client certificate object, or NULL if the file could not be read\&. \fBne_ssl_clicert_encrypted\fR returns zero if the object is in the decrypted state, or non\-zero if it is in the encrypted state\&. \fBne_ssl_clicert_name\fR returns a NUL\-terminated friendly name string, or NULL\&. \fBne_ssl_clicert_owner\fR returns a certificate object\&.
+
+.SH "EXAMPLES"
+
+.PP
+The following code reads a client certificate and decrypts it if necessary, then loads it into an HTTP session\&.
+
+.nf
+ne_ssl_client_certificate *ccert;
+
+ccert = ne_ssl_clicert_read("/path/to/client\&.p12");
+
+if (ccert == NULL) {
+   /* handle error\&.\&.\&. */
+} else if (ne_ssl_clicert_encrypted(ccert)) {
+   char *password = prompt_for_password();
+
+   if (ne_ssl_clicert_decrypt(ccert, password)) {
+      /* could not decrypt! handle error\&.\&.\&. */
+   }
+}
+
+ne_ssl_set_clicert(sess, ccert);
+
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_ssl_cert_read\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_dname_cmp.3 b/neon/doc/man/ne_ssl_dname_cmp.3
new file mode 100644 (file)
index 0000000..d2a16c2
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_readable_dname.3
diff --git a/neon/doc/man/ne_ssl_readable_dname.3 b/neon/doc/man/ne_ssl_readable_dname.3
new file mode 100644 (file)
index 0000000..7c5e999
--- /dev/null
@@ -0,0 +1,60 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SSL_DNAME" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_ssl_readable_dname, ne_ssl_dname_cmp \- SSL distinguished name handling
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_ssl\&.h>
+.sp
+.HP 36
+const\ char\ *\fBne_ssl_readable_dname\fR\ (const\ ne_ssl_dname\ *\fIdname\fR);
+.HP 22
+int\ \fBne_ssl_dname_cmp\fR\ (const\ ne_ssl_dname\ *\fIdn1\fR, const\ ne_ssl_dname\ *\fIdn2\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_ssl_readable_dname\fR function creates a single\-line, human\-readable string out of an \fBne_ssl_dname\fR object\&. The returned string is \fBmalloc\fR()\-allocated, and must be \fBfree\fR()d by the caller\&.
+
+.PP
+The \fBne_ssl_dname_cmp\fR function compares two distinguished names, and returns zero if they are equal, or non\-zero otherwise\&.
+
+.SH "RETURN VALUE"
+
+.PP
+\fBne_ssl_readable_dname\fR returns a \fBmalloc\fR\-allocated string, and never NULL\&.
+
+.SH "EXAMPLES"
+
+.PP
+See \fBne_ssl_cert_subject\fR(3) for an example use of \fBne_ssl_readable_dname\fR\&.
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_ssl_cert_subject\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_set_verify.3 b/neon/doc/man/ne_ssl_set_verify.3
new file mode 100644 (file)
index 0000000..38ecce5
--- /dev/null
@@ -0,0 +1,123 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SSL_SET_VERIFY" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_ssl_set_verify \- register an SSL certificate verification callback
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_session\&.h>
+.sp
+.HP 31
+typedef\ int\ \fBne_ssl_verify_fn\fR\ (void\ *\fIuserdata\fR, int\ \fIfailures\fR, const\ ne_ssl_certificate\ *\fIcert\fR);
+.HP 24
+void\ \fBne_ssl_set_verify\fR\ (ne_session\ *\fIsession\fR, ne_ssl_verify_fn\ \fIverify_fn\fR, void\ *\fIuserdata\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+To enable manual SSL certificate verification, a callback can be registered using \fBne_ssl_set_verify\fR\&. 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 \fBne_ssl_trust_cert\fR(3)), or if the certificate presented is invalid in some way, the connection will fail\&.
+
+.PP
+When the callback is invoked, the \fIfailures\fR 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):
+
+.TP
+\fBNE_SSL_NOTYETVALID\fR
+The certificate is not yet valid\&.
+
+.TP
+\fBNE_SSL_EXPIRED\fR
+The certificate has expired\&.
+
+.TP
+\fBNE_SSL_IDMISMATCH\fR
+The hostname used for the session does not match the hostname to which the certificate was issued\&.
+
+.TP
+\fBNE_SSL_UNTRUSTED\fR
+The Certificate Authority which signed the certificate is not trusted\&.
+
+.PP
+Note that if either of the\fBNE_SSL_IDMISMATCH\fR or\fBNE_SSL_UNTRUSTED\fR failures is given, the connection may have been intercepted by a third party, and must not be presumed to be ``secure''\&.
+
+.PP
+The \fIcert\fR parameter passed to the callback represents the certificate which was presented by the server\&. If the server presented a chain of certificates, the chain can be accessed using \fBne_ssl_cert_signedby\fR(3)\&. The \fIcert\fR object given is not valid after the callback returns\&.
+
+.SH "RETURN VALUE"
+
+.PP
+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)\&.
+
+.SH "EXAMPLES"
+
+.PP
+The following code implements an example verification callback, using the \fBdump_cert\fR function from \fBne_ssl_cert_subject\fR(3) to display certification information\&. Notice that the hostname of the server used for the session is passed as the\fIuserdata\fR parameter to the callback\&.
+
+.nf
+
+static int
+my_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
+{
+  const char *hostname = userdata;
+
+  dump_cert(cert);
+
+  puts("Certificate verification failed \- the connection may have been "
+       "intercepted by a third party!");
+
+  if (failures & NE_SSL_IDMISMATCH) { 
+    const char *id = ne_ssl_cert_identity(cert);
+    if (id) 
+      printf("Server certificate was issued to '%s' not '%s'\&.\\n",
+             id, hostname);
+    else
+      printf("The certificate was not issued for '%s'\\n", hostname);
+  }
+
+  if (failures & NE_SSL_UNTRUSTED)
+    puts("The certificate is not signed by a trusted Certificate Authority\&.");
+
+  /* \&.\&.\&. check for validity failures \&.\&.\&. */
+
+  if (prompt_user())
+    return 1; /* fail verification */
+  else
+    return 0; /* trust the certificate anyway */
+}
+
+int
+main(\&.\&.\&.)
+{
+  ne_session *sess = ne_session_create("https", "some\&.host\&.name", 443);
+  ne_ssl_set_verify(sess, my_verify, "some\&.host\&.name");
+  \&.\&.\&.
+}
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_ssl_trust_cert\fR(3), \fBne_ssl_readable_dname\fR(3), \fBne_ssl_cert_subject\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_trust_cert.3 b/neon/doc/man/ne_ssl_trust_cert.3
new file mode 100644 (file)
index 0000000..ca9b61c
--- /dev/null
@@ -0,0 +1,67 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_SSL_TRUST_CERT" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_ssl_trust_cert, ne_ssl_trust_default_ca \- functions to indicate that certificates are trusted
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_session\&.h>
+.sp
+.HP 24
+void\ \fBne_ssl_trust_cert\fR\ (ne_session\ *\fIsession\fR, const\ ne_ssl_certificate\ *\fIcert\fR);
+.HP 30
+void\ \fBne_ssl_trust_default_ca\fR\ (ne_session\ *\fIsession\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+To indicate that a given certificate is trusted by the user, the certificate object can be passed to \fBne_ssl_trust_cert\fR\&. The certificate object is duplicated internally and can subequently be destroyed\&.
+
+.PP
+The SSL library in use by neon may include a default set of CA certificates; calling the \fBne_ssl_trust_default_ca\fR function will indicate that these CAs are trusted by the user\&.
+
+.SH "EXAMPLES"
+
+.PP
+Load the CA certificate stored in \fI/path/to/cacert\&.pem\fR:
+
+.nf
+ne_session *sess = ne_session_create(\&.\&.\&.);
+ne_ssl_certificate *cert = ne_ssl_cert_read("/path/to/cacert\&.pem");
+
+if (cert) {
+   ne_ssl_trust_cert(sess, cert);
+   ne_ssl_cert_free(cert);
+} else {
+   printf("Could not load CA cert: %s\\n", ne_get_error(sess));
+}
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_ssl_cert_read\fR(3), \fBne_ssl_cert_import\fR(3), \fBne_ssl_cert_free\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_ssl_trust_default_ca.3 b/neon/doc/man/ne_ssl_trust_default_ca.3
new file mode 100644 (file)
index 0000000..49af8c5
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_ssl_trust_cert.3
diff --git a/neon/doc/man/ne_status.3 b/neon/doc/man/ne_status.3
new file mode 100644 (file)
index 0000000..925299c
--- /dev/null
@@ -0,0 +1,72 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_STATUS" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_status \- HTTP status structure
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_utils\&.h>
+
+typedef struct {
+    int major_version, minor_version;
+    int code, klass;
+    const char *reason_phrase;
+} \fBne_status\fR;
+.sp
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+An \fBne_status\fR type represents an HTTP response status; used in response messages giving a result of request\&. The major_version and minor_version fields give the HTTP version supported by the server issuing the response\&. The code field gives the status code of the result (lying between 100 and 999 inclusive), and the klass field gives the class[2], which is equal to the most significant digit of the status\&.
+
+.PP
+There are five classes of HTTP status code defined by RFC2616:
+
+.TP
+1xx
+Informational response\&.
+
+.TP
+2xx
+Success: the operation was successful
+
+.TP
+3xx
+Redirection
+
+.TP
+4xx
+Client error: the request made was incorrect in some manner\&.
+
+.TP
+5xx
+Server error
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_get_status\fR(3)\&.
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_strdup.3 b/neon/doc/man/ne_strdup.3
new file mode 100644 (file)
index 0000000..8564fc1
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_malloc.3
diff --git a/neon/doc/man/ne_strndup.3 b/neon/doc/man/ne_strndup.3
new file mode 100644 (file)
index 0000000..8564fc1
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_malloc.3
diff --git a/neon/doc/man/ne_token.3 b/neon/doc/man/ne_token.3
new file mode 100644 (file)
index 0000000..452d1e1
--- /dev/null
@@ -0,0 +1,59 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_TOKEN" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_token, ne_qtoken \- string tokenizers
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_string\&.h>
+.sp
+.HP 16
+char\ *\fBne_token\fR\ (char\ **\fIstr\fR, char\ \fIsep\fR);
+.HP 17
+char\ *\fBne_qtoken\fR\ (char\ **\fIstr\fR, char\ \fIsep\fR, const\ char\ *\fIquotes\fR);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+\fBne_token\fR and \fBne_qtoken\fR tokenize the string at the location stored in the pointer \fIstr\fR\&. Each time the function is called, it returns the next token, and modifies the \fIstr\fR 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 \fIsep\fR; if \fBne_qtoken\fR 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 \fIquotes\fR string\&.
+
+.PP
+The string being tokenized is modified each time the tokenizing function is called; replacing the next separator character with a NUL terminator\&.
+
+.SH "EXAMPLES"
+
+.PP
+The following function prints out each token in a comma\-separated string \fIlist\fR, which is modified in\-place:
+
+.nf
+static void splitter(char *list)
+{
+  do {
+    printf("Token: %s\\n", ne_token(&list, ','));
+  while (list);
+}
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_version_match.3 b/neon/doc/man/ne_version_match.3
new file mode 100644 (file)
index 0000000..4ba03e9
--- /dev/null
@@ -0,0 +1,58 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NE_VERSION_MATCH" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+ne_version_match, ne_version_string \- library versioning
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+
+#include <ne_utils\&.h>
+.sp
+.HP 22
+int\ \fBne_version_match\fR\ (int\ \fImajor\fR, int\ \fIminor\fR);
+.HP 32
+const\ char\ *\fBne_version_string\fR\ (void);
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBne_version_match\fR function returns non\-zero if the library version is not of major version \fImajor\fR, or the minor version is less than \fIminor\fR\&. For neon versions 0\&.x, every minor version is assumed to be incompatible with every other minor version\&.
+
+.PP
+The \fBne_version_string\fR function returns a string giving the library version\&.
+
+.SH "EXAMPLES"
+
+.PP
+To require neon 1\&.x, version 1\&.2 or later:
+
+.nf
+if (ne_version_match(1, 2)) {
+    printf("Library version out of date: 1\&.2 required, found %s\&.",
+           ne_version_string());
+    exit(1);
+}
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/ne_version_string.3 b/neon/doc/man/ne_version_string.3
new file mode 100644 (file)
index 0000000..4f3447d
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ne_version_match.3
diff --git a/neon/doc/man/neon-config.1 b/neon/doc/man/neon-config.1
new file mode 100644 (file)
index 0000000..96a2bc8
--- /dev/null
@@ -0,0 +1,85 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NEON-CONFIG" 1 "5 July 2004" "neon 0.24.7" "neon"
+.SH NAME
+neon-config \- script providing information about installed copy of neon library
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+.HP 12
+\fBneon\-config\fR [\fB\-\-prefix\fR] [\fB\fB\-\-cflags\fR\fR | \fB\fB\-\-libs\fR\fR | \fB\fB\-\-la\-file\fR\fR | \fB\fB\-\-support\fR\ \fIfeature\fR\fR | \fB\fB\-\-help\fR\fR | \fB\fB\-\-version\fR\fR]
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBneon\-config\fR script provides information about an installed copy of the neon library\&. The \fB\-\-cflags\fR and \fB\-\-libs\fR options instruct how to compile and link an application against the library; the \fB\-\-version\fR and \fB\-\-support\fR options can help determine whether the library meets the applications requirements\&.
+
+.SH "OPTIONS"
+
+.TP
+\fB\-\-cflags\fR
+Print the flags which should be passed to the C compiler when compiling object files, when the object files use neon header files\&.
+
+.TP
+\fB\-\-libs\fR
+Print the flags which should be passed to the linker when linking an application which uses the neon library
+
+.TP
+\fB\-\-la\-file\fR
+Print the location of the libtool library script, \fIlibneon\&.la\fR, which can be used to link against neon by applications using libtool\&.
+
+.TP
+\fB\-\-version\fR
+Print the version of the library
+
+.TP
+\fB\-\-prefix\fR \fIdir\fR
+If \fIdir\fR is given; relocate output of \fB\-\-cflags\fR and \fB\-\-libs\fR as if neon was installed in given prefix directory\&. Otherwise, print the installation prefix of the library\&.
+
+.TP
+\fB\-\-support\fR \fIfeature\fR
+The script exits with success if \fIfeature\fR is supported by the library\&.
+
+.TP
+\fB\-\-help\fR
+Print help message; includes list of known features and whether they are supported or not\&.
+
+.SH "EXAMPLE"
+
+.PP
+Below is a Makefile fragment which could be used to build an application against an installed neon library, when the \fBneon\-config\fR script can be found in \fB$PATH\fR\&.
+
+.nf
+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
+.fi
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
diff --git a/neon/doc/man/neon.3 b/neon/doc/man/neon.3
new file mode 100644 (file)
index 0000000..cb863c7
--- /dev/null
@@ -0,0 +1,107 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "NEON" 3 "5 July 2004" "neon 0.24.7" "neon API reference"
+.SH NAME
+neon \- HTTP and WebDAV client library
+.SH "DESCRIPTION"
+
+.PP
+neon is an HTTP and WebDAV client library\&. The major abstractions exposed are the HTTP session, created by \fBne_session_create\fR(3); and the HTTP request, created by \fBne_request_create\fR(3)\&. HTTP authentication is handled transparently for server and proxy servers, see \fBne_set_server_auth\fR(3); complete SSL/TLS support is also included, see \fBne_ssl_set_verify\fR(3)\&.
+
+.SH "CONVENTIONS"
+
+.PP
+Some conventions are used throughout the neon API, to provide a consistent and simple interface; these are documented below\&.
+
+.SS "Thread-safeness and global initialization"
+
+.PP
+neon itself is implemented to be thread\-safe (avoiding any use of global state), but relies on the operating system providing a thread\-safe resolver interface\&. Modern operating systems offer the thread\-safe \fBgetaddrinfo\fR interface, which neon supports; some others implement \fBgethostbyname\fR using thread\-local storage\&.
+
+.PP
+To allow thread\-safe use of the OpenSSL library, the application must register some locking callbacks in accordance with the OpenSSL documentation: \fIhttp://www.openssl.org/docs/crypto/threads.html\fR\&.
+
+.PP
+Some platforms and libraries used by neon require global initialization before use; notably: 
+
+.TP 3
+\(bu
+OpenSSL requires global initialization to load shared lookup tables\&.
+.TP
+\(bu
+The SOCKS library requires initialization before use\&.
+.TP
+\(bu
+The Win32 socket library requires initialization before use\&.
+.LP
+ The \fBne_sock_init\fR(3) function should be called before any other use of neon to perform any necessary initialization needed for the particular platform\&.
+
+.SS "Namespaces"
+
+.PP
+To avoid possible collisions between names used for symbols and preprocessor macros by an application and the libraries it uses, it is good practice for each library to reserve a particular namespace prefix\&. An application which ensures it uses no names with these prefixes is then guaranteed to avoid such collisions\&.
+
+.PP
+The neon library reserves the use of the namespace prefixes ne_ and NE_\&. The libraries used by neon may also reserve certain namespaces; collisions between these libraries and a neon\-based application will not be detected at compile time, since the underlying library interfaces are not exposed through the neon header files\&. Such collisions can only be detected at link time, when the linker attempts to resolve symbols\&. The following list documents some of the namespaces claimed by libraries used by neon; this list may be incomplete\&.
+
+.TP
+SSL, ssl, TLS, tls, ERR_, BIO_, d2i_, i2d_, ASN1_
+Some of the many prefixes used by the OpenSSL library; little attempt has been made to keep exported symbols within any particular prefixes for this library\&.
+
+.TP
+XML_, Xml[A\-Z]
+Namespaces used by the expat library\&.
+
+.TP
+xml[A\-Z], html[A\-Z], docb[A\-Z]
+Namespaces used by the libxml2 library; a relatively small number of symbols are used without these prefixes\&.
+
+.SS "Argument validation"
+
+.PP
+neon does not attempt to validate that the parameters passed to functions conform to the API (for instance, checking that pointer arguments are not NULL)\&. Any use of the neon API which is not documented to produce a certain behaviour results is said to produce undefined behaviour; it is likely that neon will segfault under these conditions\&.
+
+.SS "URI paths, WebDAV metadata"
+
+.PP
+The path strings passed to any function must be URI\-encoded by the application; neon never performs any URI encoding or decoding internally\&. WebDAV property names and values must be valid UTF\-8 encoded Unicode strings\&.
+
+.SS "User interaction"
+
+.PP
+As a pure library interface, neon will never produce output on \fBstdout\fR or \fBstderr\fR; all user interaction is the responsibilty of the application\&.
+
+.SS "Memory handling"
+
+.PP
+neon does not attempt to cope gracefully with an out\-of\-memory situation; instead, by default, the \fBabort\fR function is called to immediately terminate the process\&. An application may register a custom function which will be called before \fBabort\fR in such a situation; see \fBne_oom_callback\fR(3)\&.
+
+.SS "Callbacks and userdata"
+
+.PP
+Whenever a callback is registered, a userdata pointer is also used to allow the application to associate a context with the callback\&. The userdata is of type \fBvoid *\fR, allowing any pointer to be used\&.
+
+.SH "SEE ALSO"
+
+.PP
+\fBne_session_create\fR(3), \fBne_oom_callback\fR(3)
+
+.SH AUTHOR
+Joe Orton <neon@webdav\&.org>.
index 034d7a7..feca0a1 100644 (file)
@@ -1,21 +1,33 @@
 
 p, pre.funcsynopsisinfo { margin-left: 0.4em; margin-right: 0.4em; }
 
-div.funcsynopsis { line-height: 0.5em; }
+span.term { margin-left: 0.6em; margin-bottom: 0.0em }
 
 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; }
+div.navheader { border-top: 1px solid #bbf2bb; }
+div.navfooter { border-bottom: 1px solid #bbf2bb; }
 
-pre.programlisting { 
+div.funcprototype { margin-top: 0.2em; margin-left: 0.4em; margin-bottom: 0.2em; }
+
+pre.programlisting, pre.screen { 
        background-color: #dddddd;
        margin-left: 0.6em; margin-right: 1em;
        padding: 0.3em; width: 50em;
 }
 
+div.funcsynopsis, div.cmdsynopsis { 
+       background-color: #dddddd;
+       margin-left: 0.4em; margin-right: 0.4em;
+       padding: 0.1em;
+}
+
+div.warning {
+        border: 1px solid #777777;
+}
+
 h1.title { border-bottom: thick solid #bbf2bb; padding-bottom: 0.1em; }
 
 div.toc { 
@@ -25,5 +37,6 @@ div.toc {
 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; }
+     padding-bottom: 0.3em; padding-top: 0.2em; spacing-top: 0.1em; }
 
+h3 { border-bottom: 1px solid #bbf2bb; }
index 14b8a68..06e7bca 100644 (file)
@@ -1,10 +1,75 @@
-<?xml version='1.0'?> <!-- -*- DocBook -*- -->
+<?xml version='1.0'?> <!-- -*- text -*- -->
 
-<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
-  "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+
+<!ENTITY % isoent SYSTEM
+    "http://www.oasis-open.org/docbook/xml/4.2/ent/iso-num.ent">
+<!ENTITY % isopub SYSTEM
+    "http://www.oasis-open.org/docbook/xml/4.2/ent/iso-pub.ent">
+
+%isoent;
+%isopub;
 
 <!ENTITY fdl SYSTEM "fdl.sgml">
 
+<!-- date/version stamp files created as release tarball is rolled -->
+<!ENTITY date SYSTEM "date.xml">
+<!ENTITY version SYSTEM "version.xml">
+
+<!ENTITY neon "neon">
+
+<!-- a useful entity for writing reference examples -->
+<!ENTITY egsess "ne_session *sess = ne_session_create(...);">
+
+<!ENTITY null "<literal>NULL</literal>">
+
+<!ENTITY nul "<literal>NUL</literal>">
+
+<!-- xml.xml entities: -->
+<!ENTITY startelm "<emphasis>start-element</emphasis>">
+<!ENTITY cdata "<emphasis>character-data</emphasis>">
+<!ENTITY endelm "<emphasis>end-element</emphasis>">
+
+<!ENTITY section.features SYSTEM "feat.xml">
+<!ENTITY section.using SYSTEM "using.xml">
+<!ENTITY section.xml SYSTEM "xml.xml">
+<!ENTITY section.ssl SYSTEM "ssl.xml">
+
+<!ENTITY biblio SYSTEM "biblio.xml">
+
+<!ENTITY refneon SYSTEM "ref/neon.xml">
+<!ENTITY refconfig SYSTEM "ref/config.xml">
+
+<!ENTITY refsess SYSTEM "ref/sess.xml">
+<!ENTITY referr SYSTEM "ref/err.xml">
+<!ENTITY refopts SYSTEM "ref/opts.xml">
+<!ENTITY refsslvfy SYSTEM "ref/sslvfy.xml">
+<!ENTITY refsslcert SYSTEM "ref/sslcert.xml">
+<!ENTITY refsslcert2 SYSTEM "ref/sslcert2.xml">
+<!ENTITY refsslcertio SYSTEM "ref/sslcertio.xml">
+<!ENTITY refssldname SYSTEM "ref/ssldname.xml">
+<!ENTITY refssltrust SYSTEM "ref/ssltrust.xml">
+<!ENTITY refreq SYSTEM "ref/req.xml">
+<!ENTITY refreqhdr SYSTEM "ref/reqhdr.xml">
+<!ENTITY refstatus SYSTEM "ref/status.xml">
+<!ENTITY refgetst SYSTEM "ref/getst.xml">
+<!ENTITY refreqbody SYSTEM "ref/reqbody.xml">
+<!ENTITY refauth SYSTEM "ref/auth.xml">
+<!ENTITY refalloc SYSTEM "ref/alloc.xml">
+<!ENTITY refbuf SYSTEM "ref/buf.xml">
+<!ENTITY refbufcr SYSTEM "ref/bufcr.xml">
+<!ENTITY refbufapp SYSTEM "ref/bufapp.xml">
+<!ENTITY refbufdest SYSTEM "ref/bufdest.xml">
+<!ENTITY refbufutil SYSTEM "ref/bufutil.xml">
+<!ENTITY reftok SYSTEM "ref/tok.xml">
+<!ENTITY refshave SYSTEM "ref/shave.xml">
+<!ENTITY refvers SYSTEM "ref/vers.xml">
+<!ENTITY refinit SYSTEM "ref/init.xml">
+<!ENTITY refresolve SYSTEM "ref/resolve.xml">
+<!ENTITY refiaddr SYSTEM "ref/iaddr.xml">
+<!ENTITY refclicert SYSTEM "ref/clicert.xml">
+
 ]>
 
 <book>
@@ -16,7 +81,7 @@
        <address><email>neon@webdav.org</email></address>
       </affiliation>
     </author>
-    <copyright><year>2001</year><holder>Joe Orton</holder></copyright>
+    <copyright><year>2001-2003</year><holder>Joe Orton</holder></copyright>
 
     <legalnotice>
       <para>Permission is granted to copy, distribute and/or modify this document
 
   </bookinfo>
   
-  <chapter>
-    <title>Introduction to neon</title>
-    <para>This is neon, an HTTP and WebDAV client library</para>
+  <chapter id="intro">
+    <title>Introduction</title>
+
+    <para>This chapter provides an introduction to neon, giving an
+overview of the range of features offered, and some general guidelines
+for using the neon API.</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>
+    <para>neon aims to provide a modern, flexible, and simple API
+in the C programming language for implementing HTTP and WebDAV
+support.  The WebDAV functionality is entirely separate from the basic
+HTTP functionality; neon can be used simply as an HTTP client library,
+ignoring the WebDAV support if desired.</para>
+
+    &section.features;
+
+    &section.using;
     
   </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 id="api">
+    <title>The &neon; C language interface</title>
+
+    <para>The documentation for the &neon; interface is split between
+    this chapter, which gives a broad introduction to the abstractions
+    exposed by the library, and <xref linkend="ref"/>, which gives a
+    function-by-function breakdown of the interface.</para>
+
+    &section.xml;
+
+<!-- &section.ssl; -->
 
   </chapter>
-  
-  <reference>
+  <reference id="ref">
+
+    <!-- these are used in the man page header/footers -->
+    <referenceinfo>
+      <title>neon API reference</title>
+      <date>&date;</date>
+      <productname>neon &version;</productname>
+    </referenceinfo>
     
     <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>
+
+    &refneon; <!-- neon -->
+    &refconfig; <!-- neon-config -->
+
+    &refreqhdr; <!-- ne_add_request_header -->
+    &refresolve; <!-- ne_addr_resolve -->
+    &refbuf; <!-- ne_buffer -->
+    &refbufapp; <!-- ne_buffer_append -->
+    &refbufutil; <!-- ne_buffer_clear -->
+    &refbufcr; <!-- ne_buffer_create -->
+    &refbufdest; <!-- ne_buffer_destroy -->
+    &referr; <!-- ne_get_error -->
+    &refgetst; <!-- ne_get_status -->
+    &refiaddr; <!-- ne_iaddr_make -->
+    &refalloc; <!-- ne_malloc -->
+    &refreq; <!-- ne_request_create -->
+    &refsess; <!-- ne_session_create -->
+    &refopts; <!-- ne_set_useragent -->
+    &refreqbody; <!-- ne_set_request_body_buffer -->
+    &refauth; <!-- ne_set_server_auth -->
+    &refshave; <!-- ne_shave -->
+    &refinit; <!-- ne_sock_init -->
+    &refsslcert; <!-- ne_ssl_cert_identity -->
+    &refsslcert2; <!-- ne_ssl_cert_cmp -->
+    &refsslcertio; <!-- ne_ssl_cert_read -->
+    &refssldname; <!-- ne_ssl_dname -->
+    &refssltrust; <!-- ne_ssl_load_ca -->
+    &refsslvfy; <!-- ne_ssl_set_verify -->
+    &refclicert; <!-- ne_ssl_client_cert -->
+    &refstatus; <!-- ne_status -->
+    &reftok; <!-- ne_token -->
+    &refvers; <!-- ne_version_match -->
+
+    <!-- REFEND -->
+    <!-- ******************************************************************* -->
 
   </reference>
 
+&biblio;
+
 &fdl;
   
 </book>
index 3f8987c..68b515c 100644 (file)
       </refnamediv>
       
       <refsynopsisdiv>
-       
-       <programlisting>#include &lt;ne_string.h&gt;
+
+       <funcsynopsis><funcsynopsisinfo>#include &lt;ne_string.h&gt;
 
 typedef struct {
     char *data;
     size_t used;
     size_t length;
-} <type>ne_buffer</type>;</programlisting>
+} <type>ne_buffer</type>;</funcsynopsisinfo></funcsynopsis>
 
       </refsynopsisdiv>
 
diff --git a/neon/doc/ref/clicert.xml b/neon/doc/ref/clicert.xml
new file mode 100644 (file)
index 0000000..49f95cf
--- /dev/null
@@ -0,0 +1,153 @@
+<refentry id="refclicert">
+
+  <refmeta>
+    <refentrytitle>ne_ssl_client_cert</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname id="ne_ssl_clicert_read">ne_ssl_clicert_read</refname>
+    <refname id="ne_ssl_clicert_name">ne_ssl_clicert_name</refname>
+    <refname id="ne_ssl_clicert_encrypted">ne_ssl_clicert_encrypted</refname>
+    <refname id="ne_ssl_clicert_decrypt">ne_ssl_clicert_decrypt</refname>
+    <refname id="ne_ssl_clicert_owner">ne_ssl_clicert_owner</refname>
+    <refname id="ne_ssl_clicert_free">ne_ssl_clicert_free</refname>
+    <refpurpose>SSL client certificate handling</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_ssl.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>ne_ssl_client_cert *<function>ne_ssl_clicert_read</function></funcdef>
+        <paramdef>const char *<parameter>filename</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>const char *<function>ne_ssl_clicert_name</function></funcdef>
+        <paramdef>const ne_ssl_client_cert *<parameter>ccert</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>ne_ssl_clicert_encrypted</function></funcdef>
+        <paramdef>const ne_ssl_client_cert *<parameter>ccert</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>ne_ssl_clicert_decrypt</function></funcdef>
+        <paramdef>ne_ssl_client_cert *<parameter>ccert</parameter></paramdef>
+        <paramdef>const char *<parameter>password</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>const ne_ssl_certificate *<function>ne_ssl_clicert_owner</function></funcdef>
+        <paramdef>const ne_ssl_client_cert *<parameter>ccert</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>void <function>ne_ssl_clicert_free</function></funcdef>
+        <paramdef>ne_ssl_client_cert *<parameter>ccert</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>ne_ssl_clicert_read</function> function reads
+    a <firstterm>client certificate</firstterm> from a
+    PKCS#12-formatted file, and returns an
+    <type>ne_ssl_client_certificate</type> object.  If the client
+    certificate is encrypted, it must be decrypted before it is used.
+    An <type>ne_ssl_client_certificate</type> object holds a client
+    certificate and the associated private key, not just a
+    certificate; the term "<glossterm>client certificate</glossterm>"
+    will used to refer to this pair.</para>
+
+    <para>A client certificate can be in one of two states:
+    <emphasis>encrypted</emphasis> or <emphasis>decrypted</emphasis>.
+    The <function>ne_ssl_clicert_encrypted</function> function will
+    return non-zero if the client certificate is in the
+    <emphasis>encrypted</emphasis> state.  A client certificate object
+    returned by <function>ne_ssl_clicert_read</function> may be
+    initially in either state, depending on whether the file was
+    encrypted or not.</para>
+
+    <para><function>ne_ssl_clicert_decrypt</function> can be used to
+    decrypt a client certificate using the appropriate password.  This
+    function must only be called if the object is in the
+    <emphasis>encrypted</emphasis> state; if decryption fails, the
+    certificate state does not change, so decryption can be attempted
+    more than once using different passwords.</para>
+
+    <para>A client certificate can be given a "friendly name" when it
+    is created; <function>ne_ssl_clicert_name</function> will return
+    this name (or &null; if no friendly name was specified).
+    <function>ne_ssl_clicert_name</function> can be used when the
+    client certificate is in either the encrypted or decrypted state,
+    and will return the same string for the lifetime of the
+    object.</para>
+
+    <para>The function <function>ne_ssl_clicert_owner</function>
+    returns the certificate part of the client certificate; it must
+    only be called if the client certificate is in the
+    <emphasis>decrypted</emphasis> state.</para>
+
+    <para>When the client certificate is no longer needed, the
+    <function>ne_ssl_clicert_free</function> function should be used
+    to destroy the object.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+
+    <para><function>ne_ssl_clicert_read</function> returns a client
+    certificate object, or &null; if the file could not be read.
+    <function>ne_ssl_clicert_encrypted</function> returns zero if the
+    object is in the decrypted state, or non-zero if it is in the
+    encrypted state. <function>ne_ssl_clicert_name</function> returns
+    a &nul;-terminated friendly name string, or &null;.
+    <function>ne_ssl_clicert_owner</function> returns a certificate
+    object.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>The following code reads a client certificate and decrypts
+    it if necessary, then loads it into an HTTP session.</para>
+
+    <programlisting>ne_ssl_client_certificate *ccert;
+
+ccert = ne_ssl_clicert_read("/path/to/client.p12");
+
+if (ccert == NULL) {
+   /* handle error... */
+} else if (ne_ssl_clicert_encrypted(ccert)) {
+   char *password = prompt_for_password();
+
+   if (ne_ssl_clicert_decrypt(ccert, password)) {
+      /* could not decrypt! handle error... */
+   }
+}
+
+ne_ssl_set_clicert(sess, ccert);
+</programlisting>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><xref linkend="ne_ssl_cert_read"/></para>
+  </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/ref/config.xml b/neon/doc/ref/config.xml
new file mode 100644 (file)
index 0000000..4823cca
--- /dev/null
@@ -0,0 +1,124 @@
+    <refentry id="refconfig">
+
+      <refentryinfo><title>neon</title></refentryinfo>
+
+      <refmeta>
+       <refentrytitle>neon-config</refentrytitle>
+       <manvolnum>1</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="neon-config">neon-config</refname>
+
+       <refpurpose>script providing information about installed copy
+       of neon library</refpurpose>
+      </refnamediv>
+
+      <refsynopsisdiv>
+
+       <cmdsynopsis>
+         <command>neon-config</command>
+         <arg choice="opt"><option>--prefix</option></arg>
+         <group>
+           <arg><option>--cflags</option></arg>
+           <arg><option>--libs</option></arg>
+           <arg><option>--la-file</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><simpara>Print the flags which should be passed to
+the C compiler when compiling object files, when the object files use
+neon header files.</simpara></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--libs</option></term>
+           <listitem><simpara>Print the flags which should be passed to
+the linker when linking an application which uses the neon
+library</simpara></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--la-file</option></term>
+           <listitem><simpara>Print the location of the libtool library
+script, <filename>libneon.la</filename>, which can be used to link against
+&neon; by applications using libtool.</simpara></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--version</option></term>
+           <listitem><simpara>Print the version of the library</simpara></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--prefix</option> <replaceable>dir</replaceable></term>
+           <listitem><simpara>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.</simpara></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--support</option> <replaceable>feature</replaceable></term>
+           <listitem><simpara>The script exits with success if
+<replaceable>feature</replaceable> is supported by the
+library.</simpara></listitem>
+          </varlistentry>
+
+         <varlistentry>
+           <term><option>--help</option></term>
+           <listitem><simpara>Print help message; includes list of known
+           features and whether they are supported or not.</simpara></listitem>
+         </varlistentry>
+
+       </variablelist>
+
+      </refsect1>
+
+      <refsect1 id="example">
+       <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>
index 9d44277..5a419c4 100644 (file)
        <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>
+a pointer to the HTTP status object giving the result of a request.
+The object returned only becomes valid once the request has been
+<emphasis>successfully</emphasis> dispatched (the return value of
+<function>ne_request_dispatch</function> or
+<function>ne_begin_request</function> was zero).  The object remains
+valid until the associated request object is destroyed.</para>
 
       </refsect1>
 
       <refsect1>
        <title>See also</title>
 
-       <para><xref linkend="ne_get_status"/>, <xref
+       <para><xref linkend="ne_status"/>, <xref
        linkend="ne_request_create"/></para>
 
       </refsect1>
diff --git a/neon/doc/ref/iaddr.xml b/neon/doc/ref/iaddr.xml
new file mode 100644 (file)
index 0000000..cf64a13
--- /dev/null
@@ -0,0 +1,124 @@
+<refentry id="refiaddr">
+
+  <refmeta>
+    <refentrytitle>ne_iaddr_make</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname id="ne_iaddr_make">ne_iaddr_make</refname>
+    <refname id="ne_iaddr_cmp">ne_iaddr_cmp</refname>
+    <refname id="ne_iaddr_print">ne_iaddr_print</refname>
+    <refname id="ne_iaddr_free">ne_iaddr_free</refname>
+    <refpurpose>functions to manipulate and compare network addresses</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_socket.h&gt;
+
+typedef enum {
+    ne_iaddr_ipv4 = 0,
+    ne_iaddr_ipv6
+} <type>ne_iaddr_type</type>;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>ne_inet_addr *<function>ne_iaddr_make</function></funcdef>
+        <paramdef>ne_iaddr_type <parameter>type</parameter></paramdef>
+        <paramdef>const unsigned char *<parameter>raw</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>ne_iaddr_cmp</function></funcdef>
+        <paramdef>const ne_inet_addr *<parameter>i1</parameter></paramdef>
+        <paramdef>const ne_inet_addr *<parameter>i2</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>char *<function>ne_iaddr_print</function></funcdef>
+        <paramdef>const ne_inet_addr *<parameter>ia</parameter></paramdef>
+        <paramdef>char *<parameter>buffer</parameter></paramdef>
+        <paramdef>size_t <parameter>bufsiz</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>void <function>ne_iaddr_free</function></funcdef>
+        <paramdef>const ne_inet_addr *<parameter>addr</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>ne_iaddr_make</function> creates an
+    <type>ne_inet_addr</type> object from a raw binary network
+    address; for instance the four bytes <literal>0x7f 0x00 0x00
+    0x01</literal> represent the IPv4 address
+    <literal>127.0.0.1</literal>.  The object returned is suitable for
+    passing to <function>ne_sock_connect</function>.  A binary IPv4
+    address contains four bytes; a binary IPv6 address contains
+    sixteen bytes; addresses passed must be in network byte
+    order.</para>
+
+    <para><function>ne_iaddr_cmp</function> can be used to compare two
+    network addresses; returning zero only if they are identical.  The
+    addresses need not be of the same address type; if the addresses
+    are not of the same type, the return value is guaranteed to be
+    non-zero.</para>
+
+    <para><function>ne_iaddr_print</function> can be used to print the
+    human-readable string representation of a network address into a
+    buffer, for instance the string
+    <literal>"127.0.0.1"</literal>.</para>
+
+    <para><function>ne_iaddr_free</function> releases the memory
+    associated with a network address object.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+
+    <para><function>ne_iaddr_make</function> returns &null; if the
+    address type passed is not supported (for instance on a platform
+    which does not support IPv6).</para>
+
+
+    <para><function>ne_iaddr_print</function> returns the
+    <parameter>buffer</parameter> pointer, and never &null;.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>The following example connects a socket to port 80 at the
+    address <literal>127.0.0.1</literal>.</para>
+    <programlisting>unsigned char addr[] = "\0x7f\0x00\0x00\0x01";
+ne_inet_addr *ia;
+
+ia = ne_iaddr_make(ne_iaddr_ipv4, addr);
+if (ia != NULL) {
+    ne_socket *sock = ne_sock_connect(ia, 80);
+    ne_iaddr_free(ia);
+    /* ... */
+} else {
+    /* ... */
+}</programlisting>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><xref linkend="ne_addr_resolve"/></para>
+  </refsect1>
+
+</refentry>
+
index 219f682..7c11325 100644 (file)
@@ -7,15 +7,15 @@
 
   <refnamediv>
     <refname>neon</refname>
-    <refpurpose>neon API conventions</refpurpose>
+    <refpurpose>HTTP and WebDAV client library</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
+    abstractions exposed are the HTTP <emphasis>session</emphasis>,
+    created by <xref linkend="ne_session_create"/>; and the HTTP
     <emphasis>request</emphasis>, created by <xref
     linkend="ne_request_create"/>.  HTTP authentication is handled
     transparently for server and proxy servers, see <xref
     provide a consistent and simple interface; these are documented
     below.</para>
 
-  </refsect1>
+  <refsect2>
+    <title>Thread-safeness and global initialization</title>
+
+    <para>&neon; itself is implemented to be thread-safe (avoiding any
+    use of global state), but relies on the operating system providing
+    a thread-safe resolver interface.  Modern operating systems offer
+    the thread-safe <function>getaddrinfo</function> interface, which
+    &neon; supports; some others implement
+    <function>gethostbyname</function> using thread-local
+    storage.</para>
+
+    <para>To allow thread-safe use of the OpenSSL library, the
+    application must register some locking callbacks in accordance
+    with the <ulink
+    url="http://www.openssl.org/docs/crypto/threads.html">OpenSSL
+    documentation</ulink>.</para>
+
+    <para>Some platforms and libraries used by &neon; require global
+    initialization before use; notably:
+
+    <itemizedlist>
+      <listitem><simpara>OpenSSL requires global initialization to
+      load shared lookup tables.</simpara></listitem>
+
+      <listitem><simpara>The SOCKS library requires initialization
+      before use.</simpara></listitem>
+
+      <listitem><simpara>The Win32 socket library requires
+      initialization before use.</simpara></listitem>
+    </itemizedlist>
+
+    The <xref linkend="ne_sock_init"/> function should be called
+    before any other use of &neon; to perform any necessary
+    initialization needed for the particular platform.</para>
+
+  </refsect2>
+
+  <refsect2>
+    <title>Namespaces</title>
+
+    <para>To avoid possible collisions between names used for symbols
+    and preprocessor macros by an application and the libraries it
+    uses, it is good practice for each library to reserve a particular
+    <emphasis>namespace prefix</emphasis>.  An application which
+    ensures it uses no names with these prefixes is then guaranteed to
+    avoid such collisions.</para>
+
+    <para>The &neon; library reserves the use of the namespace
+    prefixes <literal>ne_</literal> and <literal>NE_</literal>.  The
+    libraries used by &neon; may also reserve certain namespaces;
+    collisions between these libraries and a &neon;-based application
+    will not be detected at compile time, since the underlying library
+    interfaces are not exposed through the &neon; header files.  Such
+    collisions can only be detected at link time, when the linker
+    attempts to resolve symbols.  The following list documents some of
+    the namespaces claimed by libraries used by &neon;; this list may
+    be incomplete.</para>
+
+    <variablelist>
+
+      <varlistentry>
+       <term>SSL, ssl, TLS, tls, ERR_, BIO_, d2i_, i2d_, ASN1_</term>
+       <listitem><simpara>Some of the many prefixes used by the OpenSSL
+       library; little attempt has been made to keep exported symbols
+       within any particular prefixes for this
+       library.</simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>XML_, Xml[A-Z]</term> <listitem><simpara>Namespaces
+       used by the expat library.</simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>xml[A-Z], html[A-Z], docb[A-Z]</term>
+       <listitem><simpara>Namespaces used by the libxml2 library; a
+       relatively small number of symbols are used without these
+       prefixes.</simpara></listitem>
+      </varlistentry>
+
+    </variablelist>
+
+  </refsect2>
+
+  <refsect2>
+    <title>Argument validation</title>
+  
+    <para>&neon; does not attempt to validate that the parameters
+    passed to functions conform to the API (for instance, checking
+    that pointer arguments are not &null;).  Any use of the &neon; API
+    which is not documented to produce a certain behaviour results is
+    said to produce <emphasis>undefined behaviour</emphasis>; it is
+    likely that &neon; will segfault under these conditions.</para>
+
+  </refsect2>
 
   <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>
+    <emphasis>URI-encoded</emphasis> by the application; &neon; never
+    performs any URI encoding or decoding internally.  WebDAV property
+    names and values must be valid UTF-8 encoded Unicode
+    strings.</para>
 
   </refsect2>
 
   <refsect2>
+    <title>User interaction</title>
+
+    <para>As a pure library interface, &neon; will never produce
+    output on <constant>stdout</constant> or
+    <constant>stderr</constant>; all user interaction is the
+    responsibilty of the application.</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>
+    out-of-memory situation; instead, by default, the
+    <function>abort</function> function is called to immediately
+    terminate the process.  An application may register a custom
+    function which will be called before <function>abort</function> in
+    such a situation; see <xref linkend="ne_oom_callback"/>.</para>
   
   </refsect2>
 
     <title>Callbacks and userdata</title>
 
     <para>Whenever a callback is registered, a
-    <literal>userdata</literal> variable is also used to allow the
+    <literal>userdata</literal> pointer 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>
+
   <refsect1>
     <title>See also</title>
 
-    <para><xref linkend="refsess"/>, <xref linkend="refalloc"/></para>
+    <para><xref linkend="refsess"/>, <xref linkend="ne_oom_callback"/></para>
   </refsect1>
 
 </refentry>
index 85394cb..0dfcd9a 100644 (file)
@@ -47,37 +47,42 @@ 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>
+use for the session given by the <parameter>sess</parameter>
+parameter, combined with the <parameter>path</parameter> parameter.</para>
+
+<para>The <parameter>path</parameter> string used must conform to the
+<literal>abs_path</literal> definition given in RFC2396, with an
+optional "?query" part, and must be URI-escaped by the caller (for
+instance, using <function>ne_path_escape</function>).  If the string
+comes from an untrusted source, failure to perform URI-escaping
+results in a security vulnerability.</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>
+<function>ne_read_response_block</function> functions; see
+<function>ne_begin_request</function>.</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>
+body with the request, one of the functions
+<function>ne_set_request_body_buffer</function>, <xref
+linkend="ne_set_request_body_fd"/>, or
+<function>ne_set_request_body_provider</function> 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
+result of the operation, <xref linkend="ne_get_status"/>, 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
+processing associated with the request object is complete, 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>
@@ -96,13 +101,7 @@ non-zero error code otherwise.</para>
 
       </refsect1>
 
-      <refsect1>
-        <title>Notes</title>
-       
-       <para><!-- TODO: abs_path description --></para>
-
-      </refsect1>
-
+<!-- TODO: abs_path description in a NOTES section -->
 
       <refsect1>
        <title>Errors</title>
@@ -110,32 +109,32 @@ non-zero error code otherwise.</para>
        <variablelist>
          <varlistentry><term><errorcode>NE_ERROR</errorcode></term>
            <listitem>
-             <para>Request failed (see session error string)</para>
+             <simpara>Request failed (see session error string)</simpara>
            </listitem>
          </varlistentry>
          <varlistentry><term><errorcode>NE_LOOKUP</errorcode></term>
            <listitem>
-             <para>The DNS lookup for the server (or proxy server) failed.</para>
+             <simpara>The DNS lookup for the server (or proxy server) failed.</simpara>
            </listitem>
          </varlistentry>
          <varlistentry><term><errorcode>NE_AUTH</errorcode></term>
            <listitem>
-             <para>Authentication failed on the server.</para>
+             <simpara>Authentication failed on the server.</simpara>
            </listitem>
          </varlistentry>
          <varlistentry><term><errorcode>NE_PROXYAUTH</errorcode></term>
            <listitem>
-             <para>Authentication failed on the proxy server.</para>
+             <simpara>Authentication failed on the proxy server.</simpara>
            </listitem>
          </varlistentry>
          <varlistentry><term><errorcode>NE_CONNECT</errorcode></term>
            <listitem>
-             <para>A connection to the server could not be established.</para>
+             <simpara>A connection to the server could not be established.</simpara>
            </listitem>
          </varlistentry>
          <varlistentry><term><errorcode>NE_TIMEOUT</errorcode></term>
            <listitem>
-             <para>A timeout occurred while waiting for the server to respond.</para>
+             <simpara>A timeout occurred while waiting for the server to respond.</simpara>
            </listitem>
          </varlistentry>
        </variablelist>
@@ -146,7 +145,7 @@ non-zero error code otherwise.</para>
        <title>Example</title>
        
        <para>An example of applying a <literal>MKCOL</literal>
-       operation to the resource at the location
+       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);
index 9314880..067cf61 100644 (file)
@@ -10,7 +10,6 @@
     <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>
       </funcprototype>
 
       <funcprototype>
-        <funcdef>ne_inet_addr *<function>ne_addr_first</function></funcdef>
+        <funcdef>const 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>
+        <funcdef>const 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>
     <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>
+    must 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
+    (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>
+    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
@@ -89,7 +81,7 @@
     <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>
+    <parameter>bufsiz</parameter>).</para>
 
     <para>The functions <function>ne_addr_first</function> and
     <function>ne_addr_next</function> are used to retrieve the
     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>
 
 
     <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
+    <function>ne_addr_error</function> returns the
     <parameter>buffer</parameter> parameter .</para>
 
   </refsect1>
@@ -137,10 +124,10 @@ 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;
+    const 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));
+        printf(" %s", ne_iaddr_print(ia, buf, sizeof buf));
     }
     putchar('\n');
 }
@@ -148,13 +135,11 @@ ne_addr_destroy(addr);
 </programlisting>
   </refsect1>
 
-<!--
   <refsect1>
     <title>See also</title>
 
-    <para><xref linkend="ne_sock_connect"/></para>
+    <para><xref linkend="ne_iaddr_print"/></para>
   </refsect1>
--->
 
 </refentry>
 
index b3b0047..93f0038 100644 (file)
@@ -1,8 +1,4 @@
      <refentry id="refsess">
-
-      <refentryinfo>
-       <title>neon</title>
-      </refentryinfo>
       
       <refmeta>
        <refentrytitle>ne_session_create</refentrytitle>
            <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>
+           <paramdef>unsigned 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>
+           <paramdef>unsigned int <parameter>port</parameter></paramdef>
          </funcprototype>
 
          <funcprototype>
            <funcdef>void <function>ne_close_connection</function></funcdef>
-           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
          </funcprototype>
 
          <funcprototype>
        <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>
+session - a logical grouping of a sequence of HTTP requests made to a
+certain server. Any requests made using the session can use a
+persistent connection, share cached authentication credentials and any
+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>
+contact the server (usually <literal>"http"</literal>).  Before the
+first use of <function>ne_session_create</function> in a process,
+<xref linkend="ne_sock_init"/> must have been called to perform any
+global initialization needed by any libraries used by &neon;.</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>
+(see <xref linkend="ne_ssl_set_verify"/>) or trust the appropriate 
+certificate (see <xref linkend="ne_ssl_trust_cert"/>, <xref
+linkend="ne_ssl_trust_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
@@ -88,6 +87,18 @@ session pointer produces undefined behaviour.</para>
       </refsect1>
 
       <refsect1>
+       <title>Notes</title>
+
+        <para>The hostname passed to
+<function>ne_session_create</function> is resolved when the first
+request using the session is dispatched; a DNS resolution failure can
+only be detected at that time (using the <literal>NE_LOOKUP</literal>
+error code); see <xref linkend="ne_request_dispatch"/> for
+details.</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>
@@ -105,7 +116,8 @@ ne_session_destroy(sess);
 
       <refsect1>
        <title>See Also</title>
-       <para><xref linkend="ne_ssl_set_verify"/>, <xref linkend="ne_ssl_load_ca"/></para>
+
+       <para><xref linkend="ne_ssl_set_verify"/>, <xref linkend="ne_ssl_trust_cert"/>, <xref linkend="ne_sock_init"/></para>
       </refsect1>
 
     </refentry>
index 56881c1..914699d 100644 (file)
@@ -7,7 +7,7 @@
 
       <refnamediv>
        <refname>ne_shave</refname>
-       <refpurpose>whitespace trimmer</refpurpose>
+       <refpurpose>trim whitespace from a string</refpurpose>
       </refnamediv>
       
       <refsynopsisdiv>
@@ -32,7 +32,8 @@
        <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>
+<parameter>str</parameter> may be modified.  Note that the return
+value may not be equal to <parameter>str</parameter>.</para>
 
       </refsect1>
 
diff --git a/neon/doc/ref/sslca.xml b/neon/doc/ref/sslca.xml
deleted file mode 100644 (file)
index cbac071..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-    <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>
index 9696a45..d2d0376 100644 (file)
-    <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>
+<refentry id="refcert">
+
+  <refmeta>
+    <refentrytitle>ne_ssl_cert_identity</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname id="ne_ssl_cert_identity">ne_ssl_cert_identity</refname>
+    <refname id="ne_ssl_cert_signedby">ne_ssl_cert_signedby</refname>
+    <refname id="ne_ssl_cert_issuer">ne_ssl_cert_issuer</refname>
+    <refname id="ne_ssl_cert_subject">ne_ssl_cert_subject</refname>
+    <refpurpose>functions to access certificate properties</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_ssl.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>const char *<function>ne_ssl_cert_identity</function></funcdef>
+        <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>const ne_ssl_certificate *<function>ne_ssl_cert_signedby</function></funcdef>
+        <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>const ne_ssl_dname *<function>ne_ssl_cert_subject</function></funcdef>
+        <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>const ne_ssl_dname *<function>ne_ssl_cert_issuer</function></funcdef>
+        <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The function <function>ne_ssl_cert_identity</function>
+    retrieves the <quote>identity</quote> of a certificate; for an
+    SSL server certificate, this will be the hostname for which the
+    certificate was issued.  In PKI parlance, the identity is the
+    <emphasis>common name</emphasis> attribute of the distinguished name of
+    the certificate subject.</para>
+
+    <para>The functions <function>ne_ssl_cert_subject</function> and
+    <function>ne_ssl_cert_issuer</function> can be used to access the
+    objects representing the distinguished name of the subject and of
+    the issuer of a certificate, respectively.</para>
+
+    <para>If a certificate object is part of a certificate chain, then
+    <function>ne_ssl_cert_signedby</function> can be used to find the
+    certificate which signed a particular certificate. For a
+    self-signed certificate or a certificate for which the full chain
+    is not available, this function will return &null;.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+
+    <para><function>ne_ssl_cert_issuer</function> and
+    <function>ne_ssl_cert_subject</function> are guaranteed to never
+    return &null;. <function>ne_ssl_cert_identity</function> may
+    return &null; if the certificate has no specific
+    <quote>identity</quote>. <function>ne_ssl_cert_signedby</function>
+    may return &null; as covered above.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>The following function could be used to display information
+    about a given certificate:</para>
+
+    <programlisting>void dump_cert(const ne_ssl_certificate *cert) {
+  const char *id = ne_ssl_cert_identity(cert);
+  char *dn;
+
+  if (id) 
+    printf("Certificate was issued for '%s'.\n", id);
+
+  dn = ne_ssl_readable_dname(ne_ssl_cert_subject(cert));
+  printf("Subject: %s\n", dn);
+  free(dn);
+
+  dn = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
+  printf("Issuer: %s\n", dn);
+  free(dn);
+}</programlisting>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><xref linkend="ne_ssl_cert_cmp"/>, <xref linkend="ne_ssl_readable_dname"/></para>
+  </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/ref/sslcert2.xml b/neon/doc/ref/sslcert2.xml
new file mode 100644 (file)
index 0000000..216d294
--- /dev/null
@@ -0,0 +1,49 @@
+<refentry id="refsslcert2">
+
+  <refmeta>
+    <refentrytitle>ne_ssl_cert_cmp</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname id="ne_ssl_cert_cmp">ne_ssl_cert_cmp</refname>
+    <refname id="ne_ssl_cert_free">ne_ssl_cert_free</refname>
+    <refpurpose>functions to operate on certificate objects</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_header.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>ne_ssl_cert_cmp</function></funcdef>
+        <paramdef>const ne_ssl_certificate *<parameter>c1</parameter></paramdef>
+        <paramdef>const ne_ssl_certificate *<parameter>c2</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>void <function>ne_ssl_cert_free</function></funcdef>
+        <paramdef>ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>ne_ssl_cert_cmp</function> function can be
+    used to compare two certificate objects; it returns zero if they
+    refer to the same certificate, and non-zero otherwise.</para>
+
+    <para>The <function>ne_ssl_cert_free</function> function can be
+    used to destroy a certificate object when it is no longer
+    needed.</para>
+
+  </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/ref/sslcertio.xml b/neon/doc/ref/sslcertio.xml
new file mode 100644 (file)
index 0000000..394045e
--- /dev/null
@@ -0,0 +1,95 @@
+<refentry id="refsslcertio">
+
+  <refmeta>
+    <refentrytitle>ne_ssl_cert_read</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname id="ne_ssl_cert_read">ne_ssl_cert_read</refname>
+    <refname id="ne_ssl_cert_write">ne_ssl_cert_write</refname>
+    <refname id="ne_ssl_cert_import">ne_ssl_cert_import</refname>
+    <refname id="ne_ssl_cert_export">ne_ssl_cert_export</refname>
+    <refpurpose>functions to read or write certificates to and from files or strings</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_ssl.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>ne_ssl_certificate *<function>ne_ssl_cert_read</function></funcdef>
+        <paramdef>const char *<parameter>filename</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>ne_ssl_cert_write</function></funcdef>
+        <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+        <paramdef>const char *<parameter>filename</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>ne_ssl_certificate *<function>ne_ssl_cert_import</function></funcdef>
+        <paramdef>const char *<parameter>data</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>char *<function>ne_ssl_cert_export</function></funcdef>
+        <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>ne_ssl_cert_write</function> function writes a
+    certificate to a file using the PEM encoding.  The
+    <function>ne_ssl_cert_export</function> function returns a
+    base64-encoded &nul;-terminated string representing the
+    certificate.  This string is malloc-allocated and should be
+    destroyed using <function>free</function> by the caller.</para>
+
+    <para>The <function>ne_ssl_cert_read</function> function reads a
+    certificate from a PEM-encoded file, and returns a certificate
+    object.  The <function>ne_ssl_cert_import</function> function
+    returns a certificate object from a base64-encoded string,
+    <parameter>data</parameter>, as returned by
+    <function>ne_ssl_cert_export</function>.  The certificate object
+    returned by these functions should be destroyed using <xref
+    linkend="ne_ssl_cert_free"/> after use.</para>
+
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+
+    <para><function>ne_ssl_cert_read</function> returns &null; if a
+    certificate could not be read from the file.
+    <function>ne_ssl_cert_write</function> returns non-zero if the
+    certificate could not be written to the file.
+    <function>ne_ssl_cert_export</function> always returns a
+    &nul;-terminated string, and never &null;.
+    <function>ne_ssl_cert_import</function> returns &null; if the
+    string was not a valid base64-encoded certificate.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Encoding Formats</title>
+
+    <para>The string produced by
+    <function>ne_ssl_cert_export</function> is the base64 encoding of
+    the DER representation of the certificate.  The file written by
+    <function>ne_ssl_cert_write</function> uses the PEM format: this
+    is the base64 encoding of the DER representation with newlines
+    every 64 characters, and start and end marker lines.</para>
+  </refsect1>
+
+</refentry>
+
index 70df21a..e1fd454 100644 (file)
@@ -2,27 +2,33 @@
     <refentry id="refssldname">
 
       <refmeta>
-       <refentrytitle>ne_ssl_readable_dname</refentrytitle>
+       <refentrytitle>ne_ssl_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>
+       <refname id="ne_ssl_readable_dname">ne_ssl_readable_dname</refname>
+        <refname id="ne_ssl_dname_cmp">ne_ssl_dname_cmp</refname>
+       <refpurpose>SSL distinguished name handling</refpurpose>
       </refnamediv>
       
       <refsynopsisdiv>
        
        <funcsynopsis>
 
-         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+         <funcsynopsisinfo>#include &lt;ne_ssl.h&gt;</funcsynopsisinfo>
 
          <funcprototype>
            <funcdef>const char *<function>ne_ssl_readable_dname</function></funcdef>
            <paramdef>const ne_ssl_dname *<parameter>dname</parameter></paramdef>
          </funcprototype>
 
+         <funcprototype>
+           <funcdef>int <function>ne_ssl_dname_cmp</function></funcdef>
+           <paramdef>const ne_ssl_dname *<parameter>dn1</parameter></paramdef>
+           <paramdef>const ne_ssl_dname *<parameter>dn2</parameter></paramdef>
+         </funcprototype>
+
        </funcsynopsis>
        
       </refsynopsisdiv>
@@ -36,18 +42,32 @@ creates a single-line, human-readable string out of an
 <function>malloc</function>()-allocated, and must be
 <function>free</function>()d by the caller.</para>
 
+       <para>The <function>ne_ssl_dname_cmp</function> function
+       compares two distinguished names, and returns zero if they are
+       equal, or non-zero otherwise.</para>
+
       </refsect1>
 
       <refsect1>
        <title>Return value</title>
 
-       <para><function>malloc</function>-allocated string.</para>
+       <para><function>ne_ssl_readable_dname</function> returns a <function>malloc</function>-allocated
+        string, and never &null;.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+        <para>See <xref linkend="ne_ssl_cert_subject"/> for an example
+        use of <function>ne_ssl_readable_dname</function>.</para>
+
       </refsect1>
 
       <refsect1>
        <title>See also</title>
 
-       <para><xref linkend="ne_ssl_certificate"/></para>
+       <para><xref linkend="ne_ssl_cert_subject"/></para>
       </refsect1>
 
     </refentry>
diff --git a/neon/doc/ref/ssltrust.xml b/neon/doc/ref/ssltrust.xml
new file mode 100644 (file)
index 0000000..5a22f50
--- /dev/null
@@ -0,0 +1,72 @@
+    <refentry id="refsslca">
+
+      <refmeta>
+       <refentrytitle>ne_ssl_trust_cert</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_ssl_trust_cert">ne_ssl_trust_cert</refname>
+       <refname id="ne_ssl_trust_default_ca">ne_ssl_trust_default_ca</refname>
+       <refpurpose>functions to indicate that certificates are trusted</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_ssl_trust_cert</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_ssl_trust_default_ca</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>To indicate that a given certificate is trusted by the
+user, the certificate object can be passed to
+<function>ne_ssl_trust_cert</function>.  The certificate object is
+duplicated internally and can subequently be destroyed.</para>
+
+       <para>The SSL library in use by &neon; may include a default
+set of CA certificates; calling the
+<function>ne_ssl_trust_default_ca</function> function will indicate
+that these CAs are trusted by the user.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>Load the CA certificate stored in <filename>/path/to/cacert.pem</filename>:</para>
+       <programlisting>&egsess;
+ne_ssl_certificate *cert = ne_ssl_cert_read("/path/to/cacert.pem");
+
+if (cert) {
+   ne_ssl_trust_cert(sess, cert);
+   ne_ssl_cert_free(cert);
+} else {
+   printf("Could not load CA cert: %s\n", ne_get_error(sess));
+}</programlisting>
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_ssl_cert_read"/>, <xref
+       linkend="ne_ssl_cert_import"/>, <xref
+       linkend="ne_ssl_cert_free"/></para> </refsect1>
+
+    </refentry>
index ade5028..b044d5b 100644 (file)
@@ -1,4 +1,4 @@
-    <refentry id="refsslvfy">
+    <refentry id="refsslvfy"> <!-- -*- xml-mode -*- -->
 
       <refmeta>
        <refentrytitle>ne_ssl_set_verify</refentrytitle>
@@ -19,7 +19,7 @@
          <!-- hard to put data type declarations here -->
 
          <funcprototype>
-           <funcdef>typedef int (*<function>ne_ssl_verify_fn</function>)</funcdef>
+           <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>
@@ -44,7 +44,7 @@ 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
+linkend="ne_ssl_trust_cert"/>), or if the certificate presented is invalid in
 some way, the connection will fail.</para>
 
        <para>When the callback is invoked, the
@@ -54,35 +54,42 @@ 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>
+         <varlistentry><term><constant>NE_SSL_NOTYETVALID</constant></term>
            <listitem>
-             <para>The certificate is not yet valid.</para>
+             <simpara>The certificate is not yet valid.</simpara>
            </listitem>
          </varlistentry>
-         <varlistentry><term><filename>NE_SSL_EXPIRED</filename></term>
+         <varlistentry><term><constant>NE_SSL_EXPIRED</constant></term>
            <listitem>
-             <para>The certificate has expired.</para>
+             <simpara>The certificate has expired.</simpara>
            </listitem>
          </varlistentry>
-         <varlistentry><term><filename>NE_SSL_CNMISMATCH</filename></term>
+         <varlistentry><term><constant>NE_SSL_IDMISMATCH</constant></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>
+             <simpara>The hostname used for the session does not match
+the hostname to which the certificate was issued.</simpara>
            </listitem>
          </varlistentry>
-         <varlistentry><term><filename>NE_SSL_UNKNOWNCA</filename></term>
+         <varlistentry><term><constant>NE_SSL_UNTRUSTED</constant></term>
            <listitem>
-             <para>The Certificate Authority which signed the certificate
-is not trusted.</para>
+             <simpara>The Certificate Authority which signed the certificate
+is not trusted.</simpara>
            </listitem>
          </varlistentry>
        </variablelist>
 
+       <para>Note that if either of the
+       <constant>NE_SSL_IDMISMATCH</constant> or
+       <constant>NE_SSL_UNTRUSTED</constant> failures is given, the
+       connection may have been intercepted by a third party, and
+       must not be presumed to be <quote>secure</quote>.</para>
+
        <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>
+callback represents the certificate which was presented by the server.
+If the server presented a chain of certificates, the chain can be
+accessed using <xref linkend="ne_ssl_cert_signedby"/>.  The
+<parameter>cert</parameter> object given is not valid after the
+callback returns.</para>
 
       </refsect1>
 
@@ -97,35 +104,51 @@ which case, the connection will fail).</para>
       <refsect1>
        <title>Examples</title>
 
-       <para>Manual certificate verification:</para>
+       <para>The following code implements an example verification
+       callback, using the <function>dump_cert</function> function
+       from <xref linkend="ne_ssl_cert_subject"/> to display
+       certification information.  Notice that the hostname of the
+       server used for the session is passed as the
+       <parameter>userdata</parameter> parameter to the
+       callback.</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 */
+  const char *hostname = userdata;
+
+  dump_cert(cert);
+
+  puts("Certificate verification failed - the connection may have been "
+       "intercepted by a third party!");
+
+  if (failures &amp; NE_SSL_IDMISMATCH) { 
+    const char *id = ne_ssl_cert_identity(cert);
+    if (id) 
+      printf("Server certificate was issued to '%s' not '%s'.\n",
+             id, hostname);
+    else
+      printf("The certificate was not issued for '%s'\n", hostname);
+  }
+
+  if (failures &amp; NE_SSL_UNTRUSTED)
+    puts("The certificate is not signed by a trusted Certificate Authority.");
+
+  /* ... check for validity failures ... */
+
+  if (prompt_user())
+    return 1; /* fail verification */
+  else
+    return 0; /* trust the certificate anyway */
 }
 
 int
 main(...)
 {
-    ne_session *sess = ne_session_create("https", "some.host.name", 443);
-    ne_ssl_set_verify(sess, my_verify, NULL);
-    ...
+  ne_session *sess = ne_session_create("https", "some.host.name", 443);
+  ne_ssl_set_verify(sess, my_verify, "some.host.name");
+  ...
 }</programlisting>
 
       </refsect1>
@@ -133,8 +156,8 @@ main(...)
       <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>
+       <para><xref linkend="ne_ssl_trust_cert"/>, <xref
+       linkend="ne_ssl_readable_dname"/>, <xref linkend="ne_ssl_cert_subject"/></para>
       </refsect1>
 
-    </refentry>
+   </refentry>
index 0290ac2..fbc38b7 100644 (file)
       
       <refsynopsisdiv>
        
-       <programlisting>#include &lt;ne_utils.h&gt;
+       <funcsynopsis><funcsynopsisinfo>#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>
+} <type>ne_status</type>;</funcsynopsisinfo></funcsynopsis>
        
       </refsynopsisdiv>
 
@@ -32,8 +32,12 @@ The <structfield>major_version</structfield> and
 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>
+<structfield>klass</structfield> field gives the
+class<footnote><para>the field is named <quote>klass</quote> not
+<quote>class</quote> so that the header can be used from a C++
+program, in which <quote>class</quote> is a reserved
+word)</para></footnote>, 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>
@@ -41,35 +45,34 @@ to the most significant digit of the status.</para>
        <variablelist>
          <varlistentry>
            <term><literal>1xx</literal></term>
-           <listitem><para>Informational response.</para></listitem>
+           <listitem><simpara>Informational response.</simpara></listitem>
          </varlistentry>
 
          <varlistentry>
            <term><literal>2xx</literal></term>
-           <listitem><para>Success: the operation was successful</para></listitem>
+           <listitem><simpara>Success: the operation was successful</simpara></listitem>
          </varlistentry>
 
          <varlistentry>
            <term><literal>3xx</literal></term>
-           <listitem><para>Redirection</para></listitem>
+           <listitem><simpara>Redirection</simpara></listitem>
          </varlistentry>
 
          <varlistentry>
-           <term><literal>4xx</literal></term> <listitem><para>Client
+           <term><literal>4xx</literal></term> <listitem><simpara>Client
            error: the request made was incorrect in some
-           manner.</para></listitem>
+           manner.</simpara></listitem>
          </varlistentry>
 
          <varlistentry>
            <term><literal>5xx</literal></term>
-           <listitem><para>Server error</para></listitem>
+           <listitem><simpara>Server error</simpara></listitem>
          </varlistentry>
        </variablelist>
 
       </refsect1>
 
       <refsect1> <title>See also</title> <para><xref
-linkend="ne_get_status"/>, <xref
-linkend="ne_parse_status_line"/></para> </refsect1>
+linkend="ne_get_status"/>.</para> </refsect1>
 
     </refentry>
index 9d180fe..2e7a50d 100644 (file)
@@ -38,7 +38,9 @@
     <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>
+    <parameter>minor</parameter>.  For &neon; versions 0.x, every
+    minor version is assumed to be incompatible with every other minor
+    version.</para>  <!-- TODO: remove that for 1.0 -->
 
     <para>The <function>ne_version_string</function> function returns
     a string giving the library version.</para>
diff --git a/neon/doc/ssl.xml b/neon/doc/ssl.xml
new file mode 100644 (file)
index 0000000..5581373
--- /dev/null
@@ -0,0 +1,24 @@
+<sect1 id="ssl"> <!-- -*- xml -*- -->
+
+ <title>Secure connections: HTTP over SSL</title>
+
+ <para>This section gives an introduction to SSL.  The text is
+ inspired by <xref linkend="bib.ssltls"/>.</para>
+
+ <para>&neon; supports the use of HTTP over SSL<footnote><para>The
+term <quote>SSL</quote> is used throughout this section to refer in
+general to both the SSL protocol developed by Netscape and its
+successor TLS, as adopted by the IETF.</para></footnote> to implement
+<firstterm>secure connections</firstterm>.  A secure connection in
+this context means a connection which has
+<emphasis>integrity</emphasis>, <emphasis>secrecy</emphasis> and is
+<emphasis>authenticated</emphasis>.  Applications must go to some
+effort to correctly support secure connections&mdash;an application
+based on &neon; does not magically become <quote>secure</quote> simply
+by flicking a switch and enabling the use of SSL.</para>
+
+<!-- SSL: integrity, secrecy, authentication. -->
+
+<!-- what is a certificate -->
+
+</sect1>
diff --git a/neon/doc/using.xml b/neon/doc/using.xml
new file mode 100644 (file)
index 0000000..efb53d1
--- /dev/null
@@ -0,0 +1,119 @@
+    <sect1 id="using">
+      <title>How to use neon from your application</title>
+
+      <para>This section describes how to add &neon; support to an
+      application.  If you just want to quickly try out &neon;, use
+      the <xref linkend="refconfig"/> script.</para>
+
+      <para>The &neon; source code is designed to be easily embedded
+      into an application source tree.  &neon; has no dependencies on
+      libraries other than an SSL toolkit and XML parser, though the
+      source tree can be configured to have no support for SSL or XML
+      if desired.  To configure the &neon; source code some <ulink
+      url="http://www.gnu.org/software/autoconf/">GNU autoconf</ulink>
+      macros are supplied, which can be used in a number of ways, as
+      follows:</para>
+      
+      <itemizedlist>
+       <listitem>
+         
+         <para>autoconf macros are distributed in the 'macros'
+         subdirectory of the neon distribution.  Use the NEON_LIBRARY
+         macro 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>
+         
+         <programlisting>NEON_BUNDLED(libneon, ...)</programlisting>
+         
+         <para>If your application supports builds where srcdir != builddir,
+         you should use the NEON_VPATH_BUNDLED macro like this:</para>
+         
+         <programlisting>NEON_VPATH_BUNDLED(${srcdir}/libneon, libneon, ...)</programlisting>
+         
+         <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>
+       
+<programlisting>NEON_BUNDLED(libneon, [
+  NEON_NORMAL_BUILD
+  NEON_XML_PARSER
+  SUBDIRS="libneon $SUBDIRS"
+])</programlisting>
+       
+       <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 id="compliance">
+       <title>Protocol compliance</title>
+       
+       <para>&neon; is intended to be compliant with the IETF
+       protocol standards it implements, with a few exceptions where
+       real-world use has necessitated minor deviations.  These
+       exceptions are documented in this section.</para>
+
+       <sect2><title>RFC 2518, HTTP Extensions for Distributed Authoring&mdash;WebDAV</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 correct behaviour by the WebDAV
+       working group, and is likely to formally adopted in a future
+       revision of the specification.</para></sect2>
+       
+        <sect2><title>RFC 2616, Hypertext Transfer Protocol&mdash;HTTP/1.1</title>
+        
+        <para>There is some confusion in this specification about the
+        use of the <quote>identity</quote>
+        <firstterm>transfer-coding</firstterm>.  &neon; treats the
+        presence of <emphasis>any</emphasis>
+        <literal>Transfer-Encoding</literal> response header as an
+        indication that the response message uses the
+        <quote>chunked</quote> transfer-coding.  This was the
+        suggested resolution <ulink
+        url="http://lists.w3.org/Archives/Public/ietf-http-wg-old/2001SepDec/0018.html">proposed
+        by Larry Masinter</ulink>.</para></sect2>
+
+        <sect2>
+        <title>RFC 2617, HTTP Authentication: Basic and Digest Access Authentication</title>
+
+        <para>&neon; is not strictly compliant with the quoting rules
+        given in the grammar for the <literal>Authorization</literal>
+        header.  The grammar requires that the <literal>qop</literal>
+        and <literal>algorithm</literal> parameters are not quoted,
+        however one widely deployed server implementation
+        (Microsoft&reg; IIS 5) rejects the request if these parameters
+        are not quoted.  &neon; sends these parameters with
+        quotes&mdash;this is not known to cause any problems with
+        other server implementations.</para></sect2>
+
+    </sect1>
diff --git a/neon/doc/version.xml b/neon/doc/version.xml
new file mode 100644 (file)
index 0000000..1380da0
--- /dev/null
@@ -0,0 +1 @@
+0.24.7
\ No newline at end of file
diff --git a/neon/doc/xml.xml b/neon/doc/xml.xml
new file mode 100644 (file)
index 0000000..4292eb1
--- /dev/null
@@ -0,0 +1,206 @@
+<!-- neon XML interface -*- text -*- -->
+
+<sect1 id="xml">
+
+  <title>Parsing XML</title>
+
+  <para>The &neon; XML interface is exposed by the
+  <filename>ne_xml.h</filename> header file.  This interface gives a
+  wrapper around the standard <ulink
+  url="http://www.saxproject.org/">SAX</ulink> API used by XML
+  parsers, with an additional abstraction, <firstterm>stacked SAX
+  handlers</firstterm>, and also giving consistent <ulink
+  url="http://www.w3.org/TR/REC-xml-names">XML Namespace</ulink> support.</para>
+
+<sect2 id="xml-sax">
+  <title>Introduction to SAX</title>
+
+  <para>A SAX-based parser works by emitting a sequence of
+  <firstterm>events</firstterm> to reflect the tokens being parsed
+  from the XML document.  For example, parsing the following document
+  fragment:
+
+<programlisting><![CDATA[
+<hello>world</hello>
+]]></programlisting>
+
+  results in the following events:
+
+  <orderedlist>
+    <listitem>
+      <simpara>&startelm; "hello"</simpara>
+    </listitem>
+    <listitem>
+      <simpara>&cdata; "world"</simpara>
+    </listitem>
+    <listitem>
+      <simpara>&endelm; "hello"</simpara>
+    </listitem>
+  </orderedlist>
+
+  This example demonstrates the three event types used used in the
+  subset of SAX exposed by the &neon; XML interface: &startelm;,
+  &cdata; and &endelm;.  In a C API, an <quote>event</quote> is
+  implemented as a function callback; three callback types are used in
+  &neon;, one for each type of event.</para>
+
+</sect2>
+
+<sect2 id="xml-stacked">
+  <title>Stacked SAX handlers</title>
+
+  <para>WebDAV property values are represented as fragments of XML,
+  transmitted as parts of larger XML documents over HTTP (notably in
+  the body of the response to a <literal>PROPFIND</literal> request).
+  When &neon; parses such documents, the SAX events generated for
+  these property value fragments may need to be handled by the
+  application, since &neon; has no knowledge of the structure of
+  properties used by the application.</para>
+
+  <para>To solve this problem<footnote><para>This
+  <quote>problem</quote> only needs solving because the SAX interface
+  is so inflexible when implemented as C function callbacks; a better
+  approach would be to use an XML parser interface which is not based
+  on callbacks.</para></footnote> the &neon; XML interface introduces
+  the concept of a <firstterm>SAX handler</firstterm>.  A SAX handler
+  comprises a &startelm;, &cdata; and &endelm; callback; the
+  &startelm; callback being defined such that each handler may
+  <emphasis>accept</emphasis> or <emphasis>decline</emphasis> the
+  &startelm; event.  Handlers are composed into a <firstterm>handler
+  stack</firstterm> before parsing a document.  When a new &startelm;
+  event is generated by the XML parser, &neon; invokes each &startelm;
+  callback in the handler stack in turn until one accepts the event.
+  The handler which accepts the event will then be subsequently be
+  passed &cdata; events if the element contains character data,
+  followed by an &endelm; event when the element is closed.  If no
+  handler in the stack accepts a &startelm; event, the branch of the
+  tree is ignored.</para>
+
+  <para>To illustrate, given a handler A, which accepts the
+  <literal>cat</literal> and <literal>age</literal> elements, and a
+  handler B, which accepts the <literal>name</literal> element, the
+  following document:
+
+<example id="xml-example">
+<title>An example XML document</title>
+<programlisting><![CDATA[
+<cat>
+  <age>3</age>    
+  <name>Bob</name>
+</cat>
+]]></programlisting></example>
+
+  would be parsed as follows:
+  
+  <orderedlist>
+    <listitem>
+      <simpara>A &startelm; "cat" &rarr; <emphasis>accept</emphasis></simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &startelm; "age" &rarr; <emphasis>accept</emphasis></simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &cdata; "3"</simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &endelm; "age"</simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &startelm; "name" &rarr; <emphasis>decline</emphasis></simpara>
+    </listitem>
+    <listitem>
+      <simpara>B &startelm; "name" &rarr; <emphasis>accept</emphasis></simpara>
+    </listitem>
+    <listitem>
+      <simpara>B &cdata; "Bob"</simpara>
+    </listitem>
+    <listitem>
+      <simpara>B &endelm; "name"</simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &endelm; "cat"</simpara>
+    </listitem>
+  </orderedlist></para>
+
+  <para>The search for a handler which will accept a &startelm; event
+  begins at the handler of the parent element and continues toward the
+  top of the stack.  For the root element, it begins at the base of
+  the stack.  In the above example, handler A is at the base, and
+  handler B at the top; if the <literal>name</literal> element had any
+  children, only B's &startelm; would be invoked to accept
+  them.</para>
+
+</sect2>
+
+<sect2 id="xml-state">
+  <title>Maintaining state</title>
+
+  <para>To facilitate communication between independent handlers, a
+  <firstterm>state integer</firstterm> is associated with each element
+  being parsed.  This integer is returned by &startelm; callback and
+  is passed to the subsequent &cdata; and &endelm; callbacks
+  associated with the element.  The state integer of the parent
+  element is also passed to each &startelm; callback, the value zero
+  used for the root element (which by definition has no
+  parent).</para>
+
+  <para>To further extend <xref linkend="xml-example"/>: if handler A
+  defines that the state of the root element <sgmltag>cat</sgmltag>
+  will be <literal>42</literal>, the event trace would be as
+  follows:
+
+  <orderedlist>
+    <listitem>
+      <simpara>A &startelm; (parent = 0, "cat") &rarr;
+      <emphasis>accept</emphasis>, state = 42
+      </simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &startelm; (parent = 42, "age") &rarr; 
+      <emphasis>accept</emphasis>, state = 50
+      </simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &cdata; (state = 50, "3")</simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &endelm; (state = 50, "age")</simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &startelm; (parent = 42, "name") &rarr; 
+      <emphasis>decline</emphasis></simpara>
+    </listitem>
+    <listitem>
+      <simpara>B &startelm; (parent = 42, "name") &rarr;
+      <emphasis>accept</emphasis>, state = 99</simpara>
+    </listitem>
+    <listitem>
+      <simpara>B &cdata; (state = 99, "Bob")</simpara>
+    </listitem>
+    <listitem>
+      <simpara>B &endelm; (state = 99, "name")</simpara>
+    </listitem>
+    <listitem>
+      <simpara>A &endelm; (state = 42, "cat")</simpara>
+    </listitem>
+  </orderedlist></para>
+
+  <para>To avoid collisions between state integers used by different
+  handlers, the interface definition of any handler includes the range
+  of integers it will use.</para>
+
+</sect2>
+
+<sect2 id="xml-ns">
+  <title>XML namespaces</title>
+
+  <para>To support XML namespaces, every element name is represented
+  as a <emphasis>(namespace, name)</emphasis> pair.  The &startelm;
+  and &endelm; callbacks are passed namespace and name strings
+  accordingly.  If an element in the XML document has no declared
+  namespace, the namespace given will be the empty string,
+  <literal>""</literal>.</para>
+
+</sect2>
+
+</sect1>
index 9a59cc6..24e0824 100644 (file)
@@ -1,3 +1,965 @@
+Sat Jul  3 11:39:01 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Pick up gethostbyname in
+       -lsocket for QNX.
+
+Wed May 19 08:36:44 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Declare stpcpy on modern
+       "Linux-like" AIXes.
+
+Fri Apr 16 11:43:10 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): If built using libtool,
+       and --with-expat specifies a libexpat.la file, add the libexpat.la
+       to NEON_LTLIBS.
+
+       * neon.m4 (NEON_COMMON_CHECKS): Substitute NEON_LTLIBS.
+
+Tue Apr 13 20:51:59 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_GSSAPI): Check for presence of
+       gssapi/gssapi_generic.h.
+
+Thu Nov 13 20:25:28 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for gethostbyname in
+       -lbind too.
+
+Tue Oct  7 21:20:16 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-test.m4 (NEON_TEST): Check for shutdown.
+
+Mon Sep 22 21:20:37 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_CHECK_FUNCS, NE_SEARCH_LIBS): Avoid clobbering
+       ne_save_{LIBS,CPPFLAGS}.
+
+Sun Sep 14 10:51:34 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for working AI_ADDRCONFIG
+       flag for getaddrinfo().
+
+Wed Sep 10 21:45:10 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_GSSAPI): Restore CPPFLAGS and NEON_LIBS if GSSAPI
+       detection fails.
+
+Thu Sep  4 21:29:06 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for netinet/tcp.h.
+
+Wed Jul 23 21:17:40 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_GSSAPI): New macro.
+
+Mon Apr 21 18:24:12 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (HAVE_EXPAT): Fail if --with-expat is given
+       but expat.h is not found.
+
+Wed Mar 26 20:29:11 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): And add ne_stubssl to NEON_EXTRAOBJS for
+       non-SSL build.
+
+Tue Mar 25 20:43:01 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Add ne_openssl to NEON_EXTRAOBJS.
+
+Mon Mar 17 20:34:55 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for netdb.h.
+
+Sun Mar 16 14:22:02 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NE_XML_BUNDLED_EXPAT): Define
+       HAVE_XMLPARSE_H.
+
+Sun Mar 16 11:47:35 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Also fix
+       non-included-expat build for when included expat is not an option.
+
+Sun Mar 16 11:20:23 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Fix non-included-expat
+       build broken in previous commit.
+
+Sun Mar 16 09:06:41 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Fix --with-included-expat
+       support.
+
+Sun Mar  9 10:08:57 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Fail if --with-ssl is given an argument.
+
+Sun Mar  9 08:55:04 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_SEARCH_LIBS): Bug fix to always compare against
+       `extras' if given.  Add support for optional `actions-if-found'
+       argument.
+
+       * neon-xml-parser.m4: Largely rewritten. Drop support for
+       libxml 1.x; require expat 1.95.x.
+
+Sun Mar  9 08:50:22 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for stpcpy.
+
+Mon Mar  3 22:15:56 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_CHECK_FUNCS): Check whether h_errno is declared.
+
+Wed Feb 19 21:35:20 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_FORMAT): Revert use of 'z' modifier; breaks on
+       CygWin.
+
+Tue Jan 14 17:06:07 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_FORMAT): Prefer the C99 'z' modifier to printf
+       size_t/ssize_t values where available.
+
+Fri Jan  3 23:12:20 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_PKG_CONFIG): New macro.
+       (NEON_SSL): Use NE_PKG_CONFIG rather than PKG_CHECK_MODULES.
+
+Mon Dec 16 20:02:45 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Only check for OpenSSL <=0.9.6 if version is
+       known to be <=0.9.7.
+
+Mon Dec 16 19:01:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Use pkg-config data to determine location OpenSSL
+       libraries/headers, if available.
+
+Tue Nov 19 11:21:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-test.m4 (NEON_TEST): Suggest this macro is run before
+       NEON_XML_PARSER.
+
+Mon Oct  7 22:22:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_CHECK_SSLVER): New macro.
+       (NEON_SSL): Use NE_CHECK_SSLVER.  Add --with-egd argument;
+       conditionally enable EGD, optionally using only a specific EGD
+       socket path, and only if using OpenSSL before 0.9.7.
+
+Tue Sep 24 21:36:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_ZLIB_VERSION): Removed macro.
+       (NEON_ZLIB): Removed --with-force-zlib flag; don't check zlib
+       version.
+       (NEON_SSL): Removed --with-force-ssl flag, only require OpenSSL
+       0.9.6 or later.
+
+Sat Aug 31 17:28:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_FORMAT_PREP): Add check for gcc -Wformat -Werror
+       sanity.
+       (NEON_FORMAT): Only use gcc -Wformat -Werror if sanity is assured.
+
+Fri Aug 30 22:07:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Set NEON_INTERFACE_VERSION according to
+       NEON_VERSION_MINOR, NEON_VERSION_RELEASE, for the duration of neon
+       0.x releases.
+
+Sun Aug 25 23:52:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_CHECK_FUNCS): New macro.
+       (LIBNEON_SOURCE_CHECKS): Use NE_CHECK_FUNCS rather than
+       AC_CHECK_FUNCS, so $NEON_LIBS are searched.
+
+Sun Aug 25 11:53:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Require inet_ntop as well for
+       USE_GETADDRINFO test.   
+
+Sun Aug 18 22:50:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Remove redundant check for
+       SIGPIPE definition.
+
+Sun Aug 18 22:41:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_SEARCH_LIBS): Take optional fourth argument.
+       (LIBNEON_SOURCE_CHECKS): Define USE_GETADDRINFO if getaddrinfo and
+       gai_strerror are present.  Otherwise, check for hstrerror,
+       possibly in -lresolv.
+
+       * neon-test.m4: Don't check for hstrerror().
+
+Sun Aug 18 22:31:13 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_FIND_PARSER_H, NEON_XML_LIBXML2):
+       Re-order checks to skip redundant tests, speeding up common case.
+
+Fri Aug  9 19:47:14 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Require OpenSSL 0.9.6f or later.
+
+Wed Jul 31 23:01:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * strftime.m4, readline.m4: Removed from neon/macros CVS module.
+
+Tue Jul 30 19:09:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON_CHECKS): Require AC_TYPE_SIZE_T,
+       AC_TYPE_OFF_T.
+
+       * neon-test.m4 (NEON_TEST): Require AC_TYPE_PID_T.
+
+Tue Jul 30 19:06:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Require OpenSSL 0.9.6e or later for security
+       fixes; add --with-force-ssl to override version check.
+
+Fri Jul 26 00:01:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_MACOSX): Just check for any Darwin system.
+
+Sat Jul 20 10:40:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_WITH_LIBS): Allow a colon-separated list of
+       directories for --with-libs argument.
+
+Thu Jul 18 20:07:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Require AC_FUNC_STRERROR_R.
+
+Wed Jul 17 23:26:51 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_CHECK_VERSION): Rewrite to cache results,
+       simplify, and use new ne_version_match.
+       (NE_ZLIB_VERSION): Cache result.
+       
+Fri Jul  5 12:57:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Fail if openssl/opensslv.h header is not
+       found, or if OpenSSL version is earlier than 0.9.6.
+
+Sun Jun 16 14:04:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_FORMAT): Take optional third argument giving 
+       format string specifier to use.
+       (NEON_COMMON_CHECKS): Get format string for ssize_t; fix to
+       use 'u' specifier for size_t format string.
+
+Thu Jun 13 20:34:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Undocument use of =DIR
+       parameter to --with-expat, as it gives false expectations.
+
+Wed Jun 12 23:26:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_WITH_LIBS): New macro.
+
+Mon Jun 10 22:31:50 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_WARNINGS): Replace -ansi-pedantic with -pedantic.
+
+Sun May 26 19:08:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_LIBXML2): Check for
+       libxml/xmlversion.h header too.
+
+Wed May 22 09:54:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_MACOSX): Cache result.
+       (NE_COMMON_CHECKS): Simplify tm_gmtoff check further: use
+       AC_CHECK_MEMBERS.
+
+Mon May 20 21:18:06 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_SEARCH_LIBS): Cache results.
+
+Mon May 20 20:55:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Use AC_CACHE_CHECK to check for
+       SIGPIPE in signal.h; don't invade the ac_ namespace with cache
+       variables.  Cache results of tm_gmtoff test too.
+
+Mon May 20 20:35:22 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_SNPRINTF): Simplify logic.
+
+Sun May 19 20:23:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_WARNINGS): Remove with_warnings variable;
+       simplify.
+
+Sun May 19 09:35:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_FIND_AR): Fix $PATH handling on some Linux
+       platforms.
+
+Sun May 19 09:05:22 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_FIND_AR): New macro.
+       (NEON_NORMAL_BUILD): Require NE_FIND_AR.
+
+       * neon-test.m4: Require NE_FIND_AR. Check for hstrerror().
+
+Fri May 17 23:37:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Allow --without-ssl again.
+
+Wed May 15 21:00:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_LIBXML2): sed '-L/usr/lib ' out of
+       xml2-config --libs output.
+
+Sat May 11 15:30:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SOCKS): Moved and simplified from neon-socks.m4.
+       Drop support for specifying directory argument; fail if
+       --with-socks is given and socks.h is not found.
+
+       * neon-socks.m4: Removed file.
+
+Sat May 11 15:22:36 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_DEBUG): Moved and simplified from neon-debug.m4.
+
+       * neon-debug.m4: Removed file.
+
+Sat May 11 13:40:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_WARNINGS): Moved and simplified from
+       neon-warnings.m4.
+
+       * neon-warnings.m4: Removed file.
+
+Sat May 11 13:26:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_SSL): Simplified version of NEON_SSL from
+       neon-ssl.m4.  Check for ssl.h; detect OpenSSL ENGINE correctly
+       when -lcrypto requries -ldl.
+
+       * neon-ssl.m4: Removed file.
+
+Sat May 11 13:16:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_SEARCH_LIBS): Allow passing 'extralibs' to include
+       in library list when link against a specified library fails.
+       Prepend found library/libraries to $NEON_LIBS.
+
+Sat May 11 12:40:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER),
+       * neon.m4 (NEON_REPLACE_SNPRINTF): 
+       Use AC_LIBOBJ rather than modify LIBOBJS directly, to appease
+       autoconf 2.53.
+
+Wed May  1 22:32:10 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_LIBXML1): Fix syntax error in
+       libxml 1.x detection causing spurious or missing warning message.
+
+Thu Apr 25 07:38:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_EXTERNAL_EXPAT): Check for expat.h
+       too, to support expat 1.95.x (Branko Ãˆibej).
+
+Tue Apr 23 21:09:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_FIND_PARSER_H): New macro, factored out
+       from NEON_XML_LIBXML2.
+       (NEON_XML_LIBXML2, NEON_XML_LIBXML1): Use it.
+
+Tue Apr 23 20:54:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_LIBXML2): Check for parser.h or
+       libxml/parser.h, or fail.
+
+Sat Apr 13 22:35:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_SNPRINTF): Define NEON_TRIO in NEON_CFLAGS, export
+       it from 'neon-config --cflags' output.
+
+Fri Apr  5 23:40:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_ZLIB, NE_ZLIB_VERSION): Add --with-force-zlib, to
+       skip zlib version check.  Simplify neon_zlib_message handling a
+       little.
+
+Tue Mar 12 00:18:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_ZLIB_VERSION): New macro.
+       (NEON_ZLIB): Use it to require zlib 1.1.4.
+
+Sun Mar 10 22:05:26 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_ZLIB): Don't add ne_compress to NEON_EXTRAOBJS.
+
+Mon Mar  4 21:04:28 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-ssl.m4 (NEON_SSL): Avoid adding -I/usr/include to CFLAGS
+       during build as well as not exporting it via neon-config.
+
+Tue Feb 19 21:30:50 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-socks.m4 (NEON_SOCKS): Fix configure argument name, export
+       -L argument in NEON_LIBS, check for socks.h not sock.h, define
+       NEON_SOCKS.
+
+Sun Jan 13 20:07:51 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_MACOSX): New macro.
+       (NEON_COMMON_CHECKS): Call it.
+
+Sun Jan  6 21:35:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Add XML_BYTE_ORDER to
+       CPPFLAGS; mini-expat doesn't pick up config.h.
+
+Tue Jan  1 23:30:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Set XML_BYTE_ORDER
+       appropriately.
+
+Tue Jan  1 22:50:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_CHECK_VERSION): Don't add libs for external neon
+       to NEON_LIBS here.
+
+Tue Jan  1 22:44:05 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_LIBXML2, NEON_XML_LIBXML1): Alter
+       CPPFLAGS only, not CFLAGS.
+
+Tue Jan  1 21:49:16 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_USE_EXTERNAL): Factored out from NEON_COMMON.
+       (NEON_COMMON): Use NEON_USE_EXTERNAL; simplify, improve reporting.
+       
+       * neon.m4 (NEON_COMMON_CHECKS): Move check for common headers
+       here...  (LIBNEON_SOURCE_CHECKS): from here.
+
+Tue Jan  1 21:44:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-test.m4 (NEON_TEST): Check for AR and RANLIB.
+
+Fri Dec 14 22:39:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-test.m4 (NEON_TEST): Pick up time_t definition from
+       sys/time.h if present (fix for Solaris 2.6 and probably
+       elsewhere).
+
+Fri Dec 14 22:39:32 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_FORMAT): Allow passing in headers where type may
+       be defined.
+
+Mon Dec 10 07:36:26 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON): Fix --with-neon=PATH again.
+
+Sun Dec  9 21:40:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-test.m4 (NEON_TEST): Determine how to print time_t.
+
+Sun Dec  9 11:50:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_WITHOUT_ACL): New macro.
+       (LIBNEON_SOURCE_CHECKS): Conditionally build ACL support.
+
+Sun Dec  9 01:06:32 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_CHECK_VERSION): Use NEON_CONFIG as config script,
+       drop first argument.  Better error message if the link failed.
+       (NEON_COMMON): Cleanup.  Check for neon-config in PATH.  Stop if
+       --with-neon was given, and the external neon wasn't good enough.
+
+Sun Dec  9 00:17:19 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-test.m4 (NEON_TEST): Requires NEON_COMMON_CHECKS.
+
+       * neon-warnings.m4 (NEON_WARNINGS): Requires AC_PROG_CC.
+
+Sun Dec  9 00:13:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON_CHECKS): New macro; runs common C
+       language/compiler checks, which may be useful to neon applications
+       regardless of whether a bundled or external neon is being used.
+       Use AC_REQUIRE to prevent macros being expanded more than once.
+       (LIBNEON_SOURCE_CHECKS, NEON_COMMON): Require NEON_COMMON_CHECKS
+       to have been expanded.
+       
+Sat Dec  8 00:56:34 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_FORMAT): Rewrite to use cache results (should fix
+       for cross-compiling), and for GCC, actually test for warnings -
+       fix for Linux.
+
+Sat Dec  8 00:15:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_CHECK_SUPPORT): Send --support output to
+       /dev/null, in case it is from pre-0.18 and prints the usage
+       message.
+
+Sat Dec  8 00:13:55 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON): Prepend -lneon to NEON_LIBS rather than
+       overwriting it when using bundled build.
+
+Mon Dec  3 19:49:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_FORMAT_PREP, NEON_FORMAT): New macros.
+       (LIBNEON_SOURCE_CHECKS): Call them.
+
+Mon Dec  3 19:43:11 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix gethostbyname() detection on Unixware 7:
+
+       * neon.m4 (NEON_COMMON): Add -lneon to NEON_LIBS after performing
+       source checks.  (NE_SEARCH_LIBS): Test using libraries from
+       NEON_LIBS too.
+
+Sat Nov 24 20:33:11 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_CHECK_SUPPORT): New macro.  (NEON_COMMON): Define
+       NEON_SUPPORTS_{SSL,ZLIB} when using an external neon.
+
+Sat Nov 24 20:25:15 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_WITHOUT_ZLIB): New function.
+       (LIBNEON_SOURCE_CHECKS): Conditionally enable zlib support.
+
+Sun Nov 18 12:29:08 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-ssl.m4 (NEON_SSL): Don't add -I/usr/include to NEON_CFLAGS.
+
+Sat Oct 27 12:20:08 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4, neon-ssl.m4: Substitute NEON_SUPPORTS_ZLIB,
+       NEON_SUPPORTS_DAV, NEON_SUPPORTS_SSL as "yes" or "no"
+       appropriately.
+
+Thu Oct 25 14:29:53 2001  Mo DeJong  <supermo@bayarea.net>
+
+        * neon.m4 (NEON_NORMAL_BUILD): Use AC_CHECK_TOOL instead of
+        AC_PATH_PROG so that cross compilation works properly.
+
+Sat Oct  6 13:36:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_ZLIB): New macro.  (LIBNEON_SOURCE_CHECKS):
+       print warning if struct tm lacks tm_gmtoff.
+
+Sat Oct  6 12:39:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Require autoconf 2.50.  Use AC_HELP_STRING where
+       possible, and AC_MSG_NOTICE instead of 'echo'.
+
+       * neon-ssl.m4, neon-xml-parser.m4, neon-socks.m4: Quoting fixes
+       for help strings.
+
+Tue Oct  2 21:13:24 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for tm_gmtoff in struct
+       tm.
+
+Sun Sep 30 23:35:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NE_SEARCH_LIBS): AC_SEARCH_LIBS replacement, adds found
+       libraries to NEON_LIBS instead.
+
+Sun Sep 30 11:11:19 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-test.m4: New file.
+
+Sun Sep 30 11:09:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4, neon-xml-parser.m4: Always add libs to $LIBS rather
+       than $NEONLIBS.
+
+       * neon.m4: Export NEON_CFLAGS.
+
+Sat Sep 29 14:12:53 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for zlib (zlib.h, inflate
+       in -lz).  Add ne_compress to NEON_EXTRAOBJS.
+
+Tue Sep 25 07:31:53 2001  Mo DeJong  <supermo@bayarea.net>
+
+        * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for <signal.h> instead of
+       <sys/signal.h>. Define HAVE_SIGPIPE if SIGPIPE is defined in
+       <signal.h>.
+
+Mon Sep 24 20:16:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_EXTERNAL_EXPAT): Fix broken
+       AC_DEFINE (Mo DeJong).
+
+Mon Sep 24 17:24:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ac_c_bigendian_cross.m4: New file.
+
+       * neon.m4: Use AC_C_BIGENDIAN_CROSS rather than AC_C_BIGENDIAN.
+
+Mon Sep 17 23:29:11 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for setvbuf().
+
+Sun Sep 16 20:39:05 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-ssl.m4 (NEON_SSL): Put SSL libs in LIBS rather than
+       NEONLIBS (and lib paths).
+
+Sun Sep 16 20:36:53 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON): Add library paths to NEONLIBS rather than
+       LDFLAGS.
+
+Sat Jun  9 22:06:25 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-debug.m4: New file.
+
+Thu May 31 00:04:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON_BUILD): Update filenames.
+       (NEON_CHECK_VERSION): Do simple AC_TRY_LINK and warn appropriately
+       before checking version.
+
+Thu May 31 00:03:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-warnings.m4: Add -Wbad-function-cast.
+
+Wed May 30 23:37:48 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4: Added --with-libxml1 and --with-libxml2
+       arguments.
+
+Tue Apr 17 23:06:25 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-ssl.m4: Define neon_ssl_message for configure output.
+
+Wed Apr 11 23:14:33 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON_BUILD): Fix specifying a list of object
+       files.
+
+Fri Apr  6 23:09:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Call NEON_SOCKS.
+
+Fri Apr  6 23:08:39 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-socks.m4: Add basic SOCKSv5 support (untested).
+
+Mon Apr  2 21:42:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Version is 0.13.0, interface version 13:0:0.
+
+Mon Apr  2 00:27:37 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Move check for 'ar' program to NEON_NORMAL_BUILD, it's
+       not necessary for libtool build.
+
+Mon Apr  2 00:17:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Check for xmlversion.h
+       header from libxml2.
+
+Sun Apr  1 21:23:26 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4: Add expat2 support (Sam TH <sam@uchicago.edu>).
+
+Wed Mar 21 10:56:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4: Add libxml2 support.
+
+Sun Mar  4 15:45:21 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Use an m4 ifelse rather
+       than a shell test to code the conditional on whether an argument
+       was passed to the macro or not.
+
+Sun Mar  4 15:23:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Add "actions if not bundled" argument to *_BUNDLED
+       macros.
+
+Mon Feb 26 22:52:24 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Version is 0.12.0.
+
+Mon Feb 26 22:06:13 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Versionn is 0.12.0-dev.
+
+Sun Feb 25 17:12:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Add checks for endianness (for
+       md5 code), inline, and const.
+
+Sun Feb 25 17:00:07 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON): Rename NEON_IS_BUNDLED to
+       NEON_BUILD_BUNDLED.
+
+Sun Feb 25 16:52:19 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON): Define NEON_IS_BUNDLED to "yes" or "no"
+       appropriately.
+
+Sat Feb 24 00:06:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Don't set NEON_NEED_XML_PARSER
+       here.  (NEON_COMMON): ... set it here instead.
+       (NEON_WITHOUT_WEBDAV): New macro to disable WebDAV support.
+       (NEON_COMMON_BUILD): Select default set of object files to build
+       depending on whether DAV is enabled or not.
+
+Fri Feb 23 23:28:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_COMMON_BUILD): Use an m4 'ifelse' for the number
+       of args test.
+
+       * neon.m4 (NEON_LIBTOOL_BUILD, NEON_NORMAL_BUILD,
+       NEON_COMMON_BUILD): Set NEON_OBJEXT correctly (fixes
+       dependancies).
+
+Sun Feb  4 14:55:10 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Version is 0.11.0, interface version is 11:0:0.
+
+Sun Jan 28 17:16:27 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_CHECK_VERSION): Run actions-if-okay if
+       NEON_REQUIRE has not been called.
+
+Sun Jan 28 14:53:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4 (NEON_REQUIRE, NEON_CHECK_VERSION): New macros.
+       (NEON_COMMON): If a required version is defined, check that an
+       external neon library matches it.
+
+Sun Jan 28 10:39:31 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Define NEON_VERSION, NEON_VERSION_{MAJOR,MINOR} in
+       config.h rather than substituting into neon_config.h.
+
+Sat Jan 27 22:55:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon.m4: Include version string in library message.
+
+Tue Jan 23 23:14:33 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_VERSIONS): New macro.  (NEON_COMMON): Call it from
+       here.
+
+Mon Jan 15 22:26:54 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Append rather than
+       overwrite CFLAGS.
+
+Thu Jan 11 20:49:12 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon-ssl.m4: Check for OpenSSL in /usr too.
+
+Thu Jan 11 20:05:34 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_VPATH_BUNDLED): New macro.  (NEON_BUNDLED): Call
+       NEON_COMMON_BUNDLED.  (NEON_COMMON_BUNDLED): Abstracted from
+       NEON_BUNDLED.
+
+Wed Jan 10 22:44:37 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_LIBTOOL_BUILD, NEON_NORMAL_BUILD): Pass optional
+       set of objects to build to these macros.  Else, all objects go in
+       NEONOBJS.  (NEON_COMMON_BUILD): Implement that.  Also substitute
+       NEON_LINK_FLAGS. 
+
+Mon Jan  8 22:23:51 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Put XML parser libs in
+       $NEONLIBS rather than $LIBS.
+
+Mon Jan  8 22:20:51 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon-ssl.m4 (NEON_SSL): Put OpenSSL libs in $NEONLIBS rather
+       than LIBS.
+
+Sun Jan  7 17:30:54 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_BUNDLED): Add optional second argument to specify
+       builddir of bundled sources (Peter Moulder <pjm@bofh.asn.au>).
+
+Wed Jan  3 21:33:05 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4: Place libraries needed to use neon in NEONLIBS.  Adding
+       them to LIBS breaks bundled builds since libneon doesn't exist at
+       configure-time, and configure uses $LIBS.
+
+Wed Jan  3 21:17:00 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_COMMON_BUILD): Don't set top_builddir using 'pwd'.
+
+Wed Jan  3 21:15:04 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_COMMON): If using bundled neon, add -L<bundled
+       dir> to LDFLAGS, and -lneon to LIBS.
+
+Fri Dec 22 23:13:39 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_NORMAL_BUILD, NEON_COMMON_BUILD): New macros.
+
+Tue Dec 19 22:13:18 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_LIBTOOL_BUILD): New macro.
+
+Wed Dec 13 22:07:07 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4: Add a decent interface: NEON_LIBRARY for non-bundled
+       case, NEON_BUNDLED for the bundled case.  (LIBNEON_SOURCE_CHECKS):
+       Always set NEON_NEED_XML_PARSER.
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Only execute if
+       "$NEON_NEED_XML_PARSER" = "yes".
+
+Sun Nov 19 22:52:56 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4, neon-ssl.m4, neon-warnings.m4, neon-xml-parser.m4:
+       Clarify that the more liberal license applies to the m4 files
+       only, not neon in general.
+
+Sun Nov 19 22:40:01 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_LIBRARY): Don't call NEON_XML_PARSER, set
+       NEON_NEED_XML_PARSER to "yes" if it needs to be called.
+
+Sun Nov 19 22:31:26 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Switch to useing
+       NEON_INCLUDED_EXPAT m4 macro rather than passing arguments.
+
+Sun Nov 19 22:20:36 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_LIBRARY): Switch to using NEON_INCLUDED_SOURCE m4
+       macro rather than passing arguments to the NEON_LIBRARY macro.
+       
+Sun Nov  5 23:26:18 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon-xml-parser.m4: Never set LIBS if using a libtool-built
+       libexpat.la (Greg Stein).
+
+2000-10-10  Joe Orton  <joe@monolith.orton.local>
+
+       * neon-xml-parser.m4: If libexpat.la is included in the
+       --with-expat parameter, then use a libtool-friendly LIBS. (Greg
+       Stein)
+
+Sat Oct  7 19:16:08 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon-xml-parser.m4: Link against a libexpat.la if found in
+       --with-expat location. (Greg Stein).
+
+Mon Sep 11 15:05:58 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4: Use AC_SEARCH_LIBS for finding gethostbyname() and
+       socket().
+
+Mon Sep 11 15:03:45 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon.m4 (NEON_REPLACE_SNPRINTF): New macro.
+
+Fri Sep  8 14:30:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.m4: Check for socket() in -lsocket, -linet.
+
+Thu Sep  7 00:11:51 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-xml-parser.m4: Added --with-expat flag (patch by Greg
+       Stein).
+
+Sun Aug 13 11:12:41 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * strftime.m4: New file, from fileutils-4.0i.
+
+Thu Jul 27 19:59:18 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-ssl.m4: Append the SSL libs on the end of $LIBS rather than
+       redefining it completely.
+
+Thu Jul 27 19:43:38 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.m4: Define variable neon_library_message to describe what
+       neon library is being used.
+
+Mon Jul 24 16:56:34 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-ssl.m4: Put -lssl before -lcrypto in LIBS.
+
+Thu Jul 20 15:12:14 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Pass directory name
+       containing bundled expat sources as $1.
+
+       * neon.m4 (NEON_LIBRARY): Pass directory name containing bundled
+       neon sources as $1, and $2 is passed to NEON_XML_PARSER for
+       similar use.
+
+Thu Jul 20 15:04:49 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-ssl.m4: Rewritten from scratch. Support OpenSSL only.
+
+Thu Jul 20 12:41:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.m4, neon-xml-parser.m4, neon_warnings.m4: Added licensing
+       information.
+
+Wed Jul 19 19:30:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-warnings.m4: New file. (NEON_WARNINGS): Macro for doing
+       --enable-warnings.
+
+Sun Jun 18 12:12:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.m4: Only add --with-included-neon flag if neon is bundled.
+
+Sun Jun 18 12:08:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * gnome-x-checks.m4: Imported from GNOME CVS macros module,
+       adding: descriptive args to AC_DEFINE HAVE_LIBSM call, requiring
+       Gtk 1.2.8 and the Gtk 'gthread' module.
+
+Mon May 29 15:10:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.m4 (LIBNEON_SOURCE_CHECKS): Call NEON_SSL.
+
+Tue May 23 19:11:29 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.m4: Renamed from neon-checks.m4.
+
+Sun May 21 23:52:27 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-ssl.m4: New file.
+
+Sat May 13 21:08:54 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * acconfig.h: Added HAVE_LC_MESSAGE (my lcmessage.m4 is missing
+       the appropriate description arguments).
+
+Sat May 13 21:08:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * acconfig.h: Added PACKAGE and VERSION.
+
+Sat May 13 21:02:29 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socklen-arg-type.m4: Added file, modified from a KDE
+       configure.in.
+
+Sat May 13 20:44:56 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * gnome-x-checks.m4: Added description arguments to
+       AC_DEFINE(HAVE_LIBSM).
+
 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.
index e748ee0..7a9fa23 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2001 Joe Orton <joe@manyfish.co.uk>
+# Copyright (C) 2001-2002 Joe Orton <joe@manyfish.co.uk>    -*- autoconf -*-
 #
 # This file is free software; you may copy and/or distribute it with
 # or without modifications, as long as this notice is preserved.
 
 AC_DEFUN([NEON_TEST], [
 
-AC_CHECK_FUNCS(pipe)
+AC_REQUIRE([NEON_COMMON_CHECKS])
+
+AC_REQUIRE([AC_TYPE_PID_T])
+AC_REQUIRE([AC_HEADER_TIME])
+
+dnl NEON_XML_PARSER may add things (e.g. -I/usr/local/include) to 
+dnl CPPFLAGS which make "gcc -Werror" fail in NEON_FORMAT; suggest
+dnl this macro is used first.
+AC_BEFORE([$0], [NEON_XML_PARSER])
+
+AC_CHECK_HEADERS(sys/time.h)
+
+AC_CHECK_FUNCS(pipe isatty usleep shutdown)
+
+AC_REQUIRE([NE_FIND_AR])
+
+NEON_FORMAT(time_t, [
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif])
 
 ])
index aca9d07..38a804e 100644 (file)
-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 )
-       )
+# Copyright (C) 1998-2004 Joe Orton <joe@manyfish.co.uk>    -*- autoconf -*-
+#
+# 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>
+
+# Check for XML parser, supporting libxml 2.x and expat 1.95.x,
+# or a bundled copy of expat.
+#  *  Bundled expat if a directory name argument is passed
+#     -> expat dir must contain minimal expat sources, i.e.
+#        xmltok, xmlparse sub-directories.  See sitecopy/cadaver for
+#       examples of how to do this.
+#
+# Usage: 
+#  NEON_XML_PARSER()
+# or
+#  NEON_XML_PARSER(expat-dir)
+
+dnl Find expat: run $1 if found, else $2
+AC_DEFUN([NE_XML_EXPAT], [
+AC_CHECK_HEADER(expat.h,
+  [AC_CHECK_LIB(expat, XML_SetXmlDeclHandler, [
+    AC_DEFINE(HAVE_EXPAT, 1, [Define if you have expat])
+    neon_xml_parser_message="expat"
+    NEON_LIBS="$NEON_LIBS -lexpat"
+    neon_xml_parser=expat
+  ], [$1])], [$1])
+])
+
+dnl Find libxml2: run $1 if found, else $2
+AC_DEFUN([NE_XML_LIBXML2], [
+AC_CHECK_PROG(XML2_CONFIG, xml2-config, xml2-config)
+if test -n "$XML2_CONFIG"; then
+    neon_xml_parser_message="libxml `$XML2_CONFIG --version`"
+    AC_DEFINE(HAVE_LIBXML, 1, [Define if you have libxml])
+    # xml2-config in some versions erroneously includes -I/include
+    # in the --cflags output.
+    CPPFLAGS="$CPPFLAGS `$XML2_CONFIG --cflags | sed 's| -I/include||g'`"
+    NEON_LIBS="$NEON_LIBS `$XML2_CONFIG --libs | sed 's|-L/usr/lib ||g'`"
+    AC_CHECK_HEADERS(libxml/xmlversion.h libxml/parser.h,,[
+      AC_MSG_ERROR([could not find parser.h, libxml installation problem?])])
+    neon_xml_parser=libxml2
 else
-       neon_found_parser="no"
+    $1
 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
+dnl Configure for a bundled expat build.
+AC_DEFUN([NE_XML_BUNDLED_EXPAT], [
 
-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"
+AC_REQUIRE([AC_C_BIGENDIAN])
+# Define XML_BYTE_ORDER for expat sources.
+if test $ac_cv_c_bigendian = "yes"; then
+  ne_xml_border=21
+else
+  ne_xml_border=12
 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
+# mini-expat doesn't pick up config.h
+CPPFLAGS="$CPPFLAGS -DXML_BYTE_ORDER=$ne_xml_border -DXML_DTD -I$1/xmlparse -I$1/xmltok"
+
+# Use the bundled expat sources
+AC_LIBOBJ($1/xmltok/xmltok)
+AC_LIBOBJ($1/xmltok/xmlrole)
+AC_LIBOBJ($1/xmlparse/xmlparse)
+AC_LIBOBJ($1/xmlparse/hashtable)
+
+AC_DEFINE(HAVE_EXPAT)
+
+AC_DEFINE(HAVE_XMLPARSE_H, 1, [Define if using expat which includes xmlparse.h])
+
+])
+
+AC_DEFUN([NEON_XML_PARSER], [
+
+dnl Switches to force choice of library
+AC_ARG_WITH([libxml2],
+AC_HELP_STRING([--with-libxml2], [force use of libxml 2.x]))
+AC_ARG_WITH([expat], 
+AC_HELP_STRING([--with-expat], [force use of expat]))
+
+dnl Flag to force choice of included expat, if available.
+ifelse($#, 1, [
+AC_ARG_WITH([included-expat],
+AC_HELP_STRING([--with-included-expat], [use bundled expat sources]),,
+with_included_expat=no)],
+with_included_expat=no)
+
+if test "$NEON_NEED_XML_PARSER" = "yes"; then
+  # Find an XML parser
+  neon_xml_parser=none
+
+  # Forced choice of expat:
+  case $with_expat in
+  yes) NE_XML_EXPAT([AC_MSG_ERROR([expat library not found, cannot proceed])]) ;;
+  no) ;;
+  */libexpat.la) 
+       # Special case for Subversion
+       ne_expdir=`echo $with_expat | sed 's:/libexpat.la$::'`
+       AC_DEFINE(HAVE_EXPAT)
+       CPPFLAGS="$CPPFLAGS -I$ne_expdir"
+       if test "x${NEON_TARGET}" = "xlibneon.la"; then
+         NEON_LTLIBS=$with_expat
+       else
+         # no dependency on libexpat => crippled libneon, so do partial install
+         ALLOW_INSTALL=lib
+       fi
+       neon_xml_parser=expat
+       neon_xml_parser_message="expat in $ne_expdir"
+       ;;
+  /*) AC_MSG_ERROR([--with-expat does not take a directory argument]) ;;
+  esac
+
+  # If expat wasn't specifically enabled and libxml was:
+  if test "${neon_xml_parser}-${with_libxml}-${with_included_expat}" = "none-yes-no"; then
+     NE_XML_LIBXML2(
+      [AC_MSG_ERROR([libxml2.x library not found, cannot proceed])])
+  fi
+
+  # Otherwise, by default search for libxml2 then expat:
+  if test "${neon_xml_parser}-${with_included_expat}" = "none-no"; then
+     NE_XML_LIBXML2([NE_XML_EXPAT([:])])
+  fi
+
+  # If an XML parser still has not been found, fail or use the bundled expat
+  if test "$neon_xml_parser" = "none"; then
+    m4_if($1, [], 
+       [AC_MSG_ERROR([no XML parser was found: expat or libxml 2.x required])],
+       [# Configure the bundled copy of expat
+        NE_XML_BUNDLED_EXPAT($1)
+       neon_xml_parser_message="bundled expat in $1"])
+  fi
 
+  AC_MSG_NOTICE([XML parser used: $neon_xml_parser_message])
 fi
 
-])
\ No newline at end of file
+])
index 5b4d839..0d74014 100644 (file)
+# Copyright (C) 1998-2004 Joe Orton <joe@manyfish.co.uk>    -*- autoconf -*-
+#
+# 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.
 
-AC_DEFUN([NEON_LIBRARY],[
+# 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>
+
+#
+# Usage:
+#
+#      NEON_LIBRARY
+# or   NEON_BUNDLED(srcdir, [ACTIONS-IF-BUNDLED], [ACTIONS-IF-NOT_BUNDLED]) 
+# or   NEON_VPATH_BUNDLED(srcdir, builddir, 
+#                        [ACTIONS-IF-BUNDLED], [ACTIONS-IF-NOT-BUNDLED])
+#
+#   where srcdir is the location of bundled neon 'src' directory.
+#   If using a VPATH-enabled build, builddir is the location of the
+#   build directory corresponding to srcdir.
+#
+#   If a bundled build *is* being used, ACTIONS-IF-BUNDLED will be
+#   evaluated. These actions should ensure that 'make' is run
+#   in srcdir, and that one of NEON_NORMAL_BUILD or NEON_LIBTOOL_BUILD 
+#   is called.
+#
+# After calling one of the above macros, if the NEON_NEED_XML_PARSER
+# variable is set to "yes", then you must configure an XML parser
+# too. You can do this your own way, or do it easily using the
+# NEON_XML_PARSER() macro. Example usage for where we have bundled the
+# neon sources in a directory called libneon, and bundled expat
+# sources in a directory called 'expat'.
+#
+#   NEON_BUNDLED(libneon, [
+#      NEON_XML_PARSER(expat)
+#      NEON_NORMAL_BUILD
+#   ])
+#
+# Alternatively, for a simple standalone app with neon as a
+# dependancy, use just:
+#
+#   NEON_LIBRARY
+# 
+# and rely on the user installing neon correctly.
+#
+# You are free to configure an XML parser any other way you like,
+# but the end result must be, either expat or libxml will get linked
+# in, and HAVE_EXPAT or HAVE_LIBXML is defined appropriately.
+#
+# To set up the bundled build environment, call 
+#
+#    NEON_NORMAL_BUILD
+# or
+#    NEON_LIBTOOL_BUILD
+# 
+# depending on whether you are using libtool to build, or not.
+# Both these macros take an optional argument specifying the set
+# of object files you wish to build: if the argument is not given,
+# all of neon will be built.
+
+AC_DEFUN([NEON_BUNDLED],[
+
+neon_bundled_srcdir=$1
+neon_bundled_builddir=$1
+
+NEON_COMMON_BUNDLED([$2], [$3])
 
-AC_ARG_WITH( neon,
-       [  --with-neon            Specify location of neon library ],
-       [neon_loc="$withval"])
+])
+
+AC_DEFUN([NEON_VPATH_BUNDLED],[
+
+neon_bundled_srcdir=$1
+neon_bundled_builddir=$2
+NEON_COMMON_BUNDLED([$3], [$4])
+
+])
 
-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"])
+AC_DEFUN([NEON_COMMON_BUNDLED],[
+
+AC_PREREQ(2.50)
+
+AC_ARG_WITH(included-neon,
+AC_HELP_STRING([--with-included-neon], [force use of included neon library]),
+[neon_force_included="$withval"], [neon_force_included="no"])
+
+NEON_COMMON
+
+# The colons are here so there is something to evaluate
+# in case the argument was not passed.
+if test "$neon_force_included" = "yes"; then
+       :
+       $1
+else
+       :
+       $2
 fi
 
-AC_MSG_CHECKING(for neon location)
+])
+
+dnl Not got any bundled sources:
+AC_DEFUN([NEON_LIBRARY],[
+
+AC_PREREQ(2.50)
+neon_force_included=no
+neon_bundled_srcdir=
+neon_bundled_builddir=
+
+NEON_COMMON
+
+])
+
+AC_DEFUN([NEON_VERSIONS], [
+
+# Define the current versions.
+NEON_VERSION_MAJOR=0
+NEON_VERSION_MINOR=24
+NEON_VERSION_RELEASE=7
+NEON_VERSION_TAG=
 
-if test "$neon_included" != "yes"; then
-    # Look for neon
+NEON_VERSION="${NEON_VERSION_MAJOR}.${NEON_VERSION_MINOR}.${NEON_VERSION_RELEASE}${NEON_VERSION_TAG}"
 
-    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
+# libtool library interface versioning.  Release policy dictates that
+# for neon 0.x.y, each x brings an incompatible interface change, and
+# each y brings no interface change, and since this policy has been
+# followed since 0.1, x == CURRENT, y == RELEASE, 0 == AGE.  For
+# 1.x.y, this will become N + x == CURRENT, y == RELEASE, x == AGE,
+# where N is constant (and equal to CURRENT + 1 from the final 0.x
+# release)
+NEON_INTERFACE_VERSION="${NEON_VERSION_MINOR}:${NEON_VERSION_RELEASE}:0"
+
+AC_DEFINE_UNQUOTED(NEON_VERSION, "${NEON_VERSION}", 
+       [Define to be the neon version string])
+AC_DEFINE_UNQUOTED(NEON_VERSION_MAJOR, [(${NEON_VERSION_MAJOR})],
+       [Define to be major number of neon version])
+AC_DEFINE_UNQUOTED(NEON_VERSION_MINOR, [(${NEON_VERSION_MINOR})],
+       [Define to be minor number of neon version])
+
+])
+
+dnl Define the minimum required version
+AC_DEFUN([NEON_REQUIRE], [
+neon_require_major=$1
+neon_require_minor=$2
+])
+
+dnl Check that the external library found in a given location
+dnl matches the min. required version (if any).  Requires that
+dnl NEON_CONFIG be set the the full path of a valid neon-config
+dnl script
+dnl
+dnl Usage:
+dnl    NEON_CHECK_VERSION(ACTIONS-IF-OKAY, ACTIONS-IF-FAILURE)
+dnl
+AC_DEFUN([NEON_CHECK_VERSION], [
+if test "x$neon_require_major" = "x"; then
+    # Nothing to check.
+    ne_goodver=yes
+    ne_libver="(version unknown)"
+else
+    # Check whether the library is of required version
+    ne_save_LIBS="$LIBS"
+    ne_save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS `$NEON_CONFIG --cflags`"
+    LIBS="$LIBS `$NEON_CONFIG --libs`"
+    ne_libver=`$NEON_CONFIG --version | sed -e "s/neon //g"`
+    # Check whether it's possible to link against neon
+    AC_CACHE_CHECK([linking against neon], [ne_cv_lib_neon],
+    AC_TRY_LINK_FUNC([ne_version_match],
+       [ne_cv_lib_neon=yes], [ne_cv_lib_neon=no]))
+    if test "$ne_cv_lib_neon" = "yes"; then
+       # Now check whether the neon library version is satisfactory
+       AC_CACHE_CHECK([neon library version], [ne_cv_lib_neonver],
+       AC_TRY_RUN([#include <ne_utils.h>
+int main(int argc, char **argv) {
+return ne_version_match($neon_require_major, $neon_require_minor);
+}], ne_cv_lib_neonver=yes, ne_cv_lib_neonver=no))
     fi
+    ne_goodver=$ne_cv_lib_neonver
+    LIBS=$ne_save_LIBS
+    CFLAGS=$ne_save_CFLAGS
+fi
+if test "$ne_goodver" = "yes"; then
+    AC_MSG_NOTICE([using neon library $ne_libver])
+    $1
+else
+    AC_MSG_NOTICE([incompatible neon library version $ne_libver: wanted $neon_require_major.$neon_require_minor])
+    $2
+fi])
+
+dnl NEON_CHECK_SUPPORT(feature, var)
+AC_DEFUN([NEON_CHECK_SUPPORT], [
+if $NEON_CONFIG --support $1 >/dev/null; then
+   neon_$1_message="supported by neon"
+   $2=yes
+else
+   neon_$1_message="not supported by neon"
+   $2=no
+fi
+])
+
+AC_DEFUN([NEON_USE_EXTERNAL], [
+# Configure to use an external neon, given a neon-config script
+# found at $NEON_CONFIG.
+neon_prefix=`$NEON_CONFIG --prefix`
+NEON_CHECK_VERSION([
+    CFLAGS="$CFLAGS `$NEON_CONFIG --cflags`"
+    NEON_LIBS="$NEON_LIBS `$NEON_CONFIG --libs`"
+    neon_library_message="library in ${neon_prefix} (`$NEON_CONFIG --version`)"
+    neon_xml_parser_message="using whatever neon uses"
+    NEON_CHECK_SUPPORT([ssl], [NEON_SUPPORTS_SSL])
+    NEON_CHECK_SUPPORT([zlib], [NEON_SUPPORTS_ZLIB])
+    neon_got_library=yes
+], [neon_got_library=no])
+])
+
+AC_DEFUN([NEON_COMMON],[
+
+AC_REQUIRE([NEON_COMMON_CHECKS])
+
+NEON_VERSIONS
+
+AC_ARG_WITH(neon,
+[  --with-neon[[=DIR]]       specify location of neon library],
+[case $withval in
+yes|no) neon_force_external=$withval; neon_ext_path= ;;
+*) neon_force_external=yes; neon_ext_path=$withval ;;
+esac;], [
+neon_force_external=no
+neon_ext_path=
+])
+
+if test "$neon_force_included" = "no"; then
+    # There is no included neon source directory, or --with-included-neon
+    # wasn't given (so we're not forced to use it).
 
-    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`"
+    # Default to no external neon.
+    neon_got_library=no
+    if test "x$neon_ext_path" = "x"; then
+       AC_PATH_PROG([NEON_CONFIG], neon-config, none)
+       if test "x${NEON_CONFIG}" = "xnone"; then
+           AC_MSG_NOTICE([no external neon library found])
+       elif test -x "${NEON_CONFIG}"; then
+           NEON_USE_EXTERNAL
+       else
+           AC_MSG_NOTICE([ignoring non-executable ${NEON_CONFIG}])
+       fi
     else
-       if test "$1" = "bundle"; then
-           neon_included="yes"
+       AC_MSG_CHECKING([for neon library in $neon_ext_path])
+       NEON_CONFIG="$neon_ext_path/bin/neon-config"
+       if test -x ${NEON_CONFIG}; then
+           AC_MSG_RESULT([found])
+           NEON_USE_EXTERNAL
+       else
+           AC_MSG_RESULT([not found])
+           # ...will fail since force_external=yes
+       fi
+    fi
+
+    if test "$neon_got_library" = "no"; then 
+       if test $neon_force_external = yes; then
+           AC_MSG_ERROR([could not use external neon library])
+       elif test -n "$neon_bundled_srcdir"; then
+           # Couldn't find external neon, forced to use bundled sources
+           neon_force_included="yes"
        else
+           # Couldn't find neon, and don't have bundled sources
            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"
+# This isn't a simple 'else' branch, since neon_force_included
+# is set to yes if the search fails.
+
+if test "$neon_force_included" = "yes"; then
+    AC_MSG_NOTICE([using bundled neon ($NEON_VERSION)])
+    NEON_BUILD_BUNDLED="yes"
     LIBNEON_SOURCE_CHECKS
-    LIBOBJS="$LIBOBJS \$(NEONOBJS)"
+    CFLAGS="$CFLAGS -I$neon_bundled_srcdir"
+    NEON_LIBS="-L$neon_bundled_builddir -lneon $NEON_LIBS"
+    NEON_NEED_XML_PARSER=yes
+    neon_library_message="included libneon (${NEON_VERSION})"
+else
+    # Don't need to configure an XML parser
+    NEON_NEED_XML_PARSER=no
+    NEON_BUILD_BUNDLED="yes"
+fi
+
+AC_SUBST(NEON_BUILD_BUNDLED)
+
+])
+
+dnl AC_SEARCH_LIBS done differently. Usage:
+dnl   NE_SEARCH_LIBS(function, libnames, [extralibs], [actions-if-not-found],
+dnl                            [actions-if-found])
+dnl Tries to find 'function' by linking againt `-lLIB $NEON_LIBS' for each
+dnl LIB in libnames.  If link fails and 'extralibs' is given, will also
+dnl try linking against `-lLIB extralibs $NEON_LIBS`.
+dnl Once link succeeds, `-lLIB [extralibs]` is prepended to $NEON_LIBS, and
+dnl `actions-if-found' are executed, if given.
+dnl If link never succeeds, run `actions-if-not-found', if given, else
+dnl give an error and fail configure.
+AC_DEFUN([NE_SEARCH_LIBS], [
+
+AC_CACHE_CHECK([for library containing $1], [ne_cv_libsfor_$1], [
+AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="none needed"], [
+ne_sl_save_LIBS=$LIBS
+ne_cv_libsfor_$1="not found"
+for lib in $2; do
+    LIBS="$ne_sl_save_LIBS -l$lib $NEON_LIBS"
+    AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib"; break])
+    m4_if($3, [], [], dnl If $3 is specified, then...
+              [LIBS="$ne_sl_save_LIBS -l$lib $3 $NEON_LIBS"
+               AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib $3"; break])])
+done
+LIBS=$ne_sl_save_LIBS])])
+
+if test "$ne_cv_libsfor_$1" = "not found"; then
+   m4_if($4, [], [AC_MSG_ERROR([could not find library containing $1])], [$4])
+elif test "$ne_cv_libsfor_$1" != "none needed"; then 
+   NEON_LIBS="$ne_cv_libsfor_$1 $NEON_LIBS"
+   $5
+fi])
+
+dnl Check for presence and suitability of zlib library
+AC_DEFUN([NEON_ZLIB], [
+
+AC_ARG_WITH(zlib, AC_HELP_STRING([--without-zlib], [disable zlib support]),
+ne_use_zlib=$withval, ne_use_zlib=yes)
+
+NEON_SUPPORTS_ZLIB=no
+AC_SUBST(NEON_SUPPORTS_ZLIB)
+
+if test "$ne_use_zlib" = "yes"; then
+    AC_CHECK_HEADER(zlib.h, [
+       AC_CHECK_LIB(z, inflate, [ 
+           NEON_LIBS="$NEON_LIBS -lz"
+           NEON_CFLAGS="$NEON_CFLAGS -DNEON_ZLIB"
+           NEON_SUPPORTS_ZLIB=yes
+           neon_zlib_message="found in -lz"
+       ], [neon_zlib_message="zlib not found"])
+    ], [neon_zlib_message="zlib not found"])
+else
+    neon_zlib_message="zlib disabled"
+fi
+])
+
+AC_DEFUN([NE_MACOSX], [
+# Check for Darwin, which needs extra cpp and linker flags.
+AC_CACHE_CHECK([for Darwin], ne_cv_os_macosx, [
+case `uname -s 2>/dev/null` in
+Darwin) ne_cv_os_macosx=yes ;;
+*) ne_cv_os_macosx=no ;;
+esac])
+if test $ne_cv_os_macosx = yes; then
+  CPPFLAGS="$CPPFLAGS -no-cpp-precomp"
+  LDFLAGS="$LDFLAGS -flat_namespace"
+fi
+])
+
+AC_DEFUN([NEON_COMMON_CHECKS], [
+
+# These checks are done whether or not the bundled neon build
+# is used.
+
+AC_REQUIRE([AC_PROG_CC])
+AC_REQUIRE([AC_PROG_CC_STDC])
+AC_REQUIRE([AC_LANG_C])
+AC_REQUIRE([AC_ISC_POSIX])
+AC_REQUIRE([AC_C_INLINE])
+AC_REQUIRE([AC_C_CONST])
+AC_REQUIRE([AC_TYPE_SIZE_T])
+AC_REQUIRE([AC_TYPE_OFF_T])
+
+AC_REQUIRE([NE_MACOSX])
+
+AC_REQUIRE([AC_PROG_MAKE_SET])
+
+AC_REQUIRE([AC_HEADER_STDC])
+
+AC_CHECK_HEADERS([errno.h stdarg.h string.h stdlib.h])
+
+NEON_FORMAT(size_t,,u) dnl size_t is unsigned; use %u formats
+NEON_FORMAT(off_t)
+NEON_FORMAT(ssize_t)
+
+])
+
+AC_DEFUN([NEON_FORMAT_PREP], [
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_CHECK_SIZEOF(long long)
+if test "$GCC" = "yes"; then
+  AC_CACHE_CHECK([for gcc -Wformat -Werror sanity], ne_cv_cc_werror, [
+  # See whether a simple test program will compile without errors.
+  ne_save_CPPFLAGS=$CPPFLAGS
+  CPPFLAGS="$CPPFLAGS -Wformat -Werror"
+  AC_TRY_COMPILE([#include <sys/types.h>
+  #include <stdio.h>], [int i = 42; printf("%d", i);], 
+  [ne_cv_cc_werror=yes], [ne_cv_cc_werror=no])
+  CPPFLAGS=$ne_save_CPPFLAGS])
+  ne_fmt_trycompile=$ne_cv_cc_werror
+else
+  ne_fmt_trycompile=no
+fi
+])
+
+dnl NEON_FORMAT(TYPE[, HEADERS[, [SPECIFIER]])
+dnl
+dnl This macro finds out which modifier is needed to create a
+dnl printf format string suitable for printing integer type TYPE (which
+dnl may be an int, long, or long long).
+dnl The default specifier is 'd', if SPECIFIER is not given.  
+dnl TYPE may be defined in HEADERS; sys/types.h is always used first.
+AC_DEFUN([NEON_FORMAT], [
+
+AC_REQUIRE([NEON_FORMAT_PREP])
+
+AC_CHECK_SIZEOF($1, [$2])
+
+dnl Work out which specifier character to use
+m4_ifdef([ne_spec], [m4_undefine([ne_spec])])
+m4_if($#, 3, [m4_define(ne_spec,$3)], [m4_define(ne_spec,d)])
+
+AC_CACHE_CHECK([how to print $1], [ne_cv_fmt_$1], [
+ne_cv_fmt_$1=none
+if test $ne_fmt_trycompile = yes; then
+  oflags="$CPPFLAGS"
+  # Consider format string mismatches as errors
+  CPPFLAGS="$CPPFLAGS -Wformat -Werror"
+  dnl obscured for m4 quoting: "for str in d ld qd; do"
+  for str in ne_spec l]ne_spec[ q]ne_spec[; do
+    AC_TRY_COMPILE([#include <sys/types.h>
+$2
+#include <stdio.h>], [$1 i = 1; printf("%$str", i);], 
+       [ne_cv_fmt_$1=$str; break])
+  done
+  CPPFLAGS=$oflags
+else
+  # Best guess. Don't have to be too precise since we probably won't
+  # get a warning message anyway.
+  case $ac_cv_sizeof_$1 in
+  $ac_cv_sizeof_int) ne_cv_fmt_$1="ne_spec" ;;
+  $ac_cv_sizeof_long) ne_cv_fmt_$1="l]ne_spec[" ;;
+  $ac_cv_sizeof_long_long) ne_cv_fmt_$1="ll]ne_spec[" ;;
+  esac
+fi
+])
+
+if test "x$ne_cv_fmt_$1" = "xnone"; then
+  AC_MSG_ERROR([format string for $1 not found])
 fi
 
+AC_DEFINE_UNQUOTED([NE_FMT_]translit($1, a-z, A-Z), "$ne_cv_fmt_$1", 
+       [Define to be printf format string for $1])
 ])
 
-dnl Call these checks when compiling the libneon source package.
+dnl Wrapper for AC_CHECK_FUNCS; uses libraries from $NEON_LIBS.
+AC_DEFUN([NE_CHECK_FUNCS], [
+ne_cf_save_LIBS=$LIBS
+LIBS="$LIBS $NEON_LIBS"
+AC_CHECK_FUNCS($@)
+LIBS=$ne_cf_save_LIBS])
+
+dnl Checks needed when compiling the neon source.
+AC_DEFUN([LIBNEON_SOURCE_CHECKS], [
 
-AC_DEFUN([LIBNEON_SOURCE_CHECKS],[
+dnl Run all the normal C language/compiler tests
+AC_REQUIRE([NEON_COMMON_CHECKS])
 
-AC_CHECK_HEADERS(stdarg.h string.h strings.h sys/time.h regex.h \
-       stdlib.h unistd.h limits.h sys/select.h)
+dnl Needed for building the MD5 code.
+AC_REQUIRE([AC_C_BIGENDIAN])
+dnl Is strerror_r present; if so, which variant
+AC_REQUIRE([AC_FUNC_STRERROR_R])
+
+AC_CHECK_HEADERS([strings.h sys/time.h limits.h sys/select.h arpa/inet.h \
+       signal.h sys/socket.h netinet/in.h netinet/tcp.h netdb.h])
+
+AC_REQUIRE([NE_SNPRINTF])
 
 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_FUNCS(signal setvbuf setsockopt stpcpy)
+
+if test "$ac_cv_func_stpcpy" = "yes"; then
+  AC_CHECK_DECLS(stpcpy)
+fi
+
+# Modern AIXes with the "Linux-like" libc have an undeclared stpcpy
+AH_BOTTOM([#if defined(HAVE_STPCPY) && !HAVE_DECL_STPCPY && !defined(stpcpy)
+char *stpcpy(char *, const char *);
+#endif])
+
+# Unixware 7 can only link gethostbyname with -lnsl -lsocket
+# Pick up -lsocket first, then the gethostbyname check will work.
+# QNX has gethostbyname in -lsocket. BeOS only has it in -lbind.
+NE_SEARCH_LIBS(socket, socket inet)
+NE_SEARCH_LIBS(gethostbyname, socket nsl bind)
+
+# Enable getaddrinfo() support only if all the necessary functions
+# are found.
+ne_enable_gai=yes
+NE_CHECK_FUNCS(getaddrinfo gai_strerror inet_ntop,,[ne_enable_gai=no; break])
+if test $ne_enable_gai = yes; then
+   AC_DEFINE(USE_GETADDRINFO, 1, [Define if getaddrinfo() should be used])
+   AC_CACHE_CHECK([for working AI_ADDRCONFIG], [ne_cv_gai_addrconfig], [
+   AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <netdb.h>],
+[struct addrinfo hints = {0}, *result;
+hints.ai_flags = AI_ADDRCONFIG;
+if (getaddrinfo("localhost", NULL, &hints, &result) != 0) return 1;])],
+   ne_cv_gai_addrconfig=yes, ne_cv_gai_addrconfig=no)])
+   if test $ne_cv_gai_addrconfig = yes; then
+      AC_DEFINE(USE_GAI_ADDRCONFIG, 1, [Define if getaddrinfo supports AI_ADDRCONFIG])
+   fi
+else
+   # Checks for non-getaddrinfo() based resolver interfaces.
+   NE_SEARCH_LIBS(hstrerror, resolv,,[:])
+   NE_CHECK_FUNCS(hstrerror)
+   # Older Unixes don't declare h_errno.
+   AC_CHECK_DECL(h_errno,,,[#define _XOPEN_SOURCE_EXTENDED 1
+#include <netdb.h>])
+fi
+
+AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,
+AC_MSG_WARN([no timezone handling in date parsing on this platform]),
+[#include <time.h>])
+
+ifdef([neon_no_zlib], [
+    neon_zlib_message="zlib disabled"
+    NEON_SUPPORTS_ZLIB=no
+], [
+    NEON_ZLIB()
+])
+
+# Conditionally enable ACL support
+AC_MSG_CHECKING([whether to enable ACL support in neon])
+if test "x$neon_no_acl" = "xyes"; then
+    AC_MSG_RESULT(no)
+else
+    AC_MSG_RESULT(yes)
+    NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_acl"
+fi
+
+NEON_SSL()
+NEON_SOCKS()
+NEON_GSSAPI()
+
+AC_SUBST(NEON_CFLAGS)
+AC_SUBST(NEON_LIBS)
+AC_SUBST(NEON_LTLIBS)
+
+])
+
+dnl Call to put lib/snprintf.o in LIBOBJS and define HAVE_SNPRINTF_H
+dnl if snprintf isn't in libc.
+
+AC_DEFUN([NEON_REPLACE_SNPRINTF], [
+# Check for snprintf
+AC_CHECK_FUNC(snprintf,,[
+       AC_DEFINE(HAVE_SNPRINTF_H, 1, [Define if need to include snprintf.h])
+       AC_LIBOBJ(lib/snprintf)])
+])
+
+dnl turn off webdav, boo hoo.
+AC_DEFUN([NEON_WITHOUT_WEBDAV], [
+neon_no_webdav=yes
+neon_no_acl=yes
+NEON_NEED_XML_PARSER=no
+neon_xml_parser_message="none needed"
+])
+
+dnl Turn off zlib support
+AC_DEFUN([NEON_WITHOUT_ZLIB], [
+define(neon_no_zlib, yes)
+])
+
+AC_DEFUN([NEON_WITHOUT_ACL], [
+# Turn off ACL support
+neon_no_acl=yes
+])
+
+dnl Common macro to NEON_LIBTOOL_BUILD and NEON_NORMAL_BUILD
+dnl Sets NEONOBJS appropriately if it has not already been set.
+dnl 
+dnl NOT FOR EXTERNAL USE: use LIBTOOL_BUILD or NORMAL_BUILD.
+dnl
+
+AC_DEFUN([NEON_COMMON_BUILD], [
+
+# Using the default set of object files to build.
+# Add the extension to EXTRAOBJS
+ne="$NEON_EXTRAOBJS"
+NEON_EXTRAOBJS=
+for o in $ne; do
+       NEON_EXTRAOBJS="$NEON_EXTRAOBJS $o.$NEON_OBJEXT"
+done   
+
+AC_MSG_CHECKING(whether to enable WebDAV support in neon)
+
+dnl Did they want DAV support?
+if test "x$neon_no_webdav" = "xyes"; then
+  # No WebDAV support
+  AC_MSG_RESULT(no)
+  NEONOBJS="$NEONOBJS \$(NEON_BASEOBJS)"
+  NEON_CFLAGS="$NEON_CFLAGS -DNEON_NODAV"
+  NEON_SUPPORTS_DAV=no
+  AC_DEFINE(NEON_NODAV, 1, [Enable if built without WebDAV support])
+else
+  # WebDAV support
+  NEON_SUPPORTS_DAV=yes
+  NEONOBJS="$NEONOBJS \$(NEON_DAVOBJS)"
+  # Turn on DAV locking please then.
+  AC_DEFINE(USE_DAV_LOCKS, 1, [Support WebDAV locking through the library])
+
+  AC_MSG_RESULT(yes)
+
+fi
+
+AC_SUBST(NEON_TARGET)
+AC_SUBST(NEON_OBJEXT)
+AC_SUBST(NEONOBJS)
+AC_SUBST(NEON_EXTRAOBJS)
+AC_SUBST(NEON_LINK_FLAGS)
+AC_SUBST(NEON_SUPPORTS_DAV)
+
+])
+
+# The libtoolized build case:
+AC_DEFUN([NEON_LIBTOOL_BUILD], [
+
+NEON_TARGET=libneon.la
+NEON_OBJEXT=lo
 
-AC_CHECK_FUNC(gethostbyname,
-       AC_MSG_RESULT(using libc's gethostbyname),
-       AC_CHECK_LIB(nsl,gethostbyname)
-       )
+NEON_COMMON_BUILD($#, $*)
 
-NEON_SSL
+])
+
+dnl Find 'ar' and 'ranlib', fail if ar isn't found.
+AC_DEFUN([NE_FIND_AR], [
+
+# Search in /usr/ccs/bin for Solaris
+ne_PATH=$PATH:/usr/ccs/bin
+AC_PATH_TOOL(AR, ar, notfound, $ne_PATH)
+if test "x$AR" = "xnotfound"; then
+   AC_MSG_ERROR([could not find ar tool])
+fi
+AC_PATH_TOOL(RANLIB, ranlib, :, $ne_PATH)
+
+])
+
+# The non-libtool build case:
+AC_DEFUN([NEON_NORMAL_BUILD], [
+
+NEON_TARGET=libneon.a
+NEON_OBJEXT=o
+
+AC_REQUIRE([NE_FIND_AR])
+
+NEON_COMMON_BUILD($#, $*)
+
+])
+
+AC_DEFUN([NE_SNPRINTF], [
+AC_CHECK_FUNCS(snprintf vsnprintf,,[
+   ne_save_LIBS=$LIBS
+   LIBS="$LIBS -lm"    # Always need -lm
+   AC_CHECK_LIB(trio, trio_vsnprintf,
+   [AC_CHECK_HEADERS(trio.h,,
+    AC_MSG_ERROR([trio installation problem? libtrio found but not trio.h]))
+    AC_MSG_NOTICE(using trio printf replacement library)
+    NEON_LIBS="$NEON_LIBS -ltrio -lm"
+    NEON_CFLAGS="$NEON_CFLAGS -DNEON_TRIO"],
+   [AC_MSG_NOTICE([no vsnprintf/snprintf detected in C library])
+    AC_MSG_ERROR([Install the trio library from http://daniel.haxx.se/trio/])])
+   LIBS=$ne_save_LIBS
+   break
+])])
+
+dnl Usage: NE_CHECK_SSLVER(variable, version-string, version-hex)
+dnl Define 'variable' to 'yes' if OpenSSL version is >= version-hex
+AC_DEFUN([NE_CHECK_SSLVER], [
+AC_CACHE_CHECK([OpenSSL version is >= $2], $1, [
+AC_EGREP_CPP(good, [#include <openssl/opensslv.h>
+#if OPENSSL_VERSION_NUMBER >= $3
+good
+#endif], [$1=yes], [$1=no])])])
+
+dnl Less noisy replacement for PKG_CHECK_MODULES
+AC_DEFUN([NE_PKG_CONFIG], [
+
+AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+if test "$PKG_CONFIG" = "no"; then
+   : Not using pkg-config
+   $4
+else
+   AC_CACHE_CHECK([for $2 pkg-config data], ne_cv_pkg_$2,
+     [if $PKG_CONFIG $2; then
+        ne_cv_pkg_$2=yes
+      else
+        ne_cv_pkg_$2=no
+      fi])
+
+   if test "$ne_cv_pkg_$2" = "yes"; then
+      $1_CFLAGS=`$PKG_CONFIG --cflags $2`
+      $1_LIBS=`$PKG_CONFIG --libs $2`
+      : Using provided pkg-config data
+      $3
+   else
+      : No pkg-config for $2 provided
+      $4
+   fi
+fi])
+
+dnl Check for OpenSSL
+AC_DEFUN([NEON_SSL], [
+
+AC_ARG_WITH(ssl, [AC_HELP_STRING([--with-ssl], [enable OpenSSL support])])
+
+AC_ARG_WITH(egd,
+[[  --with-egd[=PATH]       enable EGD support [using EGD socket at PATH]]])
+
+case $with_ssl in
+yes)
 
+   NE_PKG_CONFIG(NE_SSL, openssl,
+    [AC_MSG_NOTICE(using SSL library configuration from pkg-config)
+     CPPFLAGS="$CPPFLAGS ${NE_SSL_CFLAGS}"
+     NEON_LIBS="$NEON_LIBS ${NE_SSL_LIBS}"],
+    [# libcrypto may require -ldl if using the OpenSSL ENGINE branch
+     NE_SEARCH_LIBS(RSA_new, crypto, -ldl)
+     NE_SEARCH_LIBS(SSL_library_init, ssl)])
+
+   AC_CHECK_HEADERS(openssl/ssl.h openssl/opensslv.h,,
+   [AC_MSG_ERROR([OpenSSL headers not found, cannot enable SSL support])])
+
+   # Enable EGD support if using 0.9.7 or newer
+   NE_CHECK_SSLVER(ne_cv_lib_ssl097, 0.9.7, 0x00907000L)
+   if test "$ne_cv_lib_ssl097" = "yes"; then
+      AC_MSG_NOTICE([OpenSSL >= 0.9.7; EGD support not needed in neon])
+      neon_ssl_message="OpenSSL (0.9.7 or later)"
+   else
+      # Fail if OpenSSL is older than 0.9.6
+      NE_CHECK_SSLVER(ne_cv_lib_ssl096, 0.9.6, 0x00906000L)
+      if test "$ne_cv_lib_ssl096" != "yes"; then
+         AC_MSG_ERROR([OpenSSL 0.9.6 or later is required])
+      fi
+      neon_ssl_message="OpenSSL (0.9.6 or later)"
+
+      case "$with_egd" in
+      yes|no) ne_cv_lib_sslegd=$with_egd ;;
+      /*) ne_cv_lib_sslegd=yes
+          AC_DEFINE_UNQUOTED([EGD_PATH], "$with_egd", 
+                            [Define to specific EGD socket path]) ;;
+      *) # Guess whether EGD support is needed
+         AC_CACHE_CHECK([whether to enable EGD support], [ne_cv_lib_sslegd],
+        [if test -r /dev/random || test -r /dev/urandom; then
+           ne_cv_lib_sslegd=no
+         else
+           ne_cv_lib_sslegd=yes
+         fi])
+        ;;
+      esac
+      if test "$ne_cv_lib_sslegd" = "yes"; then
+        AC_MSG_NOTICE([EGD support enabled for seeding OpenSSL PRNG])
+        AC_DEFINE([ENABLE_EGD], 1, [Define if EGD should be supported])
+      fi
+   fi
+
+   NEON_SUPPORTS_SSL=yes
+   NEON_CFLAGS="$NEON_CFLAGS -DNEON_SSL"
+   NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_openssl"
+   ;;
+*) # Default to off; only create crypto-enabled binaries if requested.
+   neon_ssl_message="No SSL support"
+   NEON_SUPPORTS_SSL=no
+   NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_stubssl"
+   ;;
+esac
+AC_SUBST(NEON_SUPPORTS_SSL)
+])
+
+dnl Check for Kerberos installation
+AC_DEFUN([NEON_GSSAPI], [
+AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin)
+if test "x$KRB5_CONFIG" != "xnone"; then
+   ne_save_CPPFLAGS=$CPPFLAGS
+   ne_save_LIBS=$NEON_LIBS
+   NEON_LIBS="$NEON_LIBS `${KRB5_CONFIG} --libs gssapi`"
+   CPPFLAGS="$CPPFLAGS `${KRB5_CONFIG} --cflags gssapi`"
+   # MIT and Heimdal put gssapi.h in different places
+   AC_CHECK_HEADERS(gssapi/gssapi.h gssapi.h, [
+     NE_CHECK_FUNCS(gss_init_sec_context, [
+      ne_save_CPPFLAGS=$CPPFLAGS
+      ne_save_LIBS=$NEON_LIBS
+      AC_MSG_NOTICE([GSSAPI authentication support enabled])
+      AC_DEFINE(HAVE_GSSAPI, 1, [Define if GSSAPI support is enabled])
+      AC_CHECK_HEADERS(gssapi/gssapi_generic.h)
+      # MIT Kerberos lacks GSS_C_NT_HOSTBASED_SERVICE
+      AC_CHECK_DECL([GSS_C_NT_HOSTBASED_SERVICE],,
+        [AC_DEFINE([GSS_C_NT_HOSTBASED_SERVICE], gss_nt_service_name, 
+          [Define if GSS_C_NT_HOSTBASED_SERVICE is not defined otherwise])],
+        [#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#else
+#include <gssapi.h>
+#endif])])
+     break
+   ])
+   CPPFLAGS=$ne_save_CPPFLAGS
+   NEON_LIBS=$ne_save_LIBS
+fi])
+
+dnl Adds an --enable-warnings argument to configure to allow enabling
+dnl compiler warnings
+AC_DEFUN([NEON_WARNINGS],[
+
+AC_REQUIRE([AC_PROG_CC]) dnl so that $GCC is set
+
+AC_ARG_ENABLE(warnings,
+AC_HELP_STRING(--enable-warnings, [enable compiler warnings]))
+
+if test "$enable_warnings" = "yes"; then
+   case $GCC:`uname` in
+   yes:*)
+      CFLAGS="$CFLAGS -Wall -ansi-pedantic -Wmissing-declarations -Winline -Wshadow -Wreturn-type -Wsign-compare -Wundef -Wpointer-arith -Wcast-align -Wbad-function-cast -Wimplicit-prototypes -Wformat-security"
+      if test -z "$with_ssl" -o "$with_ssl" = "no"; then
+        # OpenSSL headers fail strict prototypes checks
+        CFLAGS="$CFLAGS -Wstrict-prototypes"
+      fi
+      ;;
+   no:OSF1) CFLAGS="$CFLAGS -check -msg_disable returnchecks -msg_disable alignment -msg_disable overflow" ;;
+   no:IRIX) CFLAGS="$CFLAGS -fullwarn" ;;
+   no:UnixWare) CFLAGS="$CFLAGS -v" ;;
+   *) AC_MSG_WARN([warning flags unknown for compiler on this platform]) ;;
+   esac
+fi
 ])
+
+dnl Adds an --disable-debug argument to configure to allow disabling
+dnl debugging messages.
+dnl Usage:
+dnl  NEON_WARNINGS([actions-if-debug-enabled], [actions-if-debug-disabled])
+dnl
+AC_DEFUN([NEON_DEBUG], [
+
+AC_ARG_ENABLE(debug,
+AC_HELP_STRING(--disable-debug,[disable runtime debugging messages]))
+
+# default is to enable debugging
+case $enable_debug in
+no) AC_MSG_NOTICE([debugging is disabled])
+$2 ;;
+*) AC_MSG_NOTICE([debugging is enabled])
+AC_DEFINE(NE_DEBUGGING, 1, [Define to enable debugging])
+$1
+;;
+esac])
+
+dnl Macro to optionally enable socks support
+AC_DEFUN([NEON_SOCKS], [
+
+AC_ARG_WITH([socks], AC_HELP_STRING([--with-socks],[use SOCKSv5 library]))
+
+if test "$with_socks" = "yes"; then
+  ne_save_LIBS=$LIBS
+
+  AC_CHECK_HEADERS(socks.h,
+    [AC_CHECK_LIB(socks5, connect,
+      [AC_MSG_NOTICE([SOCKSv5 support enabled])],
+      [AC_MSG_ERROR([could not find libsocks5 for SOCKS support])])],
+    [AC_MSG_ERROR([could not find socks.h for SOCKS support])])
+
+  CFLAGS="$CFLAGS -DNEON_SOCKS"
+  NEON_LIBS="$NEON_LIBS -lsocks5"
+  LIBS=$ne_save_LIBS
+
+fi])
+
+AC_DEFUN([NEON_WITH_LIBS], [
+AC_ARG_WITH([libs],
+[[  --with-libs=DIR[:DIR2...] look for support libraries in DIR/{bin,lib,include}]],
+[case $with_libs in
+yes|no) AC_MSG_ERROR([--with-libs must be passed a directory argument]) ;;
+*) ne_save_IFS=$IFS; IFS=:
+   for dir in $with_libs; do
+     ne_add_CPPFLAGS="$ne_add_CPPFLAGS -I${dir}/include"
+     ne_add_LDFLAGS="$ne_add_LDFLAGS -L${dir}/lib"
+     ne_add_PATH="${ne_add_PATH}${dir}/bin:"
+   done
+   IFS=$ne_save_IFS
+   CPPFLAGS="${ne_add_CPPFLAGS} $CPPFLAGS"
+   LDFLAGS="${ne_add_LDFLAGS} $LDFLAGS"
+   PATH=${ne_add_PATH}$PATH ;;
+esac])])
index d332354..27b74d1 100644 (file)
@@ -1,10 +1,11 @@
 #! /bin/sh
 # Originally from libxml, Copyright (C) Daniel Veillard
-# Modifications for neon Copyright (C) 2000 Joe Orton.
+# Modifications for neon Copyright (C) 2000-2002 Joe Orton.
 
 prefix=@prefix@
 exec_prefix=@exec_prefix@
 includedir=@includedir@
+libdir=@libdir@
 
 usage()
 {
@@ -15,21 +16,31 @@ Known values for OPTION are:
 
   --prefix=DIR         change neon prefix [default $prefix]
   --libs               print library linking information
+  --la-file            print location of libtool .la file
   --cflags             print pre-processor and compiler flags
   --help               display this help and exit
   --version            output version information
+  --support FEATURE     exit with success if feature is supported
+ Known features: dav [@NEON_SUPPORTS_DAV@], ssl [@NEON_SUPPORTS_SSL@], zlib [@NEON_SUPPORTS_ZLIB@]
+
 EOF
 
     exit $1
 }
 
+support()
+{
+    if test "$1" = "yes"; then
+       exit 0
+    else
+       exit 1
+    fi
+}
+
 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]*=//'` ;;
@@ -55,12 +66,33 @@ while test $# -gt 0; do
        ;;
 
     --cflags)
-               echo @NEON_INCLUDEDIR@ @NEON_CFLAGS@
+               echo -I${includedir}/neon @NEON_CFLAGS@
                ;;
 
     --libs)
-               echo -L@libdir@ @NEON_LIBS@
-               ;;
+       LIBS="-lneon @NEON_LIBS@"
+       # Don't add standard library paths
+       case "${libdir}" in
+       /usr/lib|/lib) ;;
+       *) LIBS="-L${libdir} ${LIBS}" ;;
+       esac
+       echo @user_LDFLAGS@ ${LIBS}
+       ;;
+
+    --la-file)
+       echo ${libdir}/libneon.la
+       ;;
+
+    --support)
+       shift
+
+       case "$1" in
+       ssl|SSL) support @NEON_SUPPORTS_SSL@ ;;
+       zlib|ZLIB) support @NEON_SUPPORTS_ZLIB@ ;;
+       dav|DAV) support @NEON_SUPPORTS_DAV@ ;;
+       *) support no ;;
+       esac
+       ;;
 
     *)
        usage
index 76b3c4e..de7f10a 100644 (file)
-# 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 
-
+#**** neon Win32 -*- Makefile -*- ********************************************
+#
+# Define DEBUG_BUILD to create a debug version of the library.
 
 !IF "$(OS)" == "Windows_NT"
 NULL=
-!ELSE 
+!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)"
+!ENDIF
+
+########
+# Support for Expat integration
+#
+# If EXPAT_SRC or EXPAT_INC are set, then assume compiling against a
+# pre-built binary Expat 1.95.X.  You can use either EXPAT_SRC 
+# to specify the top-level Expat directory, or EXPAT_INC to directly
+# specify the Expat include directory.  (If both are set, EXPAT_SRC
+# is ignored).
+#
+# If EXPAT_SRC and EXPAT_INC are not set, then the user can
+# still set EXPAT_FLAGS to specify very specific compile behavior.
+#
+# If none of EXPAT_SRC, EXPAT_INC and EXPAT_FLAGS are set, disable
+# WebDAV support.
+
+!IF "$(EXPAT_INC)" == ""
+!IF "$(EXPAT_SRC)" != ""
+EXPAT_INC = $(EXPAT_SRC)\Source\Lib
+!ENDIF
+!ENDIF
+
+BUILD_EXPAT = 1
+!IF "$(EXPAT_INC)" == ""
+!IFNDEF EXPAT_FLAGS
+EXPAT_FLAGS = /D NEON_NODAV
+BUILD_EXPAT =
+!ENDIF
+!ELSE
+EXPAT_FLAGS = /I "$(EXPAT_INC)" /D HAVE_EXPAT /D HAVE_EXPAT_H
+!ENDIF
+
+
+########
+# Support for OpenSSL integration
+!IF "$(OPENSSL_SRC)" == ""
+OPENSSL_FLAGS =
+!ELSE
+OPENSSL_FLAGS = /I "$(OPENSSL_SRC)\inc32" /D NEON_SSL
+!ENDIF
+
+########
+# Support for zlib integration
+!IF "$(ZLIB_SRC)" == ""
+ZLIB_FLAGS =
+ZLIB_LIBS =
+!ELSE
+ZLIB_FLAGS = /I "$(ZLIB_SRC)" /D NEON_ZLIB
+!IF "$(ZLIB_DLL)" == ""
+ZLIB_LIBS = "$(ZLIB_SRC)\zlibstat.lib"
+!ELSE
+ZLIB_FLAGS = $(ZLIB_FLAGS) /D ZLIB_DLL
+ZLIB_LIBS = "$(ZLIB_SRC)\zlibdll.lib"
+!ENDIF
+!ENDIF
+
+
+!IF "$(DEBUG_BUILD)" == ""
+INTDIR = Release
+CFLAGS = /MD /W3 /GX /O2 /D "NDEBUG"
+TARGET = .\libneon.lib
+!ELSE
+INTDIR = Debug
+CFLAGS = /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG"
+TARGET = .\libneonD.lib
+!ENDIF
+
+# Exclude stuff we don't need from the Win32 headers
+WIN32_DEFS = /D WIN32_LEAN_AND_MEAN /D NOUSER /D NOGDI /D NONLS /D NOCRYPT
 
 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= \
-       
+CPP_PROJ = /c /nologo $(CFLAGS) $(WIN32_DEFS) $(EXPAT_FLAGS) $(OPENSSL_FLAGS) $(ZLIB_FLAGS) /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\"
 LIB32=link.exe -lib
-LIB32_FLAGS=/nologo /out:"$(OUTDIR)\neons.lib" 
+LIB32_FLAGS=/nologo /out:"$(TARGET)"
+
 LIB32_OBJS= \
-       "$(INTDIR)\base64.obj" \
-       "$(INTDIR)\ne_xml.obj" \
        "$(INTDIR)\ne_alloc.obj" \
        "$(INTDIR)\ne_auth.obj" \
        "$(INTDIR)\ne_basic.obj" \
+       "$(INTDIR)\ne_compress.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"
+       "$(INTDIR)\ne_utils.obj"
 
-
-CLEAN :
-       -@erase "$(INTDIR)\base64.obj"
+!IF "$(BUILD_EXPAT)" != ""
+LIB32_OBJS= \
+       $(LIB32_OBJS) \
+       "$(INTDIR)\ne_207.obj" \
+       "$(INTDIR)\ne_xml.obj" \
+       "$(INTDIR)\ne_acl.obj" \
+       "$(INTDIR)\ne_props.obj" \
+       "$(INTDIR)\ne_locks.obj" 
+!ENDIF
+
+
+!IF "$(OPENSSL_SRC)" != ""
+LIB32_OBJS = $(LIB32_OBJS) "$(INTDIR)\ne_openssl.obj"
+!IFDEF OPENSSL_STATIC
+LIB32_OBJS = $(LIB32_OBJS) "$(OPENSSL_SRC)\out32\libeay32.lib" \
+                          "$(OPENSSL_SRC)\out32\ssleay32.lib"
+!ELSE
+LIB32_OBJS = $(LIB32_OBJS) "$(OPENSSL_SRC)\out32dll\libeay32.lib" \
+                          "$(OPENSSL_SRC)\out32dll\ssleay32.lib"
+!ENDIF
+!ELSE
+# Provide ABI-compatibility stubs for SSL interface
+LIB32_OBJS = $(LIB32_OBJS) "$(INTDIR)\ne_stubssl.obj"
+!ENDIF
+!IF "$(ZLIB_SRC)" != ""
+LIB32_OBJS = $(LIB32_OBJS) $(ZLIB_LIBS)
+!ENDIF
+
+
+ALL: ".\src\config.h" "$(TARGET)"
+
+CLEAN:
        -@erase "$(INTDIR)\ne_207.obj"
        -@erase "$(INTDIR)\ne_alloc.obj"
+       -@erase "$(INTDIR)\ne_acl.obj"
        -@erase "$(INTDIR)\ne_auth.obj"
        -@erase "$(INTDIR)\ne_basic.obj"
+       -@erase "$(INTDIR)\ne_compress.obj"
        -@erase "$(INTDIR)\ne_cookies.obj"
        -@erase "$(INTDIR)\ne_dates.obj"
        -@erase "$(INTDIR)\ne_i18n.obj"
@@ -165,240 +146,55 @@ CLEAN :
        -@erase "$(INTDIR)\ne_redirect.obj"
        -@erase "$(INTDIR)\ne_request.obj"
        -@erase "$(INTDIR)\ne_session.obj"
+       -@erase "$(INTDIR)\ne_openssl.obj"
+       -@erase "$(INTDIR)\ne_stubssl.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)"
+       -@erase "$(TARGET)"
+       -@erase ".\src\config.h"
 
-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) $< 
+"$(TARGET)": $(DEF_FILE) $(LIB32_OBJS)
+       -@if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+       $(LIB32) @<<
+  $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
 <<
 
-.cpp{$(INTDIR)}.sbr::
-   $(CPP) @<<
-   $(CPP_PROJ) $< 
+{src}.c{$(INTDIR)}.obj::
+       -@if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+       $(CPP) @<<
+  $(CPP_PROJ) $<
 <<
 
-.cxx{$(INTDIR)}.sbr::
-   $(CPP) @<<
-   $(CPP_PROJ) $< 
+".\src\config.h": config.hw
+       -@if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+       <<tempfile.bat
+  @echo off
+  copy .\config.hw .\src\config.h > nul
+  echo Created config.h from config.hw
 <<
 
-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 
-
+"$(INTDIR)\ne_207.obj":      .\src\ne_207.c
+"$(INTDIR)\ne_alloc.obj":    .\src\ne_alloc.c
+"$(INTDIR)\ne_acl.obj":      .\src\ne_acl.c
+"$(INTDIR)\ne_auth.obj":     .\src\ne_auth.c
+"$(INTDIR)\ne_basic.obj":    .\src\ne_basic.c
+"$(INTDIR)\ne_compress.obj": .\src\ne_compress.c
+"$(INTDIR)\ne_cookies.obj":  .\src\ne_cookies.c
+"$(INTDIR)\ne_dates.obj":    .\src\ne_dates.c
+"$(INTDIR)\ne_i18n.obj":     .\src\ne_i18n.c
+"$(INTDIR)\ne_locks.obj":    .\src\ne_locks.c
+"$(INTDIR)\ne_md5.obj":      .\src\ne_md5.c
+"$(INTDIR)\ne_props.obj":    .\src\ne_props.c
+"$(INTDIR)\ne_redirect.obj": .\src\ne_redirect.c
+"$(INTDIR)\ne_request.obj":  .\src\ne_request.c
+"$(INTDIR)\ne_session.obj":  .\src\ne_session.c
+"$(INTDIR)\ne_openssl.obj":  .\src\ne_openssl.c
+"$(INTDIR)\ne_stubssl.obj":  .\src\ne_stubssl.c
+"$(INTDIR)\ne_socket.obj":   .\src\ne_socket.c
+"$(INTDIR)\ne_string.obj":   .\src\ne_string.c
+"$(INTDIR)\ne_uri.obj":      .\src\ne_uri.c
+"$(INTDIR)\ne_utils.obj":    .\src\ne_utils.c
+"$(INTDIR)\ne_xml.obj":      .\src\ne_xml.c
diff --git a/neon/neon.pc.in b/neon/neon.pc.in
new file mode 100644 (file)
index 0000000..9d431de
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: neon
+Description: HTTP/WebDAV client library
+Version: @NEON_VERSION@
+Libs: -L${libdir} -lneon @NEON_LIBS@
+Cflags: -I${includedir}/neon @NEON_CFLAGS@
index 70f06c3..6eb5bf3 100644 (file)
@@ -1,2 +1,16 @@
 *.lo
 .libs
+Makefile
+libneon.la
+libneon.a
+neon_config.h
+*.diff
+c++.c
+checkincl.c
+*.*.*
+*.out
+*log
+*.bb
+*.da
+*.bbg
+*.[is]
index aa54969..4b330c2 100644 (file)
+Mon Jul  5 10:56:19 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (struct ne_decompress_s): Add acceptor field.
+       (gz_acceptor): New function.
+       (ne_decompress_reader): Fix to pass the user-supplied userdata
+       pointer to the user-supplied acceptor callback, via gz_acceptor.
+
+Mon Jul  5 10:52:40 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (do_inflate): Don't invoke the reader callback if
+       no bytes where produced by inflate().
+       (process_footer): Call the reader callback with size=0 to indicate
+       end-of-response for a good checksum match.
+
+Mon Jul  5 10:42:14 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (gz_destroy, gz_pre_send): New functions.
+       (ne_decompress_reader): Register pre-send and destroy hooks,
+       to initialize the compression context before each request attempt.
+       (Justin Erenkrantz).
+
+       * ne_private.h, ne_request.c (ne_kill_pre_send): New function.
+
+Sat Jul  3 14:33:56 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (auth_challenge): Fix to set got_qop in challenge
+       correctly (Hideaki Takahashi).
+
+Sun May  2 21:14:14 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix buffer overflow in RFC1036 date parser, CVE CAN-2004-0389.
+       
+       * ne_dates.c (RFC1036_FORMAT): Specify maximum field with for day
+       name.
+       (ne_rfc1123_parse, ne_rfc1036_parse, ne_asctime_parse): Make
+       thread-safe; remove static buffers.
+
+Thu Mar 11 23:38:01 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (provide_client_cert): Avoid malloc(0) when server
+       sends no CA names in CertificateRequest.
+       (ne_ssl_cert_write): Be paranoid and clear the OpenSSL error stack
+       on write failures.
+
+Sun May  2 16:59:39 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_dates.c [RFC1123_TEST] (main): Remove embedded test cases.
+
+Fri Apr 16 11:44:34 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (LIBS): Include NEON_LTLIBS.
+
+Wed Apr 14 10:39:53 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix format string vulnerabilities, CVE CAN-2004-0179:
+       
+       * ne_207.c (ne_simple_request): Avoid format string
+       vulnerabilities.
+
+       * ne_xml.c (ne_xml_set_error): Likewise.
+
+       * ne_props.c (propfind): Likewise.
+
+       * ne_locks.c (ne_lock, ne_lock_refresh): Likewise.
+
+Wed Apr 14 10:33:46 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (ah_post_send): Avoid false positives from gcc
+       -Wformat-security.
+
+Tue Apr 13 20:51:41 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c: Conditionally include gssapi_generic.h.
+
+Thu Apr  8 13:40:03 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.h: Don't use an anonymous enum for the proppatch
+       operation type, as some C++ compilers don't like it.
+
+Sun Mar 28 02:59:58 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (get_cnonce): Only use RAND_pseudo_bytes() if the PRNG
+       is seeded.
+
+Fri Mar 26 12:16:15 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (init_ssl): Just initialize the SSL library; delay
+       seeding PRNG until really necessary (performance fix).
+       (seed_ssl_prng): Split from init_ssl.
+       (ne_sock_connect_ssl): Call seed_ssl_prng().
+       (ne_sock_init): Adjust since init_ssl() can't fail.
+
+Fri Mar 26 12:01:38 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c: Include zlib.h before ne_*.h to fix issues
+       on platforms where zconf.h does "#define const".
+
+Fri Mar 26 11:55:06 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (get_gss_name, request_gssapi, gssapi_challenge,
+       auth_challenge): Implement the Negotiate auth scheme rather than
+       the obsolete GSS-Negotiate, and fix memory leaks.  Only accept
+       Negotiate challenges over SSL.
+
+Sun Feb 15 13:37:03 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_ssl.h: Define that ne_ssl_readable_dname returns UTF-8
+       encoded strings.
+
+       * ne_openssl.c (ne_ssl_readable_dname): Convert dname strings to
+       UTF-8, or use "???".
+
+Sat Jan 24 16:49:30 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (basic_challenge): Cast first parameter to ne_base64
+       to unsigned char * to fix warnings with some compilers.
+
+Thu Nov 13 20:38:28 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_begin_request): Presume a 205 response has no
+       message-body too; RFC2616 compliance fix.
+
+Thu Nov 13 20:31:07 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (ah_post_send): Treat a 401 response to a CONNECT
+       request as a valid proxy auth challenge, to work around buggy
+       proxies.
+
+Wed Oct 22 22:19:19 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_block): Treat an EOF without clean
+       SSL closure as a valid request body delimiter in any case.
+
+Wed Oct 22 21:44:48 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_defs.h (ne_attribute): New macro.
+
+       * ne_request.h, ne_session.h, ne_utils.h: Use ne_attribute instead
+       of littering #ifdef __GNUC__ and __attribute__ everywhere.
+
+Tue Oct 21 20:03:47 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix various strict signedness bugs:
+       
+       * ne_auth.c (auth_session): Make nonce_count argument unsigned.
+       (get_cnonce): Use unsigned data buffer.
+       (get_gss_name, gssapi_challenge): Use unsigned integers for status
+       variables.
+       (request_digest): Print nonce count as unsigned.
+       (verify_response): Make nonce_count unsigned.
+
+Tue Oct  7 20:52:06 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       When using SSL via a proxy, don't leak server auth credentials to
+       the proxy, and vice versa.
+       
+       * ne_auth.c (auth_session): Add context field.
+       (ah_create): Ignore challenges in a bad context.
+       (ah_pre_send, ah_destroy): Check that the request-private cookie
+       is not NULL.
+       (auth_register): Take an isproxy flag; set context field
+       appropriately in session structure.
+       (ne_set_server_auth, ne_set_proxy_auth): Adjust accordingly.
+
+Tue Oct  7 19:58:52 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_negotiate_ssl): If the returned cert chain was
+       NULL, try and create one from the peer certificate alone (fix for
+       use of SSLv2 connections).
+
+Mon Sep 29 21:57:40 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c [WIN32]: Include windows.h to fix non-SSL build.
+
+Thu Sep 25 20:05:18 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c (ne_xml_create): Specify an initial error string.
+
+Sun Sep 21 23:00:10 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_cookies.c (set_cookie_hdl): Strip whitespace around cookie
+       name and value.
+
+Sun Sep 14 10:50:01 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_addr_resolve): Use result of autoconf test for
+       working AI_ADDRCONFIG support.
+
+Sat Sep  6 12:05:00 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (check_identity): Take an optional server address
+       argument; check identity against IPaddress extension too if given.
+       (check_certificate): Optionally pass server address to
+       check_identity.
+       (populate_cert): Adjust accordingly.
+
+Thu Sep  4 21:41:38 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_init): Succeed even if PRNG was not seeded.
+
+Thu Sep  4 21:33:34 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_set_useragent): Build and store the entire
+       User-Agent header field in sess->user_agent.
+
+       * ne_request.c (add_fixed_headers): Adjust accordingly; avoid
+       unnecessary calls to ne_buffer_*.       
+
+Thu Sep  4 21:27:34 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Include netinet/tcp.h.
+       (ne_sock_connect): Disable the Nagle algorithm; thanks to Jim
+       Whitehead and Teng Xu for the analysis.
+
+Thu Sep  4 11:24:04 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_defs.h: Define ssize_t here for Win32.
+
+       * ne_socket.h: Don't define ssize_t here.
+
+Tue Sep  2 20:20:16 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (auth_challenge): Update to use ne_token not
+       split_string, patch by Tom Lee <i_am_gnomey@hotmail.com>.
+
+Wed Jul 30 21:54:38 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_cookies.c (set_cookie_hdl): Fix NULL pointer dereference;
+       thanks to Markus Mueller <markus-m.mueller@ubs.com>.
+
+Fri Jul 25 11:05:52 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (do_connect): On failure to connect, set error
+       string and call ne_sock_close directly rather than using
+       aborted(); fix leak of socket structure.
+
+Wed Jul 23 23:20:42 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix SEGV if inflateInit2 fails with Z_MEM_ERROR etc.    
+       
+       * ne_compress.c (set_zlib_error): New function.
+       (do_inflate, gz_reader): Use it.
+
+Wed Jul 23 22:50:50 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Add support for GSS-Negotiate; patch from Risko Gergely and Burjan
+       Gabor:
+
+       * ne_auth.c [HAVE_GSSAPI]: Include gssapi.h.
+       (auth_scheme): Add auth_scheme_gssapi.
+       (auth_session): Add gssapi_token.
+       (clean_session): Free gssapi_token.
+       (request_gssapi, get_gss_name, gssapi_challenge): New functions.
+       (tokenize): Handle challenge with single token.
+       (auth_challenge): Accept and process a GSS-Negotiate challenge.
+       (ah_pre_send): Send GSS-Negotiate handshake.
+
+Wed Jul 23 22:46:28 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.c (ne_207_set_response_handlers,
+       ne_207_set_propstat_handlers): Fix to match declarations (thanks
+       to Diego Tártara).
+
+Fri Jun 27 20:30:45 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c [OPENSSL_VERSION_NUMBER < 0x0090700fL]:
+       Fix build against OpenSSL < 0.9.7.
+
+Sun Jun 22 23:07:45 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_session_destroy): Replace unnecessary use of
+       NE_FREE with ne_free.
+       (set_hostinfo): Don't free hostport/hostinfo here.
+       (ne_session_proxy): Free existing proxy hostname here if
+       necessary.
+
+Sat Jun 21 12:58:25 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_begin_request): Set or clear is_http11 flag
+       for each request.
+
+Wed Jun 18 20:54:44 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Add AI_ADDRCONFIG support;
+       [USE_CHECK_IPV6]: Define only if __linux__.
+       (init_ipv6) [USE_CHECK_IPV6]: New conditional.
+       (ne_addr_resolve) [USE_ADDRCONFIG]: Use AI_ADDRCONFIG.
+
+Wed Jun 18 20:03:13 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_create): New function (renamed from
+       create_sock).
+       (ne_sock_connect): Take an ne_socket *, return int.
+       (ne_sock_accept): Likewise.
+       (ne_sock_close): Only call ne_close if fd is non-negative.
+
+       * ne_request.c (aborted): Handle NE_SOCK_* errors specially.
+       (do_connect): Adapt for ne_sock_create/connect interface.  Set
+       sess->connected here on success.
+       (open_connection): Don't set sess->connected here.
+
+Sun Jun 15 12:14:22 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_ssl.h (ne_ssl_cert_digest): Pass digest as a pointer rather
+       than an array.
+
+Sun Jun 15 11:00:09 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_stubssl.c (ne_ssl_cert_cmp): Add stub.
+
+Wed May 28 21:37:27 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_context_create): Enable workarounds in
+       OpenSSL for better interop with buggy SSL servers.
+
+Fri May 23 23:13:30 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_stubssl.c (ne_ssl_set_clicert): Add stub.
+
+Sat May 10 17:05:26 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c: Rename struct ne_xml_handler to struct handler.
+
+Thu May  8 20:55:46 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_clicert_read): Pass "b" to fopen.
+
+Tue May  6 22:08:08 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (check_certificate): Re-order verify failure
+       handling to allow caller to set a custom session error string.
+
+Tue May  6 20:21:27 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_md5.c (md5_stream): Restore.
+
+Sat Apr 26 19:21:03 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (te_hdr_handler): Treat presence of any T-E
+       response header as implying the response is chunked, regardless of
+       value.
+
+Sat Apr 26 18:11:24 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c: Rename struct ne_xml_nspace to struct namespace.
+
+Wed Apr 23 22:19:29 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_cert_export): Don't bother checking for
+       i2d_X509() failure; no OpenSSL code ever checks, so everyone's
+       doomed if it really can fail.
+
+Wed Apr 23 22:01:23 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_cert_import, ne_ssl_cert_export,
+       ne_ssl_cert_write): Clear OpenSSL error stack on errors.
+
+Wed Apr 23 18:23:53 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_stubssl.c (ne_ssl_cert_write, ne_ssl_cert_import,
+       ne_ssl_cert_export): Add stubs.
+
+Wed Apr 23 14:05:32 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_cert_write): New function.
+
+Tue Apr 22 23:21:22 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_unbase64): Optimise out some redundant branches.
+
+Tue Apr 22 20:24:44 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_cert_export, ne_ssl_cert_import,
+       ne_ssl_cert_cmp): New functions.
+
+Tue Apr 22 18:31:55 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_unbase64): New function.
+
+Tue Apr 22 15:53:41 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_base64): Fix encoding binary data; take unsigned
+       argument.
+
+Tue Apr 22 13:07:48 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_stubssl.c (ne_ssl_cert_validity): Add stub.
+
+Tue Apr 22 09:22:26 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_cert_validity): New function.
+       (asn1time_to_string): Format into a fixed-size buffer.
+
+Tue Apr 22 08:38:30 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (ne_lock_discover, ne_lock): Don't leak the cdata
+       buffer.
+
+       * ne_props.c (ne_propfind_destroy): Don't leak the value buffer.
+
+Mon Apr 21 23:52:25 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c (ne_xml_destroy): Free root element.
+
+Mon Apr 21 23:46:17 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (dup_client_cert): Set decrypted state; dup the
+       friendly name.
+       (ne_ssl_clicert_free): Free friendly name.
+
+Mon Apr 21 19:44:55 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_md5.h (ne_md5_buffer, ne_md5_stream): Remove unused
+       functions.
+
+Mon Apr 21 18:17:14 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c, ne_207.c: s/NE_ELM_/ELM_/ since no element ids are
+       exported.
+
+Mon Apr 21 16:38:14 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Redesign the XML interface: have startelm callback map {nspace,
+       name} onto a state integer or decline.  Remove "valid"/"invalid";
+       don't abort the parse if no handler accepts an element.  Push
+       cdata accumulation down into the caller; drop collect mode,
+       stripws mode.
+       
+       * ne_xml.h (ne_xml_elmid, struct ne_xml_elm): Removed.
+       (ne_xml_startelm_cb): Return a state/acceptance integer, take a
+       state integer, nspace, name and atts.
+       (ne_xml_endelm_cb, ne_xml_cdata_cb): Take a state integer.
+       (ne_xml_push_mixed_handler): Removed.
+       (ne_xml_push_handler): Removed element list argument.
+       (struct ne_xml_idmap, ne_xml_mapid): New interface.
+
+       * ne_xml.c (struct element): Replaces ne_xml_state.  Add name,
+       nspace, state fields.
+       (friendly_name, find_handler, parse_element,
+       ne_xml_push_mixed_handler, push_handler): Removed functions.
+       (declare_nspaces, expand_qname): Factored out from find_handler
+       and parse_element.
+       (start_element): Use expand_qname, declare_nspaces.  Find
+       appropriate handler here.  Guarantee not to pass a NULL atts array
+       to the start-element callback.  Drop collect mode.
+       (end_element): Drop collect mode
+       (ne_xml_push_handler): Fold push_handler back in.
+       (ne_xml_mapid): New function.
+
+       * ne_207.h (NE_ELM_*): Don't export element id.
+       (NE_207_STATE_PROP, NE_207_STATE_TOP): Export state integers.
+
+       * ne_207.c (struct ne_207_parser_s): Add cdata field.
+       (map207): Replace element list with idmap array.
+       (can_handle): New function, replacing check_context logic.
+       (start_element): Determine new state integer; only accept the
+       element in valid states.  Clear cdata.
+       (end_element): Use state rather than element id.  Do nothing for
+       end of 'response' element if element is incomplete.
+       (ne_207_create): Create cdata buffer.
+       (ne_207_destroy): Destroy cdata buffer.
+       (ne_207_ignore_unknown): Removed function.
+       (ne_simple_request): Don't call ne_207_ignore_unknown.
+
+       * ne_props.h (NE_PROPS_STATE_TOP): Define state.
+       
+       * ne_props.c (struct ne_propfind_handler_s): Add value and depth
+       fields.
+       (ELM_flatprop): Define state.
+       (flat_elms): Removed array.
+       (chardata): Append to value field when in ELM_flatprop state.
+       (startelm): Decline everything other than elements within the
+       'prop' state.  Collect flatprop contents.
+       (endelm): Collect flatprop contents.
+
+       * ne_locks.c (struct discover_ctx, struct lock_ctx): Store cdata.
+       (element_map): Replace element list with idmap array.
+       (can_accept): Replaces check_context callback.
+       (ld_startelm, lk_cdata, ld_cdata): New functions.
+
+Mon Apr 14 00:04:20 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.h (ne_207_start_response, ne_207_end_response,
+       ne_207_start_propstat, ne_207_end_propstat): Use ANSI-style
+       function pointers in typedefs.
+
+       * ne_207.c (struct ne_207_parser_s): Updated accordingly.
+
+Mon Apr 14 00:02:10 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_block): Better error messages for
+       invalid chunks, don't use strncmp for a two-character comparison.
+
+Mon Apr  7 22:26:50 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_stubssl.c (ne_ssl_cert_identity): New function.
+
+Mon Apr  7 22:16:16 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (struct ne_ssl_certificate_s): Add identity field.
+       (check_identity): Add optional identity argument.
+       (populate_cert): Retrieve cert identity using check_identity.
+       (check_certificate): Pass extra NULL to check_identity.
+       (ne_ssl_cert_identity): New function.
+       (ne_ssl_cert_free): Free the identity field.
+
+Mon Apr  7 21:29:54 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (check_identity): Take a string hostname rather
+       than a session object.
+       (check_certificate): Adjust accordingly.
+
+Sun Apr  6 21:26:05 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (NE_HEX2ASC): Cast result to char to avoid warnings
+       with some compilers.
+Sun Apr  6 20:11:42 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_readable_dname): Include commonName or
+       emailAddress in returned string if either is the only attribute.
+
+Sun Mar 30 10:54:20 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Split decryption of client certs into two steps
+       
+       * ne_openssl.c (ne_ssl_clicert_encrypted, ne_ssl_clicert_decrypt):
+       New functions.
+       (ne_ssl_client_cert_s): Add p12 and decrypted fields.
+       (find_friendly_name): New function.
+       (get_friendly_name): Removed function.
+       (ne_ssl_clicert_read): Drop password callback; on decrypt failure,
+       extract friendly name and set decrypted state of clicert.
+
+Sun Mar 30 10:54:01 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_stubssl.c (ne_ssl_clicert_encrypted, ne_ssl_clicert_decrypt):
+       New stubs.
+       (ne_ssl_clicert_read): Adjusted for API change.
+
+Sat Mar 29 14:23:37 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_dname_cmp): New function.
+
+       * ne_stubssl.c (ne_ssl_dname_cmp): New function.
+
+Sat Mar 29 13:52:47 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (struct ne_ssl_client_cert_s): Add 'friendly_name'
+       field.
+       (get_friendly_name, ne_ssl_clicert_name): New functions.
+       (ne_ssl_clicert_read): Store the cert's friendly name.
+
+       * ne_stubssl.c (ne_ssl_clicert_name): New function.
+
+Sat Mar 29 13:16:14 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_openssl.c (ne_ssl_clicert_owner): New function.
+
+Fri Mar 28 22:12:57 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_stubssl.c (ne_ssl_cert_digest): New function.
+
+       * ne_openssl.c (ne_ssl_cert_digest): New function.
+
+Wed Mar 26 20:41:57 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_ssl_trust_cert) [NEON_SSL]: Only build when SSL
+       support is present.
+
+Wed Mar 26 20:01:00 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Begin abstraction of SSL layer to better isolate
+       SSL-library-specific code, and to improve certificate handling
+       interface.
+
+       Drop support for PEM-encoded client certificates.
+
+       * ne_session.h (ne_ssl_trust_cert): Replaces ne_ssl_load_ca, in
+       conjunction with ne_ssl_load_cert.
+       (ne_ssl_trust_default_ca): Replaces ne_ssl_load_default_ca.
+       (ne_ssl_keypw_prompt): Removed function, no longer needed.
+       (ne_ssl_set_clicert): Replaces ne_ssl_load_pkcs12, in conjunction
+       with ne_ssl_clicert_read.
+       (ne_ssl_provide_clicert): Replaces ne_ssl_provide_ccert, callback
+       type changed.
+       
+       * ne_openssl.c: New file; much code moved from ne_session.c.
+
+       * ne_privssl.h: New file, private interface between ne_socket.c
+       and ne_openssl.c.
+       
+       * ne_ssl.h: New file.
+
+       * ne_private.h (struct ne_session_s): Store pointers to
+       ne_ssl_client_cert etc opaque objects, not OpenSSL structures.
+       
+       * ne_session.c: Most of ne_ssl_* moved to ne_openssl.c.
+       (ne_session_create, ne_session_destroy): Use ne_ssl_cert_* etc to
+       manage cert objects.
+
+       * ne_socket.c (struct ne_socket_s): Replace SSL *, SSL_CTX *
+       pointers with an ne_ssl_socket * pointer.
+       (readable_ossl, error_ossl, read_ossl, write_ossl, ne_sock_close):
+       Compensate for above change.
+       (ne_sock_use_ssl): Removed function.
+       (ne_sock_switch_ssl): Pass in SSL * as void for time being.
+       (ne_sock_connect_ssl): Renamed and cleaned up version of
+       ne_sock_use_ssl_os.
+       (ne_sock_sslsock): New function.
+
+       * Makefile.in: Add deps for ne_openssl.c.
+       
+Sun Mar 23 13:02:58 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_set_useragent): Use ne_malloc.
+
+Sat Mar 22 21:06:45 2003  Joe Orton  <joe@manyfish.co.uk>
+
+     * ne_socket.c (raw_connect): Fill in sin6_family or sin_family
+     since AIX 4.3 fails to do so.
+
+Wed Mar 19 20:44:11 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c [NEON_SSL] (free_client_cert): Build conditional on
+       NEON_SSL.
+
+Mon Mar 17 20:33:32 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Include netdb.h conditional on HAVE_NETDB_H. (fix
+       build for older versions of CygWin).
+
+Sun Mar 16 23:30:20 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_identity): Fix leak of subject alt. name
+       structures.
+
+Sun Mar 16 19:21:22 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (free_client_cert): New function.
+       (ne_session_destroy, ne_ssl_load_pem, ne_ssl_load_pkcs12): Call
+       it; prevent memory leak if ne_ssl_load_{pem,pkcs12} are called >1
+       per session.
+
+Sun Mar 16 18:00:34 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (provide_client_cert): Free peer certificate after
+       use.
+       (ne_session_destroy): Free client cert and key if present.
+
+Sun Mar 16 14:23:05 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c [HAVE_EXPAT]: Include xmlparse.h for bundled expat
+       build.
+
+       * ne_utils.c: Only include expat.h if HAVE_XMLPARSE_H is not
+       defined.
+
+Wed Mar 12 15:04:13 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c (struct redirect): Add 'valid' field.
+       (post_send): Set and clear 'valid' to keep track of whether stored
+       URI is valid.
+       (ne_redirect_location): Return NULL if stored URI is not valid.
+
+Wed Mar 12 14:52:49 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_free): Zero-initialize structure after
+       free'ing.
+
+Tue Mar 11 22:01:11 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c (ne_redirect_location): Return NULL if no redirect
+       session is registered, rather than SEGV; Ralf Mattes
+       <rm@fabula.de>.
+
+Sun Mar  9 16:33:24 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix a memory leak if an XML parse error occurs during a PROPFIND
+       response:
+       
+       * ne_props.c (ne_propfind_current_private): Return NULL if no
+       propset is being processed.
+       (free_propset): Free propset href here.
+       (end_response): Don't free propset href here.  Set current field
+       of handler to NULL after free'ing propset.
+       (ne_propfind_destroy): Free current propset if one was being
+       processed.
+
+Sun Mar  9 11:53:58 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.c (ne_207_destroy): Fix possible leak of reason_phrase
+       string.
+
+Sun Mar  9 11:01:15 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (ne_parse_statusline): Use ne_strclean.
+       
+       * ne_session.c (ne_get_error): Use ne_strclean.
+
+Sun Mar  9 10:53:52 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c: Remove broken "UTF-8 decoding" support used for libxml
+       1.x.
+
+Sun Mar  9 09:55:26 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c: Drop support for expat < 1.95.0 and libxml 1.x.
+
+       * ne_utils.c (version_string): Include expat version string.
+
+Sun Mar  9 09:54:00 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Don't declare h_errno on Win32 either.
+
+Sun Mar  9 08:49:40 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (do_concat) [HAVE_STPCPY]: Use stpcpy rather than
+       strlen/memcpy, when available.
+
+Mon Mar  3 22:17:04 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c [!USE_GETADDRINFO && !HAVE_DECL_H_ERRNO): Declare
+       h_errno (fix build on SCO OpenServer 5.0).
+
+Sat Mar  1 21:22:19 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c (free_redirect): Fix once-per-session memory leak.
+
+Sat Mar  1 20:23:47 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Add implemention of simple memory leak tracking, for testing
+       purposes.
+
+       * ne_alloc.c [NEON_MEMLEAK] (tracking_malloc, ne_free_ml,
+       ne_malloc_ml, ne_calloc_ml, ne_realloc_ml, ne_strdup_ml,
+       ne_strndup_ml, ne_memleak_dump): New functions.
+
+       * memleak.h: New header.
+
+Sat Mar  1 13:44:26 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       First step towards automated memory leak tests.
+       
+       * ne_alloc.c (ne_free): New function.
+
+       * All files: replace use of free() with ne_free().
+
+Sat Mar  1 09:48:39 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_path_unescape): Fix memory leak on invalid URI.
+
+Sat Mar  1 08:03:18 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_strclean): New function.
+
+Wed Feb 26 21:45:12 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_begin_request, proxy_tunnel, open_connection)
+       [NEON_SSL] Don't build CONNECT tunnel support code if SSL is not
+       supported.
+
+Wed Feb 26 21:44:18 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (ne_debug_init): Allow ne_debug_init(NULL, 0) to turn
+       off debugging.  Fix to produce debug output if the any of the
+       specified "channels" are active, not all.  (also fixing
+       NE_DBG_FLUSH support).
+
+Tue Feb 25 23:12:31 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (process_footer): Mention number of extra bytes in
+       error message for oversized footer.
+
+Sun Feb 23 21:19:20 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (verify_response): Fix to parse nonce count as hex
+       string rather than decimal; fix verification of >9 responses.
+
+Thu Feb 13 20:35:45 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_set_useragent): Fix to append "neon/x.y.z" to
+       application-supplied token rather prepend.
+
+Thu Feb 13 09:06:22 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (open_connection): Fix for CONNECT tunnelling
+       (regression since 0.23.x), thanks to Nathan Hand
+       <Nathan.Hand@defence.gov.au>.
+
+Mon Feb  3 22:10:54 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Implement Daniel Stenberg's trick to avoid the slow AF_UNSPEC
+       lookups on Linux:
+       
+       * ne_socket.c (init_ipv6, ipv6_disabled): New function and global.
+       (ne_sock_init): Call init_ipv6.
+       (ne_addr_resolve) [USE_GETADDRINFO]: Pass AF_INET in hints if
+       ipv6_disabled is set.
+
+Mon Feb  3 20:55:47 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c [__hpux && USE_GETADDRINFO]: Undefine
+       USE_GETADDRINFO to work around broken implementation in HP-UX
+       11.11.
+
+Mon Jan 27 21:39:31 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (write_raw): Fix for handling EINTR during write(),
+       from Sergey N Ushakov.
+
+Thu Jan 16 21:59:03 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Allow _init after _finish to succeed: Sergey N Ushakov.
+       * ne_socket.c (init_result): New global variable.
+       (ne_sock_init): Use init_result global rather than result.
+       (ne_sock_finish): Clear init_result.
+
+Fri Dec 27 17:03:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (build_request): Remove redundant call to
+       ne_buffer_clear.
+
+Fri Dec 27 14:38:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_request_create): strdup the method string.
+       (ne_request_destroy): free the method.
+
+Mon Dec 23 17:04:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_write, ne_read, ne_close, ne_errno): Renamed
+       macros from NEON_WRITE, NEON_READ, NEON_CLOSE, NEON_ERRNO.
+       All callers changed.
+
+Mon Dec 23 16:58:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Add proper Win32 socket error handling, merged efforts of Johan
+       Lindh and Sergey N Ushakov <ushakov@int.com.ru>:
+       
+       * ne_socket.c (ne_errno, NE_ISINTR, NE_ISRESET, NE_ISCLOSED): New
+       macros.
+       [WIN32] (print_error): New function.
+       (set_strerror) [WIN32]: Use print_error.
+       (readable_raw, read_raw, write_raw): Use new error handling
+       macros.
+       (ne_addr_resolve) [WIN32]: Use WSAGetLastError() rather than
+       h_errno.
+       (ne_addr_error) [WIN32]: Use print_error.
+
+Tue Dec 10 21:41:26 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_iaddr_print): Renamed from ne_addr_print for
+       consistency with other ne_iaddr_ functions.
+
+Sun Dec  8 20:08:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (get_cnonce): Use GetCurrentThreadId() on Win32.
+
+Sun Nov 24 18:45:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c: Remove qop_values and algorithm_names arrays.
+       (request_digest): Inlined qop, algorithm name lookups accordingly.
+
+Sun Nov 24 16:45:39 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.h: Renamed ne_request_auth typedef to ne_auth_creds.
+
+       * ne_auth.c (auth_session): Renamed reqcreds, recreds_ud fields to
+       creds, userdata.
+       (auth_register, ne_set_proxy_auth, ne_set_server_auth): Update for
+       ne_request_auth rename.
+
+Fri Nov 22 17:39:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (auth_challenge): Fix support for RFC2617-style digest
+       auth; notice the qop= parameter in challenge.  Fix leak of parsed
+       qop array.
+
+Fri Nov 22 17:08:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (get_cnonce): Rewrite to use either pseudo-random data
+       from the SSL library (if available), or really-not-random data
+       from gettimeofday/getpid otherwise.
+
+Sun Nov 17 22:13:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_addr_print) [USE_GETADDRINFO]: Use the SACAST()
+       macro.
+
+Sun Nov 17 19:29:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_connect): Make address argument const.
+       (raw_connect): Make address argument const; adjust to use a copy
+       of the sockaddr structure, which is correct anyway.
+       (ne_addr_first, ne_addr_next): Make return pointer const.
+
+       * ne_private.h (struct host_info): Store current address as const.
+
+Sun Nov 17 19:03:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_register_progress): Removed function.
+
+       * ne_socket.h (ne_block_reader, ne_progress,
+       ne_register_progress): Removed.
+
+       * ne_request.c (do_connect): Don't call ne_register_progress.
+
+       * ne_request.h: Add ne_block_reader typedef.
+
+       * ne_session.h: Include sys/types.h; add ne_progress typedef.
+       
+Sun Nov 17 18:59:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_iaddr_make, ne_iaddr_cmp, ne_iaddr_free):
+       New functions.
+       
+Mon Nov 11 19:51:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Allow discovery of document encoding.
+       
+       * ne_xml.c [HAVE_EXPAT]: (struct ne_xml_parser_s): Add encoding
+       field.  (decl_handler): New function.
+       (ne_xml_doc_encoding): New function.
+
+Mon Nov 11 19:48:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c (sax_handler): Use sax_error for fatal error callback.
+
+Fri Oct 11 23:50:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (struct ne_session_s): Change 'connected' to be a
+       simple boolean flag.
+
+       * ne_session.c (ne_close_connection): Treat 'connected' as a
+       boolean.
+
+       * ne_request.c (open_connection): Greatly simplified.
+
+Fri Oct 11 00:46:52 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c (end_propstat): Fix NULL pointer dereference
+       if no status object is given.
+
+Tue Oct  8 20:10:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c (ne_xml_create) [!HAVE_EXPAT]: Set 'replaceEntities'
+       flag in created parser so that entities are dereferenced in
+       attribute values.
+
+Mon Oct  7 22:08:46 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (init_ssl): Attempt to seed PRNG using EGD socket at
+       path EGD_PATH or a set of predetermined locations if EGD_PATH is
+       not defined.  No longer try $EGDSOCKET or $HOME/.entropy.
+
+Mon Oct  7 21:32:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (register_hooks): Removed function.
+       (auth_register): Fold in register_hooks.
+
+Tue Sep 24 21:24:44 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_request_create): Pass Request-URI to
+       create_request hooks.
+
+Tue Sep 24 20:42:45 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c [__hpux]: Define _XOPEN_SOURCE_EXTENDED to 1, to
+       pick up h_errno definition on HP-UX 10.20.
+
+Wed Sep 18 21:46:28 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (struct ne_decompress_s): Add zstrinit field.
+       (gz_reader): Set zstrinit after inflateInit2 succeeds.
+       (ne_decompress_destroy): Only call inflateEnd if zstrinit is set.
+
+Wed Sep 18 19:56:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c: Remove incomplete domain support.
+
+Tue Sep 17 21:05:11 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix rejection of server certificates which have commonName as the
+       least specific attribute.
+
+       * ne_session.c (check_identity): Don't ignore commonName if it is
+       the least specific attribute.
+
+Tue Sep 10 21:08:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (lookup_host): Destroy cached address if resolve
+       fails; fix segfault if a second request in the session is
+       dispatched after the DNS lookup fails on the first.
+
+Mon Sep  9 22:26:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (RETRY_RET): Treat SSL truncation as a legitimate
+       persistent connection timeout.
+
+Fri Aug 30 21:58:45 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_block): Clear can_persist flag if an
+       EOF was read (fix for read-till-EOF response terminated by an
+       unclean SSL shutdown).
+
+Mon Aug 26 18:05:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Fix HAVE_LIMITS check (Blair Zajac).
+
+Sun Aug 25 23:29:06 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (do_connect): Add debug message for connection
+       attempt.
+
+Sun Aug 25 22:54:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h (ne_addr_print): Make address argument const.
+
+Sun Aug 25 11:52:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_addr_print): New function.
+
+Sun Aug 25 10:09:10 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix interop with Tomcat/3.2 SSL server, which performs an unclean
+       shutdown on an HTTP/1.0 response without a C-L header.
+
+       * ne_request.c (read_response_block): Ignore SSL connection
+       truncation for a read-till-EOF response, where no reseponse
+       content has been read yet.
+       (ne_read_response_block): Always increase 'total' counter.
+
+Sun Aug 25 08:47:41 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (aborted): Handle code=0 case specifically, and
+       NE_SOCK_* as default.
+
+Sun Aug 25 08:24:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h: Add `NE_SOCK_TRUNC' return value.
+       
+       * ne_socket.c (error_ossl): Return NE_SOCK_TRUNC when an EOF is
+       received without a close_notify.
+
+Sat Aug 24 17:37:14 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h (ne_inet_addr): New type.
+       (ne_addr_first, ne_addr_next): New public interface.
+       (ne_sock_connect): Change first parameter to ne_inet_addr.
+
+       * ne_socket.c: Predefine ne_inet_addr for ne_socket.h, replacing
+       ne_raw_addr.
+       (ne_addr_first, ne_addr_first): Renamed from addr_first,
+       addr_next; return type now ne_inet_addr; made public.
+       (ne_sock_connect): Fold in make_socket() macro; just connect to
+       single IP address passed in.
+
+       * ne_private.h (struct host_info): Renamed 'addr' to 'address',
+       dded 'current' field, removed 'resolved' field.
+
+       * ne_request.c (lookup_host): Adjust for addr->address rename.
+       (ne_begin_request): Call lookup_host if 'address' is NULL in
+       host_info structure, don't use 'resolved' flag.
+       (do_connect): Replaces init_socket; factor more code out from
+       open_connection.  Loop over available addresses until an
+       ne_sock_connect call succeeds.
+       (open_connection): Moved code into do_connect.
+
+       * ne_session.c (ne_session_destroy): Adjust for addr->address
+       rename.
+
+Sat Aug 24 13:45:26 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (count_concat, do_concat): Compact into while()
+       loops.
+
+Sat Aug 24 13:36:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (VERSION_PRE11): Removed macro.
+       (struct ne_session_s): Add is_http11 field; removed version_major,
+       version_minor fields.
+
+       * ne_request.c (add_fixed_headers): Use is_http11 flag rather than
+       VERSION_PRE11 macro.
+       (ne_begin_request): Set and use is_http11 flag.
+
+       * ne_session.c (ne_version_pre_http11): Use is_http11 flag.
+       (ne_session_create): Don't set version_major, version_minor fields.
+
+Sat Aug 24 09:00:13 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (struct ne_request_s): Removed abs_path field.
+       (ne_set_request_uri): Removed function.
+       (ne_request_create): Set req->uri to be the actual Request-URI.
+       Don't use an absoluteURI in Request-URI if using SSL via a proxy
+       tunnel, or if passed-in path does not begin with a '/'.
+       (build_request): Use pre-determined Request-URI.
+       (proxy_tunnel): Pass true Request-URI to ne_request_create.
+       (ne_request_destroy): Don't free abs_path.
+
+Sat Aug 24 00:37:25 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (aborted): Fix handling of _CLOSED and _TIMEOUT
+       socket errors, and of non-socket errors.  Presume ne_sock_error
+       cannot return NULL.
+
+Sat Aug 24 00:07:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_cookies.c (set_cookie_hdl): Ensure that each cookie field is
+       safe to free().
+
+Fri Aug 23 23:46:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (aborted): Close the connection after setting the
+       session error, otherwise the socket error is lost.
+
+Fri Aug 23 22:50:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_init): Set SIGPIPE disposition before SSL
+       library initalization, so it happens even if SSL library
+       initialization fails.
+
+Fri Aug 23 22:03:26 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c [USE_GETADDRINFO] (make_socket): Pass SOCK_STREAM to
+       socket() rather than ai_socktype: on RHL6.2, ai_socktype is
+       returned as zero.
+
+Wed Aug 21 18:06:36 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Reinstate stdlib.h include.
+
+       * ne_socket.h: Reinstate sys/socket.h include.
+
+Wed Aug 21 12:58:47 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_addr_resolve): Accept IPv6 addresses enclosed in
+       square brackets.
+
+Wed Aug 21 09:37:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_parse): Parse literal IPv6 address using the
+       RFC2732 `[address]' syntax.
+
+Mon Aug 19 17:18:45 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_addr_error): Override a horribly generic error
+       message from gai_strerror().
+
+Mon Aug 19 16:24:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h: Remove netinet/in.h etc includes.
+       (ne_sock_addr): Add new opaque type.
+       (ne_addr_resolve, ne_addr_result, ne_addr_error, ne_addr_destroy):
+       New functions.
+       (ne_sock_connect): Changes address argument to `ne_sock_addr *'.
+       (ne_name_lookup): Removed function.
+
+       * ne_socket.c: Added netinet/in.h etc includes.
+       (ne_sock_addr, ne_raw_addr): Define types.
+       (make_socket): New macro.
+       (ne_addr_resolve): Replace ne_name_lookup; store results (multiple
+       addresses if returned) in returned ne_sock_addr object.  Use
+       getaddrinfo() if available.
+       (raw_connect, addr_first, addr_next, ne_addr_result,
+       ne_addr_error, ne_addr_destroy): New functions.
+       (ne_sock_connect): Re-implement to loop through available
+       addresses until a connect() succeeds; use make_socket, raw_connect
+       auxiliaries.
+
+       * ne_private.h (struct host_info): Store an ne_sock_addr pointer.
+
+       * ne_request.c (lookup_host): Use new ne_addr_* interface.
+
+       * ne_session.c (ne_session_destroy): Destroy address objects.
+       
+Mon Aug 19 00:19:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Move prng_seeded inside ifdef NEON_SSL region to
+       prevent unused variable warning for non-SSL build.
+
+Sun Aug 18 23:21:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (ne_strerror): Return buffer.
+
+Sun Aug 18 23:17:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (set_error): Use ne_strnzcpy.
+
+Sun Aug 18 23:14:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_strerror): Use ne_strnzcpy.
+
+Sun Aug 18 23:11:45 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (ne_strnzcpy): New macro.
+
+Sun Aug 18 22:48:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_init): Check directly for SIGPIPE
+       definition rather than HAVE_SIGPIPE.
+
+Sun Aug 18 13:49:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (set_hostport): Use %u for printing unsigned int.
+
+Sun Aug 18 13:47:43 2002  Joe Orton  <joe@manyfish.co.uk>
+       
+       * ne_utils.h (NE_DBG_SSL): New constant.
+
+       * ne_session.c [NEON_SSL] (everywhere): Use NE_DBG_SSL channel for
+       debugging messages.
+
+Sun Aug 18 08:17:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (match_hostname): Fix to use case-insensitive
+       string comparison.
+
+Sun Aug 18 08:10:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_identity): Check the commonName if no
+       alt. names of DNS type were found.
+
+Sun Aug 18 07:39:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_identity): Use the most specific commonName
+       attribute found, not the first.  (for RFC2818 compliance)
+
+Sun Aug 18 01:54:53 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (match_hostname): Invert return value.
+       (check_identity): New function; split out commonName check from
+       check_certificate, check subjectAltName extension instead if
+       present.
+       (check_certificate): Use check_identity.
+
+Sat Aug 17 19:59:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_certificate): Extend debugging code to dump
+       the whole certificate chain, but #if 0 it by default.
+
+Mon Aug 12 12:04:51 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (aborted): Use NE_FMT_SSIZE_T to print ssize_t
+       value.
+
+Mon Aug 12 11:08:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Support PRNG seeding via EGD to make SSL work on platforms which
+       lack /dev/random:
+
+       * ne_socket.c (init_ssl): New function.
+       (ne_sock_init): Call init_ssl, set prng_seeded global on success.
+       (ne_sock_use_ssl_os): Fail early if prng_seeded is not set, and
+       RAND_status returns false.
+
+Tue Aug  6 07:18:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_use_ssl_os): Remove goto-based error
+       handling.  Don't call SSL_shutdown after SSL_connect fails.
+
+Mon Aug  5 23:18:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_ssl_keypw_prompt): Don't set SSL_CTX default
+       password callbacks, since these are never invoked.  Implement
+       once, stub for !NEON_SSL is no longer needed.
+
+Mon Aug  5 21:01:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_ssl_load_pem): Pass private key prompt callback
+       to PEM_read_X509, PEM_read_PrivateKey (patch by Daniel Berlin).
+       Also handle errors properly; call ERR_get_error() to pop the
+       errors of the error stack.
+
+Mon Aug  5 20:15:10 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (provide_client_cert): Increase reference count on
+       key and certificate, to prevent them being free'd too early.
+
+Sun Aug  4 22:35:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Fix `retry_after_abort' test in request.c:
+
+       * ne_request.c (send_request): Don't use the 'persisted' flag
+       until after a new connection has been opened, when it may have
+       been reset.
+
+Sun Aug  4 17:26:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (struct ne_request_s): Remove reqbuf field.
+       (ne_request_create, ne_request_destroy): Don't (de)allocate reqbuf.
+       (build_request): Allocate the returned buffer internally.
+       (ne_begin_request): Destroy the buffer after use.
+
+Sun Aug  4 15:36:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_ssl_load_pem): Close file after use.
+
+Sun Aug  4 12:55:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Factor out EPIPE, ECONNRESET handling from write_raw:
+       
+       * ne_socket.c (MAP_ERR): New macro.
+       (write_raw, error_ossl): Use MAP_ERR.
+
+Sun Aug  4 12:25:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_switch_ssl): New function.
+
+Sun Aug  4 12:24:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_switch_ssl): New function, really just for
+       test suite.
+
+Sat Aug  3 22:11:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (ne_forget_auth): Fix segfault if either server or
+       proxy auth is not in use.
+
+Sat Aug  3 22:06:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c (create, post_send, ne_redirect_register,
+       ne_redirect_location): Updated for new hook interface.
+
+Sat Aug  3 19:02:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Adjustment of hook interface and use: fixing a design flaw causing
+       a segfault in the auth hooks when two requests are used
+       concurrently for a single session during a CONNECT tunnel.
+       
+       * ne_request.h, ne_session.h:
+       (ne_get_request_private, ne_get_session_private): Replace
+       ne_request_hook_private, ne_session_hook_private.
+       (ne_set_session_private, ne_set_request_private): Replace
+       ne_hook_session_accessor, ne_hook_request_accessor.
+
+       * ne_request.h (ne_create_request_fn, ne_pre_send_fn,
+       ne_post_send_fn): Add ne_request pointer as first argument.
+       (ne_hook_destroy_request): Take ne_destroy_req_fn function.
+       (ne_hook_destroy_session): Take ne_destroy_sess_fn function.
+       
+       * ne_request.c (struct ne_request_s): Renamed `accessor_hooks'
+       field to `private'.
+       (get_private): Renamed from call_access; don't invoke function.
+       (ne_null_accessor): Removed function.
+
+       * ne_auth.c (struct auth_class): Store hook id.
+       (auth_session): Remove auth_request pointer.
+       (ah_create): Store auth_request pointer as request-private data.
+       (ah_pre_send, ah_post_send, ah_destroy): Retrieve auth_request
+       pointer from request-private data.
+       (register_hooks, ne_forget_auth): Use
+       ne_{get,set}_session_private.
+
+       * ne_locks.c (struct lh_req_cookie): New structure.
+       (struct ne_lock_store_s): Remove submit_locks field.
+       (lk_create, lk_pre_send, submit_lock, ne_lock_using_resource,
+       ne_lock_using_parent, lk_destroy): Adjust to use lh_req_cookie
+       pointer as request-private data.
+
+       * ne_cookies.c (create, pre_send): Adjust for hook prototype
+       changes.
+
+Wed Jul 31 23:46:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c [NEON_SSL]: Include limits.h for INT_MAX definition.
+
+Mon Jul 29 20:55:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (struct auth_class): New structure; abstracts out
+       proxy/server generic auth handling more cleanly.
+       (ah_server_class, ah_proxy_class): Declare variables.
+       (auth_session): Reference an auth_class structure.
+       (auth_register): Replaces auth_create.
+       (ne_set_server_auth, ne_set_proxy_auth): Simplify, use
+       auth_register.
+       (everywhere): Reference req_hdr etc via ->spec-> reference.
+
+Sun Jul 28 12:29:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (proxy_tunnel): Reset 'persisted' flag, so that a
+       newly tunnelled connection is not treated as persistent.
+
+Sun Jul 28 12:26:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (CONCAT2, CONCAT3, CONCAT4): Removed macros.
+
+Thu Jul 25 23:16:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (send_request): Don't clear retry until a
+       status-line has been read.
+
+Thu Jul 25 00:03:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (basic_challenge, request_basic): Use ne_concat not
+       the CONCAT? macros.
+
+       * ne_basic.c (ne_mkcol): Use ne_concat not the CONCAT2 macro.
+
+Wed Jul 24 00:16:39 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (count_concat, do_concat): Factored out from
+       ne_buffer_concat.
+       (ne_buffer_concat): Rewrite to use count_concat, do_concat.
+       (ne_concat): New (resurrected) function.
+
+Thu Jul 18 21:52:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (proxy_tunnel): Don't use server.hostport in
+       Request-URI; always include `:port' even if default port is used;
+       fix CONNECT through Inktomi Traffic-Server.
+
+Thu Jul 18 21:33:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (aborted, ne_set_request_body_fd): Use ne_strerror.
+
+       * ne_session.c (ne_ssl_load_pem, ne_ssl_load_pkcs12): Use ne_strerror.
+
+       * ne_basic.c (get_to_fd): Use ne_strerror.
+
+Thu Jul 18 20:19:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_strerror): New function.
+
+       * ne_socket.c (set_strerror): Move portability logic to
+       ne_strerror; just use that here.
+
+Thu Jul 18 20:00:46 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (read_raw, write_raw): Don't re-evaluate 'errno',
+       per Ulrich Drepper's advice.
+
+Wed Jul 17 23:47:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (struct ne_socket_s): Store buffer for error string.
+       (set_error, set_strerror): New macros.
+       (everywhere): Use set_error, set_strerror or ne_snprintf to set
+       the socket error string.
+
+Wed Jul 17 23:19:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (ne_version_match): Fix inverted minor version test.
+
+Sun Jul 14 20:13:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.h (ne_uri): Store port as unsigned.
+
+       * ne_uri.c (ne_uri_defaultport): Return unsigned int, and zero for
+       undefined port.
+
+Sun Jul 14 20:07:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_session_proxy): Take port parameter as unsigned
+       int, as per ne_session_create.
+
+Sun Jul 14 20:03:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (strip_eol): Take ssize_t 'len' parameter.
+       (read_message_header): Use ssize_t for 'n'.
+
+Sun Jul 14 12:45:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_use_ssl_os): Unconditionally enable
+       SSL_MODE_AUTO_RETRY now OpenSSL 0.9.6 is required.
+
+Sun Jul 14 12:15:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.h (NE_XML_MEDIA_TYPE): New definition.
+
+       * ne_acl.c (ne_acl_set),
+       * ne_props.c (ne_proppatch, propfind): Use NE_XML_MEDIA_TYPE,
+       rather than hard-coding the incorrect "text/xml" media type.
+
+Sun Jul 14 10:53:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (ne_version_match): Replace ne_version_minimum.
+
+Sat Jul 13 11:40:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_negotiate_ssl): Include socket error string in
+       session error if SSL negotiation fails.
+
+Sat Jul 13 11:27:50 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (error_ossl): New function.
+       (ERROR_SSL_STRING): Removed macro.
+       (CAST2INT): New macro; safety harness for OpenSSL compatibility.
+       (read_ossl, write_ossl): Use error_ossl, CAST2INT.
+       (ne_sock_use_ssl_os): Use error_ssl.
+
+Sat Jul 13 11:16:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Define ECONNRESET as WSAECONNRESET on Win32.
+
+Sat Jul 13 10:10:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (struct ne_session_s): Replace 'reqcount' with
+       'persisted' flag.
+
+       * ne_request.c (ne_end_request): Set 'persisted' flag if
+       connection is left open.
+       (send_request): Adjust to allow retry if 'persisted' flag is set.
+       (init_socket): Clear 'persisted' flag here...
+       (open_connection): ... rather than here.
+
+Wed Jul 10 22:51:39 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (RETRY_RET): Retry on an NE_SOCK_RESET too.
+       (send_request): Fix to only retry if not on the first request on a
+       connection (close_not_retried test).
+
+Sun Jul  7 20:49:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h: Add NE_SOCK_RESET return value; improve comments.
+
+       * ne_socket.c (read_raw, write_raw): Return NE_SOCK_RESET if an
+       ECONNRESET error is received when reading or writing.
+
+Sat Jul  6 13:30:15 2002  Joe Orton  <joe@manyfish.co.uk>
+       
+       * ne_request.c (read_status_line, discard_headers): New functions,
+       split out from send_request.
+       (send_request_body): Move debugging here from send_request.
+       (RETRY_RET): Renamed from CAN_RETRY.
+       (send_request): Simplify: remove complex 100-continue graceful
+       failure logic; use read_status_line, discard_headers, RETRY_RET.
+       Fix to only send request body once (expect_100_once test case).
+       Fix to not return NE_RETRY if reading status-line other than the
+       first fails (fail_eof_continued test case).
+
+Fri Jul  5 21:47:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (build_request): Fix from previous commit: clear
+       the buffer before building the request.
+
+Fri Jul  5 21:00:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (build_request): Fold Host header and Request-Line
+       into single ne_buffer_concat call.  Don't set req->use_expect100
+       here.  Fold an if/else into an ?:.  Optimise to use
+       ne_buffer_append to add 100-continue, user-supplied headers, and
+       trailing EOL, since they all have known lengths.
+       (send_request): Take request data as argument.
+       (ne_begin_request): Call build_request here; pass to send_request.
+       Move Expect100 logic here.
+
+Fri Jul  5 17:12:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_read_file): Removed function.
+
+Fri Jul  5 17:10:11 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (process_footer): Take unsigned char buffer.
+       Store calculated CRC in a uLong.
+       (do_inflate, gz_reader): Cast buffers to unsigned char for
+       strict compatibility with zlib interface.
+
+Wed Jul  3 19:21:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (struct ne_request_s): Use a fixed char array for
+       respbuf field.
+       (ne_request_create, ne_request_destroy): Don't allocate respbuf
+       dynamically.
+       (send_request): Move 'buffer' to appropriate scope.
+       (ne_request_dispatch): Remove 'buffer'; read into respbuf.
+
+Tue Jul  2 08:35:05 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (proxy_tunnel): Remove redundant sess->connected
+       assignment.
+
+Sun Jun 30 21:04:50 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_begin_request): Only set host->resolved if
+       lookup is successful.
+
+Sun Jun 30 18:25:51 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (SOCK_ERR): New macro.
+       (struct ne_request_s): Remove 'forced_close' field.
+       (aborted): Renamed from set_sockerr; also closes connection and
+       prints message in debug log.
+       (send_request_body): Don't use set_sockerr or set forced_close.
+       (read_response_block, read_message_header): Use SOCK_ERR; adjust
+       to use aborted().
+       (ne_read_response_block, read_response_headers): Don't set
+       forced_close.
+       (CAN_RETRY): New macro.
+       (send_request): Adjust to use CAN_RETRY(); use aborted() to make
+       sure connection is closed in error cases.
+       (ne_begin_request): Don't close connection here in error cases;
+       don't use forced_close.
+       (open_connection): Adjust to use aborted() not set_sockerr().
+
+Sun Jun 30 17:26:41 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_close_connection): Clarify debug messages.
+
+Sun Jun 30 14:36:11 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_block): Fail on chunk size lines
+       which contain no valid chunk size digits.
+
+Sun Jun 30 12:35:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_negotiate_ssl): Use ne_get_session rather
+       than req->session.
+
+       * ne_request.c (struct header_handler, struct body_reader,
+       struct ne_request_s): Moved from ne_private.h.
+
+Sun Jun 30 12:13:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Cleanup of response handling:
+       
+       * ne_private.h (struct ne_response): Replace 'is_chunked' field with
+       'mode' enum.
+
+       * ne_request.c (te_hdr_handler): Set mode.
+       (connection_hdr_handler): Reset can_persist for 'close'.
+       (clength_hdr_handler): New function.
+       (ne_request_create): Use clength_hdr_handler to parse Content-Length
+       header.
+       (read_response_block, ne_read_response_block): Adapt for 'mode' enum; 
+       simplify.
+       (normalize_response_length): Removed function.
+       (ne_begin_request): Fold in normalize_response_length logic.
+       (ne_end_request): Simplify logic.
+
+Sun Jun 30 11:08:26 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c: Remove X509_NAME workaround in favour of a
+       neon.mak change.
+
+Tue Jun 25 23:14:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c: Undefine X509_NAME if it is defined (by a Windows
+       header).
+
+Tue Jun 25 22:51:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c: Rename READ_BUFFER to RDBUFSIZ.
+
+Tue Jun 25 21:07:13 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (struct host_info): Store port as unsigned int.
+
+       * ne_session.c (set_hostport, set_hostinfo, ne_session_create):
+       Take port argument as unsigned int.
+
+       * ne_socket.c (ne_sock_connect): Take port argument as unsigned
+       int.
+
+Tue Jun 25 20:59:14 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.h [__GNUCC__] (NE_DEBUG): Remove implementation using
+       GNU C extensions.
+
+Sun Jun 23 22:47:52 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (set_request_uri): Renamed from ne_set_request_uri;
+       made static.
+       (ne_request_create): Update accordingly.
+
+       * ne_private.h (ne_set_request_uri): Removed prototype.
+       
+Sun Jun 23 15:40:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (send_request, ne_request_destroy): Free
+       reason_phrase now it is malloc-allocated.
+
+Sun Jun 23 14:59:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Simplify given loss of const qualifier on ne_status.reason_phrase:
+
+       * ne_props.c (struct propstat): Remove r_phrase field.
+       (end_propstat, free_propset): Use status.reason_phrase not
+       r_phrase field.
+
+Sun Jun 23 14:42:22 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.h (ne_207_end_response, ne_207_end_propstat): Remove
+       status_line parameter from callbacks.
+
+       * ne_207.c (struct ne_207_parser_s): Remove status_line field.
+       (end_element): Don't store status_line.
+       (handle_error): Drop status_line argument, recreate dummy status
+       line from status object.
+       (end_response, end_propstat): Drop status_line arguments.
+
+       * ne_props.c (end_propstat, end_response): Drop status_line
+       arguments.
+
+Sun Jun 23 14:39:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.h (ne_status): Remove const qualifier from
+       'reason_phrase' field.
+       
+       * ne_utils.c (ne_parse_statusline): strdup the reason_phrase on
+       successful return.
+
+Sun Jun 23 11:39:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (struct ne_decompress_s): Replace footer union
+       with buffer.
+       (BUF2UINT): New macro.
+       (process_footer): Convert footer to integer in a portable manner,
+       using BUF2UINT.
+
+Sun Jun 23 09:05:25 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (ne_decompress_s): Use unsigned int for 32-bit
+       integers, not uLong (fix for 64-bit platforms).
+
+Wed Jun 19 18:46:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_session_destroy): Don't leak the proxy
+       hostname.
+
+Sun Jun 16 14:09:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_block): Use NE_FMT_SSIZE_T rather
+       than %d, cast field precision argument to int.
+       (ne_pull_request_body): Use ssize_t for store callback return
+       value, use NE_FMT_SSIZE_T rather than %d, cast field precision
+       argument to int.
+
+Sun Jun 16 12:15:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_negotiate_ssl): Don't leak peer certificate in
+       error cases.  Fix spelling mistake in error message.
+
+Sun Jun 16 11:23:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (open_connection): When SSL negotation fails after
+       doing CONNECT request, use ne_close_connection so that
+       sess->connection is reset to 0, and ne_sock_close isn't called
+       twice for the socket.
+
+Wed Jun 12 23:22:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c (ne_proppatch): Add missing call to
+       ne_lock_using_resource.
+
+Mon Jun 10 20:45:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (verify_response): Remove redundant prototype, fix
+       sscanf format string to use signed integer.
+
+Mon Jun 10 20:13:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (do_inflate): Continue calling inflate() whilst
+       unconsumed input remains: fix from Justin Erenkrantz
+       <jerenkrantz@apache.org>.
+
+Mon Jun 10 19:53:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_readline): If a complete line is found in
+       the buffer, avoid the memmove() and simply copy the line directly
+       out of the buffer.
+
+Sun Jun  9 11:39:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c (post_send): Perform simple relative URI
+       resolution.
+
+Tue Jun  4 16:51:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_path_parent): Simplify.
+
+Mon Jun  3 17:50:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_cookies.c (set_cookie_hdl): Avoid free'ing cookie name/value,
+       thanks to Dan Mullen.
+
+Mon Jun  3 17:45:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_base64): Use size_t for outlen.
+
+Mon Jun  3 17:42:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.h, ne_socket.h [WIN32]: Move ssize_t definition to
+       ne_socket.h.
+
+Mon Jun  3 17:27:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_block): Use correct types for
+       passing to/from ne_sock_*.
+
+Mon Jun  3 11:32:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (ne_decompress_destroy): Don't fail if response
+       reader callback is never invoked.
+
+Sun Jun  2 12:51:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (read_ossl, read_raw): Call readable_{ossl,raw}
+       function here.
+       (ne_sock_read, ne_sock_peek, ne_sock_readline): Remove explicit
+       calls to ops->readable before ops->read.
+
+Thu May 30 22:00:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (ne_sock_readline): Optimise to use socket read
+       buffer directly, and use ->read (and ->readable) functions.
+
+Tue May 28 17:00:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_session_destroy): Don't free proxy.hostport,
+       since it's no longer set.
+
+Sun May 26 19:11:46 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c: #error if no expat header is configured; flatten
+       nested #if's, include libxml/xmlversion.h if present.
+
+Sun May 26 19:09:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c: Include libxml/xmlversion.h if present.
+
+Sun May 26 11:55:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (set_hostport): Renamed from get_hostport: set
+       host_info field directly; take defaultport argument.
+       (set_hostinfo): Don't use get_hostport.
+       (ne_session_create): Use set_hostinfo and set_hostport; pass
+       in default port correctly for http:/https:.
+
+Thu May 23 19:44:44 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c (resolve_nspace): Split out from parse_element.
+       (ne_xml_get_attr): Take parser object, and optional
+       namespace; resolve the namespace if necessary.
+       (parse_element): Use resolve_nspace.
+
+       * ne_props.c (startelm): Use new ne_xml_get_attr interface.
+
+Wed May 22 22:29:05 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_dates.c: Renamed HAVE_TM_GMTOFF to HAVE_STRUCT_TM_TM_GMTOFF
+       from use of AC_CHECK_MEMBERS.
+
+Tue May 21 21:21:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c (ne_redirect_register): Drop confirm, notify,
+       userdata arguments.
+       (struct redirect): Drop most fields; add a uri structure.
+       (auto_redirect): Removed function.
+       (post_send): Remove functionality which retries a request with a
+       different URI to automatically follow redirects.  Qualify the URI
+       if non-absolute.
+       (create): Remove now redundant code.
+       (ne_redirect_location): Return an ne_uri object.
+
+Sun May 19 18:53:22 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_set_useragent): Use strcat/malloc/strcpy
+       directly, rather than CONCAT2; allow compiler optimisations.
+       (AGENT): Renamed from NEON_USERAGENT, append space.
+
+Sun May 19 17:31:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Move everything exported by ne_socket.h into ne_*/NE_* namespace;
+       purge inappropriate and unused interfaces.  Renaming done by Olof
+       Oberg.
+       
+       * ne_socket.h:
+
+       (SOCK_FULL): Removed constant.
+       (sock_call_progress, sock_transfer, sock_sendline,
+       sock_send_string, sock_readfile_blocked): Removed functions.
+       
+       (NE_SOCK_ERROR, NE_SOCK_TIMEOUT, NE_SOCK_CLOSED):
+       Renamed constants.
+
+       (ne_progress, ne_block_reader): Renamed types.
+
+       (ne_register_progress, ne_sock_init, ne_sock_exit, ne_sock_read,
+       ne_sock_peek, ne_sock_block, ne_sock_fullwrite, ne_sock_readline,
+       ne_sock_connect, ne_sock_accept, ne_sock_fd, ne_sock_error,
+       ne_sock_read_timeout, ne_name_lookup, ne_service_lookup,
+       ne_sock_use_ssl, ne_sock_use_ssl_os): Renamed functions.
+       
+       * ne_private.h, ne_request.c, ne_session.c: Update accordingly.
+
+       * ne_request.c (build_request): Return the ne_buffer pointer.
+       (send_request): Remove redundant strlen(), use known buffer
+       length.
+
+       * ne_request.h: Drop ne_block_reader definition.
+
+Sun May 19 13:32:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_get_session, ne_get_request): Take const
+       request pointer.
+
+Sun May 19 13:21:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_buffer_ncreate): Renamed from
+       ne_buffer_create_sized.
+
+       * ne_session.c (check_certificate, provide_client_cert): Update
+       accordingly.
+
+       * ne_request.c (ne_request_create): Update accordingly.
+       
+Sun May 19 13:12:14 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_token): Drop quotes parameter.
+       (ne_qtoken): Split out from ne_token.
+
+       * ne_basic.c (dav_hdr_handler, ne_content_type_handler): Use
+       ne_qtoken.
+
+       * ne_compress.c (find_token): Removed function.
+       (gz_reader): Compare header value directly against "gzip",
+       remove a stale comment.
+
+Sun May 19 09:45:28 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h (sock_fullread): Return ssize_t; takes buflen as
+       size_t.
+       (sock_read, sock_peek): Fix prototypes to match actual definition.
+
+       * ne_socket.c (write_raw): Return ssize_t.
+
+Sat May 18 14:53:45 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (ne_buffer): Remove separate struct ne_buffer_s
+       definition.
+
+       * ne_string.c (ne_buffer_create_sized): Don't use struct
+       ne_buffer_s.
+
+Sun May 12 11:33:02 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_base64): Moved from base64.c.
+
+       * base64.c, base64.h: Removed files.
+
+       * Makefile.in: Updated accordingly.
+
+       * ne_auth.c: Don't include base64.h.
+
+Sun May 12 11:26:05 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (ne_utf8_decode, ne_utf8_encode): Removed functions.
+
+Sat May 11 15:42:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       As part of patch from Olof Oberg <mill@pedgr571.sn.umu.se>:
+       
+       * ne_request.h (ne_destroy_fn): Renamed from typo'ed
+       ne_destory_fn.
+
+       * ne_request.c (ne_request_destroy, ne_hook_destroy_request,
+       ne_hook_destroy_session): Update accordingly.
+
+       * ne_session.c (ne_session_destroy): Update accordingly.
+
+Thu May  9 21:44:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Major improvements to socket layer to incorporate socket read
+       buffering and rewrite sock_readline, and add an abstraction layer
+       to simplify SSL support.  Grunt work by Jeff Johnson
+       <jbj@redhat.com>
+       
+       * ne_socket.c (struct iofns): New type.
+       (struct nsocket_s): Store 'ops' pointer to I/O functions in use
+       for the socket.  Add buffer, bufpos, bufavail fields for read
+       buffering.
+       (sock_block, sock_read, sock_peek): Reimplement to add read
+       buffer, simplify to use I/O functions abstraction.
+       (readable_raw, read_raw, write_raw, readable_ossl, read_ossl,
+       write_ossl): Factored out from sock_read/fullwrite, avoiding
+       in-lined ifdefs.
+       (sock_fullwrite): Just use ops->write.
+       (sock_readline): Simplify greatly to exploit read-buffering, return
+       ssize_t.
+       (sock_fullread): Simplify, removing unnecessary local variables,
+       return ssize_t.
+       (create_sock, sock_enable_ssl_os): Set ops pointer.
+
+Wed May  8 11:54:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (sock_name_lookup): Avoid casts; use INADDR_NONE.
+       [!INADDR_NONE]: Define to (unsigned long) -1.
+
+Wed May  1 22:19:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.h (ne_lock): Document that ->token and ->owner fields
+       must be malloc-allocated if non-NULL.
+
+Wed May  1 22:15:41 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (get_ltoken_hdr): New function.
+       (ne_lock): Correctly parse Coded-URL from Lock-Token response
+       header.
+
+Wed May  1 22:03:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c (post_send): Adjust for ne_uri_parse handling of
+       unspecified port.
+
+Wed May  1 22:00:50 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (provide_client_cert): Fail if peer certificate not
+       known when client cert requested.
+
+Wed May  1 21:58:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.h (ne_ssl_provide_fn): Adjust callback typedef to
+       return void.
+
+Wed May  1 21:52:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.h: Remove NE_SERVERAUTH and NE_AUTHPROXY; fix
+       NE_PROXYAUTH description.
+
+Wed May  1 21:32:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_parse): For consistency, port field is 0 if
+       unspecified.
+
+Tue Apr 30 10:05:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (shave_string): Removed function.
+
+Tue Apr 23 21:19:53 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c (start_propstat, startelm): Use ne_realloc not
+       realloc (thanks to Jeff Johnson).
+
+Tue Apr 23 20:55:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c: Include parser.h or libxml/parser.h, depending on
+       which is found.
+
+Mon Apr 15 00:37:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (build_request, send_request): Simplify logic.
+
+Sun Apr 14 16:59:50 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c: Remove unused accidental 'propstat' global.
+       (struct propstat): Add r_phrase field.
+       (end_propstat): Dup the reason_phrase string.
+       (free_propset): Free the reason_phrase.  Avoid another possible
+       free(NULL) call.
+
+Sun Apr 14 12:00:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_content_type_handler): For text/* media types,
+       use default charset of ISO-8859-1.
+
+Sat Apr 13 23:11:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.h: Include trio.h if NEON_TRIO is defined.
+
+Sun Apr  7 17:38:14 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_headers): Don't zero-initialize hdr.
+
+Sun Apr  7 17:15:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_headers): Ignore whitespace between
+       header name and colon, simplify logic a little.
+
+Sun Apr  7 14:09:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_ssl_readable_dname): New function.
+
+Sun Apr  7 12:32:25 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_buffer_destroy): Remove redundant check for data
+       pointer being NULL.
+
+Wed Apr  3 19:44:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Optimisation/simplification of header name hashing.
+
+       * ne_request.c (hash_and_lower): Renamed from hdr_hash; convert
+       string to lower-case in-place too.
+       (lower_string): Removed function.
+       (ne_add_response_header_handler): Use hash_and_lower rather than
+       lower_string.
+       (HH_ITERATE): Change parameter name to 'ch'.
+
+Fri Mar 29 23:00:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_parse): Minor optimisation.
+
+Mon Mar 25 21:45:36 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Pass a lock context around during LOCK processing; search for the
+       correct <activelock> element in the response body.
+       
+       * ne_locks.c (ne_lock_create): Don't take a path argument.
+       (ne_unlock): Constify lock parameter.
+       (discover_results): Don't parse out href here...
+       (ld_create): do it here instead; renamed from create_private.
+       (lk_startelm): New function.
+       (lk_endelm): Renamed from end_element_lock.
+       (ne_lock): Require a Lock-Token response header; pass lock context
+       to callbacks.  Copy lock back out.
+
+Mon Mar 25 21:35:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.h (NE_SSL_FAILMASK): New constant.
+       (NE_SSL_*): Shift right one bit.
+
+Mon Mar 25 21:21:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_close_connection): Return void.
+
+Mon Mar 25 20:09:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c (free_propset): Avoid free(NULL).
+
+Mon Mar 11 19:59:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (ne_lock_using_parent): Iterate over the lock list by
+       hand: check for infinite depth locks with cover the parent too
+       (fixing if_covered_child test).
+
+Mon Mar 11 19:25:44 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_request_dispatch): Move variable to scope in
+       which is is used.
+
+Sun Mar 10 22:04:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (NEON_BASEOBJS): Always build ne_compress.o.
+
+Sun Mar 10 22:01:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c [!NEON_ZLIB] (ne_decompress_reader,
+       ne_decompress_destroy): Add stubs.
+
+Sun Mar 10 21:42:11 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (struct discover_ctx): Store an ne_session pointer.
+       (discover_results): If lock URI is not an absoluteURI, qualify it
+       using the server host/port/scheme from the session.  Don't leak
+       the lock object.
+       (create_private): Simplify, use ne_lock_create.
+
+Thu Mar  7 20:08:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_defaultport): Fix default port number for https
+       scheme.
+
+Wed Mar  6 21:22:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (lk_pre_send): Use an absoluteURI in the If: header.
+
+Wed Mar  6 21:15:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_unparse): New function.
+
+Tue Mar  5 22:57:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_cmp): Compare hostnames and schemes
+       case-insensitively, and compare empty abspath and "/" as
+       equivalent, as per RFC 2616.
+
+Tue Mar  5 20:53:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (ne_uri_defaultport): New function.
+
+Mon Mar  4 21:10:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.h (ne_uri): Renamed from struct uri.
+
+       * ne_uri.c (ne_path_parent): Renamed from uri_parent.
+       (ne_path_has_trailing_slash): Renamed from uri_has_trailing_slash.
+       (uri_abspath, uri_absolute): Removed.
+       (ne_uri_parse): Renamed from uri_parse, don't take a 'defaults'
+       parameter.
+       (ne_uri_free): Renamed from uri_free.
+       (ne_path_unescape): Renamed from uri_unescape.
+       (ne_path_escape): Renamed from uri_abspath_escape.
+       (ne_uri_cmp): Renamed from uri_cmp.
+       (ne_path_compare): Renamed from uri_compare.
+       (ne_path_childof): Renamed from uri_childof.
+
+       * ne_basic.c, ne_locks.c, ne_uri.c, ne_redirect.c, ne_session.c,
+       ne_session.h: all callers changed.
+
+Mon Mar  4 01:03:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (strip_eol): Fix potential segfault.
+
+Mon Mar  4 00:38:10 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (insert_lock): New function.
+       (ne_lockstore_add, submit_lock): use insert_lock.
+
+Mon Mar  4 00:33:39 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (ne_lockstore_remove): Free list item.
+
+Mon Mar  4 00:31:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (free_list): Really destroy the lock.
+       (ne_lock_free): Don't free the lock object itself.
+
+Mon Mar  4 00:17:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_request_destroy): Free accessor hook list.
+
+Sun Mar  3 20:35:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Changes to lock interface; replacing "lock session" with a lock
+       store, which can be registered with an ne_session.  Lock objects
+       now store URI as complete URI structure.
+       
+       * ne_locks.h (struct ne_lock): Store URI as complete URI
+       structure.  Remove next/prev fields.
+       (ne_lock_store): New type.
+
+       * ne_locks.c (struct lock_list): New type.
+       (struct ne_lock_store_s): Replaces ne_lock_session_s; store
+       lock_list pointers for stored locks, cursor, and locks to
+       submit.
+       (ne_lock_create): New function.
+       (lk_create): Renamed from create.
+       (lk_pre_send): Renamed from pre_send; adjust for lock list
+       type and to use URI path.
+       (free_list): New function; split out from old 'destroy'.
+       (lk_destroy): Renamed from destroy; use free_list.
+       (ne_lockstore_create, ne_lockstore_destroy, 
+       ne_lockstore_first, ne_lockstore_next): New functions.
+       (ne_lockstore_register): Most of old ne_lock_register.
+       (submit_lock): Adjusted for lock_list type.
+       (ne_lockstore_findbyuri): Renamed from ne_lock_find; use
+       full URI structure.
+       (ne_lock_using_resource, ne_lock_using_parent): Adjusted
+       for lock_list/full URI changes.
+       (ne_lock_iterate): Removed function.
+       (ne_lockstore_add, ne_lockstore_remove): Renamed from
+       ne_lock_add, ne_lock_copy; adjusted for lock_list/full URI.
+       (ne_lock_copy): Adjusted for full URI.
+       (ne_lock_create, ne_lock_destroy): New function.
+       (ne_lock, ne_lock_refresh, ne_unlock): Adjusted for full URI.
+
+Sun Mar  3 15:23:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c (uri_cmp): New function.
+
+Sun Mar  3 11:01:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_fill_server_uri): New function.
+
+Mon Feb 25 21:25:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (version_string): Add zlib version.
+
+Mon Feb 25 20:49:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * (everywhere): Replace use of snprintf, vsnprintf with
+       ne_snprintf, ne_vsnprintf so that trio replacements are used when
+       appropriate.
+
+       * ne_dates.h: Pick up ne_utils.h for ne_{v,}snprintf defines.
+       
+Sun Feb 24 11:23:05 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.h: Define ne_snprintf, ne_vsnprintf for trio or
+       non-trio builds.
+
+Sun Feb 24 11:20:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (check-incl): Add target to check that each header
+       file can be included standalone.
+
+Sun Feb 24 11:17:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.h: Add missing sys/types.h include.
+
+Sun Feb 24 11:12:22 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.h: Remove HTTP_QUOTES, HTTP_WHITESPACE globals.
+
+       * ne_cookies.c (set_cookie_hdl): Don't use HTTP_QUOTES,
+       HTTP_WHITESPACE globals.
+
+Wed Feb 20 19:32:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (set_sockerr, ne_set_request_body_fd,
+       send_request): Use ne_set_error rather than accessing session
+       error directly.
+
+Tue Feb 19 21:34:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (version_string) [NEON_SOCKS]: Mention SOCKSv5
+       support.
+
+       * ne_socket.c (sock_init) [NEON_SOCKS]: Call SOCKSinit.
+
+Tue Feb 19 19:21:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (open_connection): Remove notify_status call
+       duplicated with ne_negotiate_ssl.
+
+Tue Feb 19 19:16:44 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (sock_get_version): Removed function.
+
+Tue Feb 19 19:12:52 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_ssl_provide_ccert): Moved outside ifdef
+       NEON_SSL.
+       [!NEON_SSL] (ne_ssl_load_pem, ne_ssl_load_pkcs12,
+       ne_ssl_keypw_prompt): Added stubs.
+
+Sun Feb 17 21:15:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_session_create, ne_session_destroy): Only use
+       the SSL context is SSL is being used for the session.
+
+Sun Feb 17 20:19:05 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Add back client certificate support, much improved.
+
+       * ne_private.h (struct ne_session_s): Add client cert/key fields,
+       provider, privkey password callbacks.
+       
+       * ne_socket.c (sock_init): Call PKCS12_PBE_add.
+       (sock_enable_ssl_os): Add optional 'appdata' argument.
+       (sock_enable_ssl): Adjust accordingly.
+
+       * ne_session.c (provide_client_cert, privkey_prompt,
+       ne_ssl_keypw_prompt, ne_ssl_load_pkcs12, ne_ssl_load_pem,
+       ne_ssl_provide_ccert): New functions.
+       (ne_negotiate_ssl): Pass session as appdata to sock_enable_ssl_os.
+
+Sun Feb 17 12:32:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (make_dname): New function.
+       (check_certificate): Use make_dname.
+
+Sun Feb 17 11:29:10 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (struct get_context): Remove unused 'progress' field,
+       add 'session' field.
+       (get_to_fd, content_range_hdr_handler, clength_hdr_handler): Set
+       session error directly.
+       (clength_hdr_handler): Also fix check for expected range.
+       (everywhere): Initialize session field, don't set session error;
+       use NE_FMT_OFF_T to print off_t's rather than casting to long int.
+
+Sat Feb 16 23:24:10 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.h (NE_XML_STRIPWS): New element flag.
+       
+       * ne_xml.c (start_element): Clear cdata buffer if not in mixed
+       mode.  (char_data): Only strip leading whitespace if
+       NE_XML_STRIPWS is set for the element.
+
+Sat Feb 16 14:52:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (enum state): New state NE_Z_AFTER_DATA.
+       (struct ne_decompress_s): Add fields for storing and parsing
+       stream footer; add checksum field for storing current crc.
+       (process_footer): New function.
+       (do_inflate): Compute checksum.  Switch to AFTER_DATA state and
+       process footer after reading DEFLATE data.
+       (gz_reader): Fail on trailing content.  Handle AFTER_DATA state.
+       (ne_decompress_destroy): Return error if final state was not
+       PASSTHROUGH, or FINISHED.
+       (ne_decompress_reader): Initialize crc.
+
+Sat Feb 16 14:26:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (ne_decompress_destroy): Fix potential segfault
+       with use-after-free.
+
+Thu Feb 14 16:50:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_headers): Ignore header lines
+       without a ':', rather than failing the request.
+
+Tue Feb 12 20:17:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_response_block): Read chunk size as unsigned
+       using strtoul; check that it fits within an unsigned int (and
+       hence, probably a size_t).
+
+Tue Feb 12 20:15:13 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (STRIP_EOL): Removed macro.
+
+Mon Feb 11 22:11:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (match_hostname): Match fully-qualified hostnames
+       against commonName with leading "*." wildcard.
+
+Mon Feb 11 20:47:28 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (match_hostname): New function.
+       (check_certificate): Use it.
+
+Sun Feb 10 00:50:49 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (lookup_host): Set error string on lookup failure.
+
+Sun Feb 10 00:34:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (strip_eol): New function; more efficient
+       STRIP_EOL.
+       (send_request): Use strip_eol.
+       (read_message_header): Use strip_eol, simplify, remove redundant
+       variables.
+
+Sat Feb  9 21:02:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_set_error): Drop STRIP_EOL call.
+
+Sat Feb  9 21:01:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_set_error): Take printf-style format string +
+       varargs list.
+
+Sat Feb  9 16:15:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h (SOCKET_READ_TIMEOUT): Moved to ne_socket.c.
+       
+       * ne_socket.c (struct nsocket_s): Add rdtimeout field.
+       (create_sock): Initialize rdtimeout to SOCKET_READ_TIMEOUT.
+       (sock_read, sock_recv): Use ->rdtimeout field for read timeout.
+       (sock_set_read_timeout): New function.
+
+       * ne_private.h (struct ne_session_s): Add rdtimeout field.
+       
+       * ne_session.c (ne_set_read_timeout): New function.
+
+       * ne_request.c (init_socket): New function.
+       (open_connection): Use init_socket.
+       
+Sat Feb  9 15:11:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_session_destroy): Don't leak the server cert.
+
+Sat Feb  9 09:59:11 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_certificate): Only call verification
+       callback once per certificate; watch for the server cert
+       changing and fail if it does.
+
+Wed Feb  6 20:28:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_certificate): Only call verification
+       callback if failures is non-zero.
+       (ne_ssl_load_ca): Renamed from ne_ssl_add_ca.
+       (ne_ssl_load_default_ca): New function.
+
+Wed Feb  6 20:21:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (sock_init): Cache and return result of
+       initialization.
+
+Wed Feb  6 01:12:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_certificate): Ignore cert validity errors
+       from OpenSSL since these are duplicated.
+
+Wed Feb  6 01:08:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_negotiate_ssl): Fix for invalidating cached
+       SSL_SESSION.
+
+Wed Feb  6 01:03:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c [!NEON_SSL] (STUB): New function.
+       (ne_negotiate_ssl, ne_ssl_add_ca): Implement using STUB.
+
+Tue Feb  5 19:56:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.h (ne_ssl_certificate): New type.
+       
+       * ne_session.c (ne_session_create) [NEON_SSL]: Create the SSL_CTX
+       structure.  
+       (ne_ssl_get_context): Return the SSL_CTX rather than setting it.  
+       (ne_session_destroy): Free the SSL_CTX.
+
+       (asn1time_to_string): Function moved in from sslcerts.c.
+       (check_certificate): Use OpenSSL's internal validity result.
+       Pass back an ne_ssl_certificate to the verification function; 
+       including validity dates.
+       (ne_ssl_add_ca): New function, registers CA certs.
+
+Sat Feb  2 14:05:26 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (sock_enable_ssl_os): Take an optional SSL_SESSION
+       argument.
+
+       * ne_private.h (struct ne_session_s): Add an SSL_SESSION field.
+       
+       * ne_session.c (ne_negotiate_ssl): Pass stored SSL session to
+       sock_enable_ssl_os, cache session after successful negotiation.
+       (ne_session_destroy): Free cached session.
+
+Sat Feb  2 10:45:46 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c, ne_utils.c: Globally replace ENABLE_SSL cpp symbol
+       with NEON_SSL.
+
+Sat Feb  2 09:43:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (check_certificate): Use 1K on-stack buffer.
+
+Sat Feb  2 08:27:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (struct host_info): Add 'resolved' flag.
+       (struct ne_session_s): Add scheme field, rename have_proxy to
+       use_proxy, remove proxy_decider.
+       (struct ne_request_s): Remove use_proxy field.
+
+       * ne_request.c (set_sockerr, ne_set_request_uri, build_request, 
+       open_connection): Use session->use_proxy field to determine
+       whether proxy is used.
+       (ne_request_create): Drop use of proxy_decider callback.
+       (lookup_host): Moved here from ne_session.c.
+       (ne_begin_request): Lookup server/proxy hostname if not already
+       resolved.
+
+       * ne_session.c (ne_session_create): Moved within file; takes
+       scheme, and server hostname, port as arguments.
+       (ne_ssl_enable, ne_session_decide_proxy, ne_session_server):
+       Removed functions.
+       (ne_get_scheme): Simply return scheme field.
+
+Fri Feb  1 23:12:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (add_fixed_headers): Remove last traces of TLS
+       upgrade support.
+
+Thu Jan 31 20:50:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (struct ne_session_s): Rename use_secure to
+       use_ssl; removed nssl_context, added SSL_CTX, server cert, verify
+       callback pointers.
+
+       * ne_request.c (send_request): Remove support for TLS upgrade.
+       (open_connection): Use ne_negotiate_ssl; close socket properly if
+       negotiation fails.
+
+       * ne_session.c (ne_session_destroy): Free SSL_CTX stored in
+       session.
+       (ne_ssl_set_context, ne_ssl_set_verify, verify_err, getx509field,
+       check_context, ne_negotiate_ssl, ne_ssl_server_cert): New
+       functions.
+       (ne_set_secure_context, ne_set_request_secure_upgrade,
+       ne_set_accept_secure_upgrade): Removed functions.
+       (ne_ssl_enable): Renamed from ne_set_secure.
+
+       * ne_socket.c (struct nssl_context_s): Removed type.
+       (sock_create_ssl_context, sock_destroy_ssl_context,
+       sock_disable_*, key_prompt_cb, sock_set_key_prompt,
+       sock_set_client_cert): Removed functions.
+       (sock_enable_ssl_os): Renamed from sock_make_secure; take an
+       SSL_CTX pointer, and optionally pass out the SSL structure.
+       (sock_enable_ssl): New function.
+       
+Wed Jan 30 19:47:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_buffer_concat, ne_buffer_zappend,
+       ne_buffer_append, ne_buffer_grow): Don't return success value,
+       presume universe ends at OOM.
+
+Sat Jan 26 10:57:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c: Renamed enum state constants to have prefix
+       NE_Z_, to avoid conflict with Windows headers (Branko Ãˆibej).
+
+Mon Jan 14 20:26:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_concat): Removed function - it didn't work, and
+       it wasn't used.
+
+Mon Jan 14 02:09:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_content_type_handler): Parse charset parameter.
+
+Sun Jan 13 14:29:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_content_type_handler): Remove trailing '/' from
+       parsed type, fix search for parms separator (Greg Stein).
+
+Sun Jan 13 12:07:51 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.c (ne_simple_request): Drop unused Content-Type handling.
+
+Thu Jan 10 00:39:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (hdr_hash): Mark as inline.
+
+Tue Jan  8 22:03:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (add_timeout_header): New function.  (ne_lock,
+       ne_lock_refresh): Send a Timeout header if lock->timeout is set.
+
+Mon Jan  7 21:48:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (parse_timeout): Fix parsing lock timeout (Arun
+       Garg).
+
+Mon Dec 17 22:46:36 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (struct ne_session_s): Make expect100_works a plain
+       integer (rather than a bitfield).
+
+Sun Dec  9 14:04:27 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_buffer_grow, ne_buffer_create_sized): Don't
+       zero-fill new memory.  (ne_buffer_concat): Zero terminate the
+       string as _grow doesn't do it.
+
+Sun Dec  9 13:31:55 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_buffer_zappend): Minor optimisation; implement
+       using ne_buffer_append.
+
+Sun Dec  9 13:18:35 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_buffer_concat): Optimise to use time O(n) [n ==
+       total string length).
+
+Sun Dec  9 11:57:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (NEON_DAVOBJS): Remove ne_acl.o.
+
+Sat Dec  8 01:11:30 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_pull_request_body): Use NE_FMT_SIZE_T in
+       debugging message; cast size_t to int to avoid GCC warning for
+       field size parameter.  (set_body_size): Use NE_FMT_SIZE_T.
+
+       * ne_xml.c (ne_xml_parse): Similarly.
+
+Mon Dec  3 19:56:07 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c (ne_session_destroy): Return void.
+
+Sat Dec  1 18:37:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (ah_create): Reset attempt counter
+       here... (ah_post_send): ...rather than here.
+
+Tue Nov 27 21:26:01 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (send_with_progress): Actually call the callback;
+       fix for correct sock_fullwrite return codes.
+
+Tue Nov 27 20:20:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h (VERSION_PRE11): Define macro; as
+       ne_version_pre_http11.
+
+       * ne_session.c (ne_version_pre_http11): Use VERSION_PRE11.
+
+       * ne_request.c (add_fixed_headers, build_request, ne_end_request):
+       Use VERSION_PRE11.
+
+Sun Nov 18 19:32:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (discover_results): Check status is 2xx before
+       invoking callback; pass NULL lock and non-NULL status pointer in
+       failure cases.  (create_private): Initialize lock to some "value
+       unspecified" defaults.
+
+Sun Nov 18 19:25:10 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (auth_session): Rename 'tries' field to 'attempt'.
+       (get_credentials, ah_pre_send, ah_post_send): Increment attempt
+       counter only when requesting credentials; reset it to zero when no
+       auth failure is signaled.
+
+Sun Nov 18 15:49:00 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.h (ne_request_auth): Pass username and password as
+       buffers of size NE_ABUFSIZ to callback.  Add 'attempt' argument.
+
+       * ne_auth.c (auth_session): Store username in buffer.
+       (get_credentials, basic_challenge, digest_challenge): Updated for
+       callback prototype changes.  (ah_post_send): Request credentials,
+       and retry authentication until callback returns non-zero.
+
+Mon Nov 12 20:57:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (get_to_fd): Really cope with short writes (thanks to
+       rado <dzusto@yahoo.com>).
+
+Sun Nov  4 15:09:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.h: Define NE_ELM_PROPS_UNUSED for picking element ids
+       for use with the propfind XML parser.
+
+Sat Nov  3 19:06:04 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c (NSPACE): New macro.  (set_body, pnamecmp, startelm,
+       free_propset): Handle property having NULL nspace element in
+       propfind code.
+
+Sun Oct 28 22:04:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c (parse_element): Prevent false matches of found prefix
+       "abcde" against stored prefix "abcdeFGH".  Compare
+       case-sensitively.
+
+Fri Oct 26 20:28:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (send_request): Fix case where persistent
+       connection times out, and improve error handling.
+
+Thu Oct 25 20:42:24 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c (ne_proppatch): Really handle properties with a NULL
+       namespace correctly; use the "D:" prefix for elements in the
+       "DAV:" namespace; allow properties to have no namespace.
+
+Tue Oct 16 08:54:46 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c (parse_element): Fail the parse if a namespace prefix
+       definition is given with an empty value.
+
+Tue Oct 16 08:52:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.h, ne_207.h: Move ne_propname definition into
+       ne_props.h.
+
+Tue Oct 16 08:49:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c (ne_proppatch): Handle properties with a NULL nspace
+       field correctly.
+
+Sun Oct  7 19:31:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_acl.c (ne_acl_set) [USE_DAV_LOCKS]: Notify use of resource to
+       locking code.
+
+Sun Oct  7 17:45:01 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_acl.c, ne_acl.h: New files, contributed by Arun Garg
+       <arung@pspl.co.in>.
+
+       * Makefile.in: Add ne_acl.* to build.
+
+Sun Oct  7 16:10:05 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private (struct ne_session_s): Add 'reqcount' field.
+
+       * ne_request.c (send_request): Refactor slightly; don't loop, but
+       return NE_RETRY when appropriate.  Increment reqcount.
+       (ne_begin_request): Loop if send_request returns NE_RETRY.
+       (open_connection): Reset reqcount field.
+
+Tue Oct  2 21:11:39 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_dates.c (GMTOFF): New macro.  (ne_iso8601_parse,
+       ne_rfc1123_parse, ne_rfc1036_parse, ne_asctime_parse): Use new
+       macro, fix up date handling on some platforms.
+
+Sat Sep 29 14:20:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (gz_reader): Fix tests 4 and 7: don't try to
+       inflate after reading header if no bytes are left in the buffer.
+
+Sat Sep 29 14:04:11 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c: Fix API; return an opaque object which must
+       be destroyed later.
+       
+       (ne_decompress_reader): Renamed from ne_gzip_response_body_reader.
+       Doesn't need the session object passed in any more.
+       (ne_decompress_destroy): Merge of co_destroy, co_post_end.
+
+Sat Sep 29 13:50:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_get_session): New function.
+
+Sat Sep 29 12:52:31 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c (parse_header): Bail if flags are set to something
+       unexpected.
+
+Sat Sep 29 11:15:30 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_compress.c, ne_compress.h: New files.
+
+       * Makefile.in: Add deps for ne_compress.
+
+Thu Sep 27 09:05:24 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c: Adapted for new hooks interface.
+
+       * ne_cookies.c: Adapted for new hooks interface.
+       (ne_cookie_register): New function.
+
+Thu Sep 27 09:01:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c, ne_locks.c: Adapted for new hooks interface.  Store
+       pointer to per-request object in the per-session object.
+
+Thu Sep 27 08:48:16 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       Re-write hooks interface to register callbacks individually rather
+       than as a block.  Inspired by the Apache 2.0/APR hooks interface.
+
+       * ne_private.h (struct hook): Store a callback, userdata, id.
+       (struct hook_request): Removed.  (struct ne_session_s): Store
+       hooks lists for create_req, pre_send, post_send, destroy_req,
+       destroy_sess, accessor.  (struct ne_request_s): Store accessor
+       hooks list.
+       
+       * ne_request.c (ne_add_hooks): Removed.
+       (ne_hook_create_request, ne_hook_pre_send, ne_hook_post_send,
+       ne_hook_destroy_request, ne_hook_destroy_session,
+       ne_hook_session_accessor, ne_hook_request_accessor,
+       ne_null_accessor, call_access, add_hook): New functions.
+       (ne_request_create, ne_request_destroy, build_request,
+       ne_end_request): Adapt for new interface.
+
+       * ne_session.c (destroy_hooks): New function.
+       (ne_session_destroy): Use it to destroy hooks lists appropriately.
+
+Tue Sep 25 07:46:32 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.c: Only decode UTF-8 for parsers other than libxml 1.x.
+
+Tue Sep 25 07:33:09 2001  Mo DeJong  <supermo@bayarea.net>
+
+       * src/ne_socket.c: Include <signal.h> instead of <sys/signal.h>.
+       (sock_init): Only use signal() to ignore SIGPIPE if both
+       HAVE_SIGNAL and HAVE_SIGPIPE are defined.
+
+Tue Sep 25 07:09:53 2001  Mo DeJong  <supermo@bayarea.net>
+
+       * ne_socket.c (sock_init): Declare local variables before invoking
+       any instructions since that is not valid C code.
+
+Sun Sep 23 10:30:54 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c (struct auth_challenge): Make members const.
+       (clean_session): Free the realm string.  (basic_challenge,
+       digest_challenge): strdup the realm string.  (request_digest):
+       opaque is no longer stored quoted.  (tokenize): New function.
+       (verify_response, auth_challenge): Rejig to use tokenize().
+
+Sat Sep 22 20:17:00 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_shave): Fix possible memory corruption when
+       result should be the empty string.
+
+Thu Sep 20 21:27:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_pull_request_body): Add debugging dump of body
+       blocks.
+
+Thu Sep 20 21:23:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h: Remove obsolete 'if_locks' member from struct
+       ne_request_s.
+
+Tue Sep 18 23:35:30 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_get_range): Handle write errors too.
+
+Tue Sep 18 22:14:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.h (ne_xml_validate_cb): Take userdata parameter.
+
+       * ne_xml.c (find_handler): Pass validate callback the handler's
+       userdata.
+
+       * ne_207.c, ne_props.c, ne_locks.c: All users changed.
+
+Tue Sep 18 21:49:14 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c (ne_lock_refresh): New function.
+
+Tue Sep 18 21:17:29 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (copy_or_move): Take a depth parameter, add depth
+       header, for COPY requests.  (ne_copy): Take depth parameter, pass
+       through.  (ne_move): Adjusted accordingly.
+
+Mon Sep 17 23:29:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (ne_debug_init): Set debug stream to be unbuffered if
+       setvbuf() is available.
+
+Mon Aug 27 00:36:37 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.c (start_element, end_element): Remember when context is
+       valid for a <propstat>, and only invoke callback then.
+
+Sun Aug 26 22:30:39 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_get_range): Better error handling.  Cope with
+       Apache's 416 problem.
+
+Sun Aug 26 18:58:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c: Store unquoted challenge parameters in session
+       object, prevent having to unquote/free them >1 times.
+
+Sun Aug 26 18:57:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (sock_init): Do nothing on any calls after first.
+
+Sun Aug 26 12:45:04 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (server_hdr_handler): Remove function. (ne_options):
+       Don't add server_hdr_handler.
+
+Tue Jul 17 11:25:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (sock_init): Set signal dispostion for SIGPIPE to
+       ignore.
+
+Sat Jun 30 12:11:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c (ne_supports_ssl): New function.
+
+Tue Jun 19 21:57:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_dates.c (ne_iso8601_parse): Fix month off-by-one bug, use
+       separate vars for offsets.  (ne_rfc1036_parse): Fix Y2K bug,
+       parsing problem.
+
+Tue Jun 19 21:57:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_dates.c (ne_iso8601_parse): New function.
+
+Sun Jun 10 15:39:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (send_with_progress): New function.
+       (send_request_body): Use send_with_progress to trigger progress
+       callbacks if necessary.
+
+Sat Jun  9 15:42:33 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h: Bring back NE_ASC2HEX/HEX2ASC.
+
+       * ne_md5.c: Use them.
+
+Sat Jun  9 15:42:08 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_xml.h: Include ne_defs.h.
+
+Fri Jun  8 23:02:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.h, ne_socket.c: Update for includes (Mo DeJong).
+
+Fri Jun  8 21:34:00 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (dav_hdr_handler): Use ne_token.
+
+Sat Jun  2 14:37:07 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_private.h: Renamed from http_private.h.
+
+Sat Jun  2 14:35:23 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_auth.c, ne_auth.h: Renamed from http_auth.c, ne_auth.h.
+
+Sat Jun  2 14:35:02 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_cookies.c, ne_cookies.h: Renamed from http_cookies.c,
+       http_cookies.h.
+
+Sat Jun  2 14:34:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_dates.c, ne_dates.h: Renamed from dates.c, dates.h
+
+Sat Jun  2 14:22:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_redirect.c, ne_redirect.h: Renamed from http_redirect.c,
+       http_redirec.h. Big rename... s/http_/ne_g/
+
+Sat Jun  2 12:54:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_md5.c (md5_process_block): Fix for word alignment issue on
+       Sparc from Kai Sommerfeld.
+
+Wed May 30 23:15:31 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_put, ne_get, ne_put_if_unmodified, ne_get_range,
+       ne_post): Take an integer fd rather than FILE * stream.
+       (get_to_fd): Write to fd rather than stream.
+
+Wed May 30 23:08:55 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_i18n.h, ne_i18n.c: Renamed from neon_i18n.h, neon_i18n.c.
+
+       * *.c: All changed accordingly.
+       
+Wed May 30 23:02:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_defs.h: Renamed from neon_defs.h.
+
+       * *.h: All changed accordingly.
+
+Wed May 30 22:58:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_md5.c, ne_md5.h: Renamed from md5.c, neon_md5.h
+
+Wed May 30 22:55:19 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.h: In-line ne_debug for GCC which can cope with varargs
+       preprocessor macros.  
+
+Wed May 30 00:43:05 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (ah_use_body): Removed function.  (digest_body): New
+       function.  (request_digest): Use ne_pull_request_body to find the
+       MD5 digest of the request body, when necessary.
+
+Wed May 30 00:30:52 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_redirect.c: Store Request-URI, session pointer, and method
+       string in redirect object.  Avoid looking inside
+       ne_request/ne_session internals.
+
+Wed May 30 00:04:30 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c: Re-implement request body handling in terms of a
+       callback which provides the request body blocks on demand.  Remove
+       'use_body' hook, in favour of the hooks calling
+       ne_pull_request_body when necessary.  (ne_pull_request_body,
+       body_fd_send, body_string_send): New functions.
+       (send_request_body): Re-implemented using ne_pull_request_body.
+       (run_set_body_hooks): Removed function.  (ne_set_request_body_fd):
+       Replacement for ne_set_request_body_stream, using a raw fd rather
+       than a FILE *.
+
+Tue May 29 22:39:39 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_basic.h, dav_basic.h: Removed.
+
+Tue May 29 22:38:54 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.c (ne_simple_request, etc): Copied in from dav_basic.c.
+
+Tue May 29 22:12:23 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c, ne_locks.h, ne_props.c, ne_props.h, ne_207.c,
+       ne_207.h: Big rename. dav_* -> ne_*, and so on.
+
+Tue May 29 22:06:24 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_add_depth_header): Moved from dav_basic.c.
+
+Tue May 29 21:55:30 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_props.c, ne_props.h: Renamed from dav_props.c, dav_props.h.
+
+Tue May 29 21:43:15 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_207.c, ne_207.h: Renamed from dav_207.c, dav_207.h.
+
+Tue May 29 21:22:25 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_locks.c, ne_locks.h: Renamed from dav_locks.c, dav_locks.h.
+
+Tue May 29 21:21:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c (sock_fullwrite): Cast return value of SSL_write to
+       size_t to prevent comparison of signed with unsigned.
+
+Tue May 29 21:05:27 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c [!NEON_NODAV]: Move ne_copy, ne_mkcol, ne_move,
+       ne_delete in here.
+
+Tue May 29 20:12:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_uri.c, ne_uri.h: Renamed from uri.c, uri.h.
+
+Tue May 29 19:17:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_socket.c, ne_socket.h: Renamed from socket.c, nsocket.h.
+
+Tue May 29 18:58:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (ne_mkcol, ne_copy, ne_move, ne_delete): Renamed from
+       dav_*.
+
+Tue May 29 17:58:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c (copy_or_move, dav_copy, dav_move, dav_delete,
+       dav_mkcol): Copied in from dav_basic.c.
+
+Tue May 29 17:55:33 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_basic.c, ne_basic.h: Renamed from http_basic.c, http_basic.h.
+
+Tue May 29 17:47:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (ah_create, ah_pre_send): Add the response body
+       handler in pre_send, and only if qop=auth-int.
+
+Wed May 16 20:54:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (ne_get_request_headers): Removed function.
+
+Sat May 12 18:48:46 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c (read_message_header, read_response_headers): Use a
+       fixed-size char * buffer argument rather than an ne_buffer.
+       Append directly to it when header-folding.
+
+Mon May  7 10:42:38 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c (ne_token): Use an optimized search (strchr) if
+       quotes is NULL.
+
+Mon May  7 01:33:48 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (basic_challenge, get_cnonce): Updated for ne_base64
+       change.
+
+Mon May  7 01:32:22 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * base64.c (ne_base64): Take length parameter.  [BASE64_TEST]
+       (main): Remove function, obsoleted by test code.
+
+Wed May  2 12:06:59 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c, ne_string.h (ne_token, ne_shave): New functions,
+       destined to replace split_string, shave_string, etc.
+
+       * ne_string.c [SPLIT_STRING_TEST, PAIR_STRING_TEST] (main): Remove
+       tests, functions are obsolete.
+
+Tue May  1 22:14:14 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dates.c (ne_httpdate_parse): Moved from ne_utils.c.
+
+Tue May  1 21:55:45 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_utils.c, ne_utils.h: Renamed from http_utils.c, http_utils.h.
+       Big rename.  http_* -> ne_*. neon_* -> ne_*. DEBUG() ->
+       NE_DEBUG().  DEBUG_* -> NE_DBG_*.
+
+Tue May  1 21:35:10 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_request.c: Updated for ne_buffer changes.
+
+Tue May  1 21:28:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.h (ne_buffer_size): Implement as macro.
+
+       * ne_string.c (ne_buffer_size): Remove function.
+
+Tue May  1 21:23:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c, ne_string.h: Make ne_buffer a transparent type, and
+       no longer be an implicit pointer type.  (ne_buffer_*): All
+       changed.  (ne_buffer_data, NE_BUFFER_CAST): Removed.
+
+Tue May  1 21:17:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c, ne_string.h: Renamed sbuffer -> ne_buffer.
+       Implicit pointer removed ne_buffer type.
+
+Tue May  1 21:12:15 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_string.c, ne_string.h: Renamed from string_utils.c,
+       string_utils.h (CVS copy'n'delete).
+
+Tue May  1 20:49:46 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * md5.c (ASC2HEX, HEX2ASC): Moved here from string_utils.h.
+
+       * string_utils.h: As above.
+
+Tue May  1 20:47:20 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c, http_request.h: Removed files.
+
+       * ne_request.c, ne_request.h: Copied from old http_request.[ch].
+       Renamed http_* -> ne_*.
+
+Tue May  1 20:43:11 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * hip_xml.c renamed to ne_xml.c, hip_xml.h renamed to ne_xml.h:
+       CVS repository copy'n'delete.
+
+Tue May  1 20:41:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * hip_xml.c, hip_xml.h: Big rename. hip_xml_* -> ne_xml_*.
+
+Tue May  1 20:37:13 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_basic.c, http_basic.h: Big rename. http_* -> ne_*.
+
+Tue May  1 19:59:01 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.c: Renamed http_* to ne_*.
+
+Tue May  1 19:55:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_alloc.h (NE_FREE): Renamed from HTTP_FREE() in http_utils.h
+
+Tue May  1 19:54:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (sock_make_secure): Set SSL_MODE_AUTO_RETRY when
+       available.
+
+Mon Apr 30 00:36:34 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_session.[ch]: New files, split down from http_request.[ch].
+
+Sun Apr 29 15:02:23 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri.c [URITEST] (main): Remove, obsoleted by new test suite.
+
+Sun Apr 29 15:01:30 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri.c (uri_has_trailing_slash): Return false if uri is "".
+
+Sun Apr 29 13:53:41 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dates.c (ne_asctime_parse, ne_rfc1123_date, ne_rfc1036_parse):
+       Set tm_isdst to -1 in struct tm.
+
+Sun Apr 29 13:28:26 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_utils.c (http_parse_statusline): Skip leading whitespace.
+       (Johan Lindh).  (http_parse_statusline): Ensure status-code is not
+       more than three digits.
+
+Sun Apr 29 13:26:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (build_request): Don't add "Content-Length: 0"
+       header if no body: Squid 2.3-STABLE1 doesn't like this.
+
+Sun Apr 29 13:25:16 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (everywhere): Renamed md5_* -> ne_md5_*.
+
+Sun Apr 29 13:24:12 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * md5.c: Renamed md5_* -> ne_md5_*.  (ne_ascii_to_md5,
+       ne_md5_to_ascii): Moved from string_utils.c.
+
+Thu Apr 26 22:39:05 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri.c (uri_parse): A zero-length URI is invalid.
+
+Wed Apr 25 23:11:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (startelm): Check for xml:lang attribute and store
+       as prop->lang.  (dav_propset_lang): New function.  (free_propset):
+       Free lang.
+
+Wed Apr 25 23:08:52 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * hip_xml.c (hip_xml_get_attr): New function.
+
+Sun Apr 22 21:48:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri.c (uri_parse): Skip a userinfo@ segment if present (Johan
+       Lindh <johan@link-Data.com>).
+
+Wed Apr 18 13:29:46 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_locks.c (dav_lock_copy): Allow owner to be NULL.
+
+Tue Apr 17 22:57:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_alloc.h, dav_locks.h: Add C++ inclusion safety.
+
+Tue Apr 17 22:56:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri.c (uri_parse): Correctly handle URIs with no scheme or
+       hostport segments (i.e. just a path).
+
+Tue Apr 10 00:29:25 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c [HAVE_SOCKS_H]: Include socks.h for SOCKSv5 support.
+
+Wed Apr  4 21:41:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_utils.h [WIN32]: Define ssize_t (Kai).
+
+Tue Apr  3 21:03:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_locks.c (dav_lock_discover): Cope with below API change.
+
+Tue Apr  3 20:43:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (propfind): Register the flat element handler here,
+       to pick up *any* properties and store them as 'flat' if they are
+       not handled by a handler further down the stack.  (make_elms,
+       free_elms, dav_propfind_set_flat, dav_propfind_set_complex):
+       Removed functions.  (dav_propfind_named): Take the list of
+       property names, and call set_body here.
+       (dav_propfind_set_private): New function.
+
+Tue Apr  3 09:33:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.h: Added C++ inclusion safety. (Kai Sommerfeld)
+
+Mon Apr  2 02:39:18 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * hip_xml.c (sax_error): Add parse error callback for libxml.
+
+Mon Apr  2 02:23:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_redirect.c (post_send): Clean up and fix logic.  Only check
+       for confirmation for same-server redirects.
+
+Mon Apr  2 02:13:48 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_redirect.c (http_redirect_location): New function.
+       (destroy): Removed function.  (create): Free location.
+       (post_send): Only call notify callback for followed redirects.
+
+Mon Apr  2 01:55:27 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_set_request_uri): Allow using the '*' URI
+       even when using a proxy server.
+
+Mon Apr  2 01:32:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_redirect.c (post_send): Give up on trying to follow a
+       redirect to another server.  Return HTTP_REDIRECT on such a
+       redirect.
+
+       * http_redirect.c (post_send): Fix leaks of URI object (Kai
+       Sommerfeld).
+
+Mon Apr  2 01:08:33 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (read_response_headers): Don't read more than 100
+       response headers.
+
+Mon Apr  2 00:54:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_request_dispatch): Remove mapping of auth
+       status codes to return values, the hook does it now.
+
+Mon Apr  2 00:53:20 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_set_request_uri): New function, split out
+       from http_request_create. (http_request_create): Use it.
+
+Mon Apr  2 00:51:23 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (http_set_proxy_auth, http_set_server_auth): Add
+       failure codes.  (ah_post_send): Return failure code if
+       authentication fails.
+
+Mon Apr  2 00:19:17 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_utils.c: Pick up xmlversion.h from libxml2.
+       (neon_version_string): Include libxml2 version string if defined.
+
+Sun Apr  1 21:40:00 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_basic.c (http_get_range): Fix total length
+       calculation. (Johan Lindh <johan@linkdata.se>).
+       (clength_hdr_handler): Use range->total.
+
+Sun Apr  1 21:26:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * hip_xml.c: Add expat2 support (Sam TH <sam@uchicago.edu>).
+
+Sun Apr  1 21:07:19 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * string_utils.h (CONCAT2, CONCAT3, CONCAT4): Use ne_malloc.
+
+Sun Apr  1 20:59:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (propfind, dav_proppatch): As below.
+
+Sun Apr  1 20:32:29 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_basic.c (http_post): Changed as per
+       http_set_request_body_buffer change.
+
+       * dav_locks.c (dav_lock): Likewise.
+
+Sun Apr  1 20:31:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_set_request_body_buffer): Pass in size
+       parameter too.  (send_request_body): Use sized rather than
+       NUL-terminated buffer.
+
+Sun Apr  1 20:12:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_private.h: Added 'body_callback' request body type, and
+       body_cb, body_ud fields to http_req.
+
+       * http_request.c (http_set_request_body_provider): New function.
+       (set_body_size): New function, factored out from old
+       http_set_request_body_*.  (http_set_request_body_stream,
+       http_set_request_body_buffer): Use it.
+
+Sun Apr  1 19:56:17 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c: Replaced 'uri' and 'depth' fields in propfind
+       handler object with an 'http_req' pointer.  (dav_propfind_create):
+       Create the request object here, and set the depth header.
+       (propfind): Changed accordingly.  (dav_propfind_destroy): Destroy
+       request object too.
+
+       * dav_props.c (dav_propfind_get_request): New function.
+       
+Fri Mar 30 16:50:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * hip_xml.c (start_element): Quote attributes in collect (Kai
+       Sommerfeld).
+
+Fri Mar 30 16:36:08 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_basic.c (http_put_if_unmodified): Changed as below.
+
+Thu Mar 22 14:05:52 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_utils.c (http_dateparse): Changed as below.
+
+       * http_auth.c (get_conce, basic_challenge): Likewise.
+
+Thu Mar 22 14:04:54 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dates.c (ne_rfc1123_date, ne_rfc1123_date, ne_asctime_parse,
+       ne_rfc1036_parse): Added ne_ prefix.
+
+Thu Mar 22 14:03:12 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * base64.c (ne_base64): Renamed from base64.
+
+Tue Mar 20 20:34:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (make_elms): Don't request UTF-8 decoding of
+       property values.
+
+Tue Mar 20 20:33:39 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * string_utils.c (ne_utf8_decode): New function.
+
+Mon Mar 19 22:08:45 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_basic.c (get_callback): Removed function.  (http_get,
+       http_read_file, http_post): Use callbacks directly rather than
+       indirectly through get_callback.
+
+Mon Mar 19 21:55:19 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (notify_status, http_set_status,
+       http_set_progress): New functions: request status and progress
+       callbacks.  (open_connection, lookup_host): Use notify_status to
+       trigger status callbacks, and register socket progress callbacks.
+
+Mon Mar 19 21:53:07 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (sock_register_notify): Removed function.
+       (sock_connect_u): Renamed to sock_connect.
+       (sock_register_progress): Per-socket progress callbacks rather
+       than global ones.  (sock_call_progress): Take socket argument.
+       (all callers changed).
+
+Mon Mar 19 21:52:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (sock_get_version): New function.
+
+Mon Mar 19 13:59:21 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (propfind): Destroy the handler.
+
+Mon Mar 19 13:36:55 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (dav_propnames): New function.
+
+Wed Mar 14 22:42:12 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.h (http_begin_request, http_end_request,
+       http_read_response_block): New functions.
+       (http_request_dispatch): Reimplemented using new caller-pulls
+       interface.
+
+Wed Mar 14 22:20:38 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (ah_post_send): If authentication fails (i.e. bad
+       credentials), clean the session details.
+
+Wed Mar 14 20:46:55 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c: Retry the request simply if it has not been tried
+       before with authentication details, otherwise, fail on 40[17].
+
+Wed Mar 14 20:12:52 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (open_connection): Make sure to close the
+       connection, and return HTTP_CONNECT if the SSL negotiation fails.
+
+Tue Mar  6 18:37:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_alloc.c (ne_strndup): Allocate n+1 bytes not 'n' (Kai
+       Sommerfeld).
+
+Mon Mar  5 01:05:31 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c: Moved local sbuffer variables of
+       http_request_dispatch into http_req members 'reqbuf' and
+       'respbuf'.  (build_request): Return a const char * of the request.
+       (send_request): Call build_request directly, use req->respbuf.
+       (http_request_dispatch): Don't call build_request.  Removed 'goto'
+       exception handling (hoorah).  (http_request_create,
+       http_request_destroy): Create and destroy reqbuf and respbuf here.
+
+Mon Mar  5 00:43:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_set_request_body_stream): Set
+       req->body_size and Content-Length header here: return success
+       value.  (http_set_request_body_buffer): Likewise (but no return
+       value).  (get_request_bodysize): Removed function.
+       (build_request): Add Content-Length: 0 header if no request body.
+
+Mon Mar  5 00:27:24 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (http_forget_auth): New function.
+
+Mon Mar  5 00:25:15 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_request_hook_private): Renamed from
+       http_get_hook_private.  (http_session_hook_private): New function.
+
+       * dav_locks.c (dav_lock_using_resource, dav_lock_using_parent):
+       Renamed simiarly.
+
+Sun Mar  4 23:12:12 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c: Moved per-request state into struct auth_request.
+       (request_digest): Take struct auth_request argument.  (free_auth):
+       New function.  (http_add_hooks): Pass free_auth as cleanup
+       function for auth session.
+
+Sun Mar  4 23:08:46 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_redirect.c (auto_redirect): Auto-redirect OPTIONS requests.
+       (free_redirect): New function.  (http_redirect_register): Pass
+       cleanup function.
+
+Sun Mar  4 23:07:01 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_locks.c (dav_lock_unregister): Removed function.
+       (free_locks): New function.  (dav_lock_register): Pass free_locks
+       as cleanup function for hooks.
+
+Sun Mar  4 22:54:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.h (http_add_hooks): Added fourth argument to
+       register a cleanup function for the cookie.
+
+Sun Mar  4 19:53:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (request_digest): Use an sbuffer to create the
+       header value.
+
+Sun Mar  4 19:44:18 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c: Remove namespace protection for private
+       functions. s/http_auth/auth/g.
+
+Sun Mar  4 19:39:13 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (ah_pre_send): Increase attempt counter here, ...
+       (ah_post_send): instead of here.
+
+Sun Mar  4 18:40:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_request_dispatch): Simplify post_send hook
+       invocation: run them until one doesn't return HTTP_OK. Don't run
+       pre_send hooks here.  Loop while a hook returns HTTP_RETRY.
+       (build_request): Run pre_send hooks here.
+
+       * http_request.c (read_response_body): Call
+       normalize_response_length here.
+
+Sun Mar  4 18:12:26 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       Re-implemented HTTP authentication using generic hooks interface.
+
+       * http_auth.c: Added http_auth.h.  (http_auth_init,
+       http_auth_set_creds_cb, http_auth_new_request,
+       http_auth_request_header): Removed functions, merged into new
+       hooks code.  (ah_create, ah_post_send, ah_pre_send, ah_use_body,
+       ah_destroy): New functions.  (auth_body_reader,
+       http_set_server_auth, http_set_proxy_auth): Moved over from
+       http_request.c and redone for hooks interface.
+
+       * http_request.c (http_set_server_auth, http_set_proxy_auth,
+       give_creds, auth_body_reader): Moved to http_auth.c.
+       (http_accept_always): Renamed from always_accept_response and made
+       public.  (http_request_create, build_request,
+       http_request_dispatch): Removed authentication code.
+
+Tue Feb 27 19:49:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (set_body): Remove UTF-8 encoding again.
+
+Mon Feb 26 22:38:41 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       Patch from Kai Sommerfeld to remove URI escaping from inside neon.
+       
+       * dav_207.c (end_element): Don't unescape href elements.
+
+       * http_request.c (http_request_create): Don't escape Request-URI.
+
+       * dav_basic.c (copy_or_move): Don't escape destination URI.
+
+Mon Feb 26 21:44:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (set_body): UTF-8 encode the property name and
+       value.  (make_elms): Request UTF-8 decoding of property values.
+
+Mon Feb 26 21:40:14 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * hip_xml.c: Do perform UTF-8 decoding when using libxml.
+       (char_data): Fix UTF-8 decoding bug where the byte after a
+       multi-byte encoded sequence would be skipped.
+
+Sun Feb 25 20:04:05 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * string_utils.c (ne_utf8_encode): New function.
+
+Sun Feb 25 19:52:01 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * ne_alloc.c (ne_realloc): New function.
+
+Sun Feb 25 17:00:32 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (all): Rename NEON_IS_BUNDLED to NEON_BUILD_BUNDLED.
+
+Sun Feb 25 16:52:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (all): Build according to NEON_IS_BUNDLED.
+
+Fri Feb 23 23:38:10 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Fix deps: neon_config.h has gone.
+
+Fri Feb 23 22:57:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * dav_props.c (dav_simple_propfind): Support a NULL 'props'
+       argument to do an allprop request, as per the advertising
+       literature.  Register a catch-all handler in this case.
+
+Fri Feb 23 22:16:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (http_session_destroy): Free up hooks list.
+
+Thu Feb 22 21:54:36 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.h (http_add_hooks): Make request_hooks 'const'.
+
+Thu Feb 15 08:36:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (read_response_body): Accept SOCK_CLOSED as
+       end-of-response if we don't have a Content-Length (and not
+       chunked). (Kai Sommerfeld).
+
+Thu Feb 15 08:36:23 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (add_fixed_headers): Don't add the Host header
+       here.  (http_request_dispatch): Add it here instead.
+
+       * http_request.c (set_hostinfo): Dup the hostname.
+       (http_session_destroy): Free the hostname.  (Kai Sommerfeld).
+
+Thu Feb 15 08:35:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_private.h: Make hostname in struct host_info char *. (Kai
+       Sommerfeld).
+
+Thu Feb 15 08:08:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_redirect.h: Add C++ header-inclusion safety macros (Kai
+       Sommerfeld <kai.sommerfeld@germany.sun.com>).
+
+Wed Feb 14 23:37:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (read_response_body): Use a size_t for readlen,
+       as read_response_block requires.
+
+Wed Feb 14 23:25:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_auth.c (request_digest): Fix incorrect signed-ness of
+       buffer.
+
+Wed Feb 14 23:22:13 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri.h, string_utils.h: Comment-out tokens after #endif.
+
+Sun Feb  4 14:36:11 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_redirect.c (post_send): Prototype change.
+
+Sun Feb  4 14:31:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c: Added key_prompt, key_userdata, key_file to
+       nssl_context.  (sock_set_key_prompt, key_prompt_cb): New
+       functions.  (sock_make_secure): Set ctx->key_file to private key
+       filename.
+
+Sun Feb  4 13:31:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.h: Make http_status argument of post_send a const
+       pointer.
+
+Sun Feb  4 10:38:12 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_request.c (set_sockerr): Only use the socket error if it is
+       NULL, else print generic "something went wrong"-type error
+       message.
+
+Sun Feb  4 10:29:37 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (sock_set_client_cert): Call SSL_CTX_check_private_key
+       to ensure that the cert and private key match.
+
+Sun Feb  4 10:28:02 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (sock_make_secure): In error cases after SSL_connect
+       succeeds, call SSL_shutdown and assign sock->ssl = NULL before
+       returning.
+
+Sat Feb  3 18:33:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (sock_close): Call SSL_shutdown before close()'ing the
+       fd.
+
+Sat Feb  3 18:30:48 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c: Store an default SSL_CTX in nsocket, to be used when
+       no nssl_context is supplied.  (create_socket): Create default
+       SSL_CTX (sock_close): Free it here.  (sock_make_secure): Use it
+       here.
+
+Sat Feb  3 15:52:15 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (sock_set_client_cert): New function.
+
+Sat Feb  3 15:48:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c: Rejig of nssl_context handling.  An nssl_context is
+       now really a wrapper for an SSL_CTX.  (sock_create_ssl_context):
+       Create the SSL_CTX here.  (sock_disable_tlsv1, sock_disable_sslv2,
+       sock_disable_sslv3): Set the SSL_CTX option directly.
+       (sock_make_secure): Create an SSL_CTX if no nssl_context is
+       supplied, otherwise use SSL_CTX from nssl_context.
+
+Sun Jan 28 13:52:03 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http_utils.c (neon_version_minimum): New function.
+
+Sun Jan 28 10:37:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * neon_config.h.in: Removed file.
+
+       * http_request.c, http_utils.c: Don't include neon_config.h.
+
+Sat Jan 27 22:52:37 2001  Joe Orton  <joe@light.plus.com>
+
+       * socket.c: Use closesocket() as NEON_CLOSE (Markus Fleck
+       <fleck@isoc.de>).
+
+Sat Jan 27 22:35:16 2001  Joe Orton  <joe@light.plus.com>
+
+       * hip_xml.c: Add 'char_data' as libxml cdataBlock handler.
+
+Tue Jan 23 23:17:00 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h.in: Renamed from neon_config.h.  Define
+       NEON_VERSION_MAJOR and NEON_VERSION_MINOR too: all picked up from
+       the NEON_VERSIONS macro.
+
+Sun Jan 21 22:07:34 2001  Joe Orton  <joe@light.plus.com>
+
+       * string_utils.c (ne_concat): New function.
+
+Thu Jan 18 22:25:34 2001  Joe Orton  <joe@light.plus.com>
+
+       * ne_alloc.h: Added ne_oom_callback.
+
+       * ne_alloc.c: Added DO_MALLOC macro to do the malloc and oom
+       callback test.  (ne_malloc): Use DO_MALLOC.  (ne_strdup,
+       ne_calloc, ne_strndup): Reimplement using DO_MALLOC rather than
+       calling ne_malloc().
+
+Tue Jan 16 20:16:35 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.10.1.
+
+Tue Jan 16 20:14:40 2001  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c (http_session_create): Default expect-100 support
+       to OFF.
+
+Mon Jan 15 22:59:33 2001  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.10.0.
+
+Mon Jan 15 22:58:04 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_basic.c (dav_simple_request): Use dav_207_ignore_unknown.
+
+Sun Jan 14 22:52:31 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c (propfind): Call dav_207_ignore_unknown.
+
+Sun Jan 14 22:49:06 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_207.c: Don't handle unknown elements in normal handler.
+       (ignore_cc, dav_207_ignore_unknown): New functions, for ignoring
+       any unknown elements in the parse.
+
+Sun Jan 14 21:53:00 2001  Joe Orton  <joe@light.plus.com>
+
+       * hip_xml.c: Renamed 'handlers' back to top_handlers.
+       (push_handler): Now, p->root->handlers points to BASE of stack,
+       p->top_handlers points to TOP of stack.  (hip_xml_destroy):
+       Changed to start from new BASE of stack.
+
+Sun Jan 14 10:50:09 2001  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c (http_session_server): Do perform the DNS lookup
+       if we have a proxy_decider function, since that means we MIGHT
+       need to know the IP address of the origin server.
+       (http_request_create): Pass the real scheme back to the proxy
+       decider callback.
+
+Wed Jan 10 22:43:16 2001  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in: Rename OBJ_EXT to NEON_OBJEXT.  Remove
+       NEON_INTERFACE_VERSION, use NEON_LINK_FLAGS instead.
+
+Wed Jan 10 22:02:02 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_locks.c (create_private): New function.
+       (dav_lock_discover): Switch to using new dav_propfind_set_complex
+       API.
+
+Wed Jan 10 21:59:36 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.h (dav_propfind_set_complex): Switch the
+       'sizeof_private' argument for a callback 'creator': the return
+       value of this callback is used as the 'private' field for the
+       current resource.
+
+Mon Jan  8 22:09:55 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_locks.h (dav_lock_result): Re-order arguments.  Make lock
+       object const.
+       
+       * dav_locks.c (dav_lock_copy): New function.  (discover_results):
+       Set lock->uri given href for current results.  Free lock object
+       after passing to results.
+
+Sun Jan  7 21:55:14 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_locks.c (dav_lock): Destroy XML parser after use.  Remove
+       handling of Lock-Token header: it wasn't used and it leaked.
+
+Sun Jan  7 19:58:29 2001  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c (free_propset): Free the property values, and the
+       result set URI too.
+
+Sun Jan  7 16:58:19 2001  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c (read_response_block): Fix handling of
+       SOCK_CLOSED from sock_read as end-of-connection.
+
+Sat Jan  6 15:02:57 2001  Joe Orton  <joe@light.plus.com>
+
+       * hip_xml.c (struct hip_xml_parser): Renamed 'top_handlers' to
+       'handlers' in hip_xml_parser.  (push_handler): New function.
+       (hip_xml_push_handler, hip_xml_push_mixed_handler): Use
+       push_handler.
+
+       * hip_xml.c (find_handler): Begin the search for a new handler
+       from the handler of the current (parent) element, and work up the
+       stack.
+
+Sat Jan  6 11:15:17 2001  Joe Orton  <joe@light.plus.com>
+
+       * hip_xml.c (find_handler): Display error message for unknown XML
+       element as 'elmname (in nspace)' rather than 'nspace:elmname'
+       since the latter produces confusing errors like
+       'DAV::displayname'.
+
+Wed Jan  3 21:34:44 2001  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in: Hard-code top_builddir as '..' (possibly wrong, but
+       true for all neon apps so far).  Remove INCLUDES, now unused.  Add
+       top_srcdir.
+
+Fri Dec 22 22:51:27 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c: Added 'has_props' field to propfind_handler struct.
+       (set_body): Only add leading 'prop' element if has_props is not
+       set.  Set has_props.  Don't add trailing </prop> element here.
+       (dav_propfind_named): ... add it here instead.
+       (dav_propfind_set_complex, dav_propfind_set_flat): New set_body
+       interface.
+
+Fri Dec 22 21:10:39 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c (sock_transfer): Don't use NEON_READ here, this is
+       for reading from a non-socket fd.
+
+Wed Dec 20 00:19:34 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.9.1.
+
+Wed Dec 20 00:19:09 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c (free_propset): Don't free the private structure,
+       make this the caller's responsibility.
+
+Wed Dec 20 00:12:36 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c (end_propstat): Rename propstat argument to
+       'pstat_v' to kill warnings.
+
+Tue Dec 19 23:42:39 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c (start_response): Zero-out the private structure on
+       creation.
+
+Tue Dec 19 22:54:06 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c (read_response_block): Rename local variable
+       'socket' to 'sock'.
+
+Tue Dec 19 22:52:56 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_request.h: Rename argument from 'stat' in post_send
+       definition.
+
+Tue Dec 19 22:52:32 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_utils.h: Add 'const' to char * argument.
+
+Tue Dec 19 22:19:28 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.9.0.
+
+Tue Dec 19 22:12:19 2000  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.in: New file.
+
+Tue Dec 19 22:07:50 2000  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.incl: Removed file.
+
+Tue Dec 19 22:06:06 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_locks.c (dav_lock_discover): New callback-based lock
+       discovery interface.  Re-implemented using new propfind interface.
+
+Tue Dec 19 21:22:43 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.h: Replace old dav_propfind_* interface with better
+       one.  (dav_simple_propfind): Renamed from dav_get_props.
+       (dav_propfind_current_private, dav_propfind_set_complex,
+       dav_propfind_set_flat): New functions.
+       (dav_propfind_get_current_resource): Removed function.
+       (dav_propfind_named, dav_propfind_allprop): Change second argument
+       to be the results callback.
+
+       * dav_props.c: Replace implementatino of old interface with new
+       one.  (dav_simple_propfind): Re-implemented on top of new
+       all-singing all-dancing dav_propfind_* interface.
+
+Sun Dec 17 18:24:50 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c: Add dav_get_props, and all its auxiliaries.
+
+Sun Dec 17 15:43:55 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_props.c (propfind): Destroy the request after calling
+       http_get_status.
+
+Sun Dec 17 18:04:58 2000  Joe Orton  <joe@light.plus.com>
+
+       * hip_xml.c (find_handler): Allow using NULL as name and nspace in
+       HIP_ELM_unknown elements.
+
+Sun Dec 17 18:03:03 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_207.c (check_context): Don't handle the unknown element when
+       it is a child of the prop element, this prevents handling allprop
+       responses.
+
+Thu Dec 14 21:48:06 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.8.0.
+
+Thu Dec 14 21:43:31 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_basic.c, dav_locks.c, dav_props.c (everywhere): Changed to
+       new response-status interface, and _class->klass change.
+
+Thu Dec 14 21:37:38 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c (http_get_status): Add new response-status
+       interface.
+
+Thu Dec 14 21:30:25 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_basic.c (everywhere): Use new status interface.
+
+Thu Dec 14 21:25:03 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_private.h: Made http_status pointer in http_req a declared
+       object.
+
+       * http_request.h: Removed passing status pointer to
+       http_request_dispatch.
+
+       * http_request.c (everywhere): Removed passing extra http_status *
+       to auxiliaries, use req->status instead.  Renamed '_class' to
+       'klass' everywhere.
+       
+Thu Dec 14 21:15:54 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_utils.h: Renamed '_class' member of http_status to 'klass'.
+       (http_parse_statusline): Change accordingly.
+
+Wed Dec 13 23:00:23 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c: Changes for pre-BONE BeOS (David Reid
+       <dreid@jetnet.co.uk>).
+
+Wed Dec 13 21:29:36 2000  Joe Orton  <joe@light.plus.com>
+
+       * uri.c (ESCAPE): Explicitly cast the character to const unsigned
+       int.  (uri_abspath_escape): Make 'pnt' a normal const char *.
+
+Wed Dec 13 21:17:31 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c: Remove netinet/in.h include, add limits.h
+       include. (Peter Boos) (read_response_block): Make readlen a size_t
+       (David Reid).
+
+Wed Dec 13 21:08:08 2000  Joe Orton  <joe@light.plus.com>
+
+       * ne_alloc.h [WIN32]: Include stdlib.h. (Peter Boos)
+
+Wed Dec 13 20:54:27 2000  Joe Orton  <joe@light.plus.com>
+
+       Patches from Peter Boos and David Reid for Win32 and
+       BeOS changes respectively:
+       
+       * socket.c: Add NEON_READ, NEON_WRITE, NEON_CLOSE macros to use
+       send/recv/closesocket for BeOS, send/recv/close for Win32,
+       write/read/close otherwise.  Include WinSock2.h in Windows.  Add
+       arpa/inet.h check. (sock_read, sock_write): Use the NEON_ macros.
+       (sock_connect, sock_close): Use NEON_CLOSE. (sock_init): Winsock
+       initialization.  (sock_exit) Winsock cleanup.  (sock_fullwrite):
+       Use size_t rather than ssize_t for 'sent'.  (sock_connect,
+       sock_connect_u): Make 'port' parameter an unsigned short int.
+
+Wed Dec 13 20:42:18 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_basic.c (clength_hdr_handler): Use an off_t for len, to
+       avoid comparison with size_t.
+
+Wed Dec 13 20:38:59 2000  Joe Orton  <joe@light.plus.com>
+
+       * hip_xml.c (char_data): Use an 'int' for wslen, avoid comparison
+       between size_t (which is signed) and int (which [is|might be?]
+       unsigned).
+
+Wed Dec 13 20:29:12 2000  Joe Orton  <joe@light.plus.com>
+
+       * nsocket.h [WIN32]: Use Windows headers rather than Unixy ones.
+       (sock_exit): New function.
+
+Wed Dec 13 20:21:22 2000  Joe Orton  <joe@light.plus.com>
+
+       * string_utils.c, string_utils.h, uri.h: Includes change (Peter
+       Boos).
+
+Wed Dec 13 20:20:09 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_auth.c (http_auth_response_body): Don't make
+       inline. Includes change.  (both by Peter Boos).
+
+Wed Dec 13 20:18:38 2000  Joe Orton  <joe@light.plus.com>
+
+       * uri.c (uri_unescape): Cast strtol return to (char). Includes
+       change as below (both by Peter Boos).
+
+Wed Dec 13 20:07:38 2000  Joe Orton  <joe@light.plus.com>
+
+       * base64.c, dates.c, dates.h, dav_207.c, dav_207.h, dav_basic.h,
+       dav_locks.h, hip_xml.h, http_auth.h, http_basic.h, http_cookies.c,
+       http_redirect.c, http_redirect.h, http_request.h, http_utils.c,
+       md5.c, ne_alloc.c: Use #include "..." rather than #include <...>
+       for neon headers. (Peter Boos <PediB@colorfullife.com>).
+
+Thu Dec  7 21:45:02 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c (sock_read): Return zero immediately if a zero count
+       parameter is passed, following SUSv2 semantics.
+
+Thu Dec  7 21:41:36 2000  Joe Orton  <joe@light.plus.com>
+
+       * nsocket.h (sock_readfile_blocked): Define an interface, allow
+       taking -1 as the length parameter.  Only return SOCK_CLOSED if
+       length == -1 is NOT passed.
+
+Sun Nov 26 09:46:53 2000  Joe Orton  <joe@light.plus.com>
+
+       * nsocket.h: Fix use of 'socket' in function prototypes.
+
+Sun Nov 19 00:29:48 2000  Joe Orton  <joe@light.plus.com>
+
+       * nsocket.h: Increase read timeout to 120 seconds.
+
+Sun Nov  5 14:42:46 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_locks.c: Fix element id's (fixes segfault when using locks).
+
+Thu Oct 26 22:28:17 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c (sock_peek): Return SOCK_CLOSED if recv() returns zero.
+
+Thu Oct 26 22:24:14 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c (sock_block): Return "got data" if SSL_pending
+       indicates data pending.  Otherwise select on socket as normal.
+
+Thu Oct 26 22:15:14 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c (sock_readline, sock_peek): Check whether SSL
+       connection has been closed if SSL_peek returns 0 (thanks to Jeff
+       Costlow <j.costlow@f5.com>).
+
+Thu Oct 14 19:57:31 2000  Joe Orton  <joe@light.plus.com>
+
+       * Makefile.incl: Fix spurius backslash at line 69 (thanks to 
+       Dirk Bergstrom <dirk@juniper.net>).
+
+Sat Oct 14 19:51:44 2000  Joe Orton  <joe@light.plus.com>
+
+       * dav_basic.c (copy_or_move): Use http_get_scheme rather than
+       hard-coding "http".
+
+2000-10-02  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c (http_get_scheme): New function.
+
+Tue Oct 10 19:56:42 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.7.5.
+
+Sat Oct  7 19:26:58 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.7.4.
+
+Sat Oct  7 19:19:37 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_auth.c (request_digest): Quote algorithm and qop parameters
+       in digest header.
+
+Sat Oct  7 19:15:29 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c (sock_connect_u): Don't leak the fd if connect fails
+       (David Sloat).
+
+Sat Sep 16 16:49:57 2000  Joe Orton  <joe@light.plus.com>
+
+       * hip_xml.h: Add 'HIP_ELM_UNUSED', defining lowest element ID
+       which should be used.
+
+       * hip_xml.c, hip_xml.h (hip_xml_push_handler,
+       hip_xml_push_mixed_handler): Renamed from hip_xml_add_handler /
+       hip_xml_add_mixed_handler to reflect stack-like usage of handlers.
+       'handlers' field of hip_xml_parser renamed to top_handler for same
+       reason (globally search'n'replaced).
+
+       * hip_xml.h: Documentation update.
+       
+Thu Sep 14 22:37:33 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_auth.c (request_digest): Quote qop= value, fixes
+       IIS5 interop.
+
+Thu Sep 14 00:40:04 2000  Joe Orton  <joe@light.plus.com>
+
+       * socket.c (sock_connect_u): If connect() fails, close the socket
+       before returning: thanks to David Sloat <d.sloat@f5.com>.
+
+Tue Sep 12 20:08:40 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c (read_response_headers): Remove redundant
+       tolower().
+
+Tue Sep 12 00:41:39 2000  Joe Orton  <joe@light.plus.com>
+
+       * neon_config.h: Bumped version to 0.7.3.
+
+Mon Sep 11 15:31:13 2000  Joe Orton  <joe@light.plus.com>
+
+       * http_request.c, http_auth.c: Include snprintf.h if
+       HAVE_SNPRINTF_H is defined.
+
+Fri Sep  8 10:46:53 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.7.2.
+
+Fri Sep  8 10:44:42 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_block): Return immediately if this is an SSL
+       socket.
+
+Thu Sep  7 00:31:12 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.h: Correct order of hip_xml_validate_cb arguments in
+       prototype (thanks to Greg Stein).
+
+Thu Sep  7 00:27:29 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (propfind): Don't destroy the handler after use.
+       (dav_propfind_destroy): New function.
+
+Thu Sep  7 00:08:45 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.incl: Added targets for ne_alloc.o, string_utils.o,
+       uri.o, base64.o.
+
+Tue Aug 15 21:53:53 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.7.1.
+
+Tue Aug 15 21:16:34 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_request_create): Only add authentication
+       response body callbacks if a supply-credentials callback has been
+       set for the session.  (http_request_dispatch): Similarly for
+       response header callbacks.
+
+Mon Aug 14 09:28:38 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.7.0.
+
+Mon Aug 14 09:23:54 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * string_utils.h (SBUFFER_CAST): New macro.
+
+Mon Aug 14 09:13:05 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_private.h: Use a hash table for storing response header
+       handlers.  Added response header 'catchers', which are passed ALL
+       response headers.
+
+       * http_request.c (http_add_response_header_handler): Place the
+       handler in the correct hash bucket. (hdr_hash): New function.
+       (http_add_response_header_catcher): New function.
+       (http_request_destroy): Destroy the header catchers, and iterate
+       over the hash table to destroy the handlers.
+       (read_response_headers): Optimisation: hash and search for ':' in
+       a single loop.  Remove another local variable.  Iterate through
+       catchers too.
+
+Sun Aug 13 15:57:35 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.6.1.
+
+Sun Aug 13 15:50:42 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (send_request): Only go through the loop at most
+       twice.
+
+Sun Aug 13 15:49:52 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_redirect.c (destroy): Don't free the redirect structure.
+
+Sat Aug 12 17:10:32 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.6.0.
+
+Sat Aug 12 16:48:47 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_session_decide_proxy): New function.
+       (http_request_create): Call proxy "decider callback" to determine
+       whether to use the proxy server for a given request or not.
+
+Sat Aug 12 16:39:10 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.incl: Updated for http_private.h and
+       http_redirect.[ch].
+
+Sat Aug 12 16:36:49 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c: Removed data structure definitions.
+
+       * http_private.h: New file, contains data structure definitions.
+       Interface NOT exported.
+
+Sat Aug 12 16:31:32 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_redirect.c (destroy): No return value.
+
+Sat Aug 12 16:04:02 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_redirect.[ch]: First cut at HTTP redirect handling.
+
+Sat Aug 12 11:05:13 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.5.1.
+
+Sat Aug 12 02:04:15 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_locks.c (dav_lock_using_resource, dav_lock_using_parent):
+       Prevent segfault if locking is not in use.
+
+Fri Aug 11 17:19:06 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.5.0.
+
+Fri Aug 11 16:31:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (read_message_header): Take a buffer for storing
+       continuation lines. (read_response_headers): No need to strip EOL
+       since read_message_header does this already.  Use one less
+       variable.
+
+Fri Aug  4 22:12:04 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (send_request): Don't retry sending the request
+       more than once.
+
+Wed Aug  2 11:08:31 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * ne_alloc.[ch]: Renamed from xalloc.[ch].
+
+Wed Aug  2 02:15:32 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_transfer): Return SOCK_CLOSED, or set sock->error
+       appropriately on read failure.
+
+Tue Aug  1 13:04:27 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c, nsocket.h (sock_progress, sock_call_progress,
+       sock_transfer, sock_readfile_blocked): Use 'off_t' not 'size_t' as
+       file size type.
+
+Fri Jul 28 13:32:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.4.2.
+
+Fri Jul 28 13:31:38 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (send_request): Fix sending request body after
+       getting 100-continue response.
+
+Fri Jul 28 11:26:47 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.4.1.
+
+Fri Jul 28 10:32:34 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.4.0.
+
+Fri Jul 28 10:28:21 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.[ch] (http_post): New function, from Sander Alberink
+       <sander.alberink@cmg.nl>.
+
+Thu Jul 27 18:55:49 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c (neon_debug): No conditional compilation for
+       function body: compile it all regardless of whether debugging is
+       enabled or not, to allow applications to be debugged regardless of
+       whether debugging is compiled into the library or not.
+
+Thu Jul 27 16:59:26 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (clength_hdr_handler): Cast off_t to long int for
+       printing.
+
+Tue Jul 25 18:14:15 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (read_message_header): Iterate over header
+       handler list before placing zero-terminator at ':': if a handler
+       has a NULL name field, pass it the entire header value.
+
+Tue Jul 25 18:00:49 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_get_request_headers): New function.
+
+Mon Jul 24 16:55:29 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.3.9.
+
+Mon Jul 24 16:54:33 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.h: Use 'off_t' in http_content_range.
+
+       * http_basic.c (http_get_range): Cast range values to (long int)
+       to prevent compiler warnings.
+
+Thu Jul 20 20:03:30 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.[ch], socket.c, http_basic.c: Include nsocket.h not
+       socket.h.
+
+Thu Jul 20 20:02:20 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c (version_string): Indicate which XML parser is
+       supported.
+
+Thu Jul 20 20:01:12 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * nsocket.h: Renamed from socket.h.
+
+Thu Jul 20 15:02:35 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c, socket.c, http_request.c: SSL_ENABLE renamaed to
+       ENABLE_SSL.
+
+Thu Jul 20 12:20:13 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * md5.c, http_auth.h: Include neon_md5.h.
+
+Thu Jul 20 12:19:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_md5.h: Renamed from md5.h.
+
+Wed Jul 19 22:33:46 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (dav_simple_request): Don't leak ctype.value.
+
+Wed Jul 19 22:32:03 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c (check_context): Accept unknown elements.
+
+Wed Jul 19 22:31:10 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_locks.c (dav_lock_iterate): Allow passing func as NULL.
+
+Wed Jul 19 22:26:13 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.h (SOCKET_READ_TIMEOUT): Increase to 60.
+
+Wed Jul 19 22:25:51 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_locks.h: Include http_request.h.
+
+Mon Jul 17 11:41:16 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dates.c (asctime_parse, rfc1036_parse): Actually pass the string
+       to sscanf (thanks to lclint).  (rfc1123_date): Check for gmtime()
+       returning NULL.
+
+Mon Jul 17 09:16:43 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.3.1.
+
+Mon Jul 17 09:07:58 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_locks.c: Include limits.h: thanks to Paul D'Anna.
+
+Sun Jul 16 18:47:15 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: Bumped version to 0.3.0.
+
+Sun Jul 16 16:44:25 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_i18n.c (neon_i18n_init) [ENABLE_NLS && NEON_IS_LIBRARY]:
+       New compilation conditions.
+
+Sun Jul 16 16:41:12 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_i18n.c: New file.
+
+Sun Jul 16 16:15:02 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * string_utils.c (sbuffer_*): Change to assert postcondition
+       (buf->used == strlen(buf->data) + 1).  (sbuffer_append): Fix
+       brokenness.
+
+Sun Jul 16 16:11:05 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_transfer): Increase sum length written correctly.
+
+Sun Jul 16 16:10:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_get_hook_private): New function.
+
+Sun Jul 16 16:07:11 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c (find_handler): Call validate_cb for any handler which
+       recognizes the element.  Allow return codes
+       HIP_XML_{VALID,INVALID,DECLINE} from validate_cb.  If DECLINE is
+       returned, continue searching handlers until one returns
+       (IN)VALID.  (start_element): Don't call validate_cb.
+
+       * hip_xml.c (start_element, end_element): In collect mode, don't
+       print namespace prefix if present.
+
+Sun Jul 16 15:30:19 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_locks.[ch]: New file, code mainly taken from cadaver and
+       adapted for neon HTTP request/response handling.
+
+Sun Jul 16 15:28:25 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (copy_or_move, dav_move, dav_copy): Pass overwrite
+       as parameter.
+
+Sun Jul 16 15:26:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * uri.c (uri_compare): Fixed to only return equal if *shorter*
+       string has no trailing slash.
+
+Sat Jul 15 20:14:07 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c (dav_207_get_current_response,
+       dav_207_get_current_propstat): New functions.
+
+       * dav_props.c (dav_propfind_get_current_resource): Implement using
+       dav_207_get_current_response.
+
+Sat Jul 15 17:36:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * xalloc.c (xcalloc): New function.
+
+Sat Jul 15 14:11:14 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.[ch] and elsewhere: Replace 'class' field of
+       http_status with '_class' to be C++-safe. (patch from Tom
+       Bednarz).
+
+Thu Jul  6 18:48:52 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (copy_or_move): Escape the destination URI.
+
+Thu Jul  6 18:45:51 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (end_response): Added description parameter.
+
+Thu Jul  6 18:43:14 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.[ch] (end_element): Fix handling of responsedescription.
+       Add "description" parameter to dav_207_end_response callback, and
+       pass the contents of responsedescription.
+
+       * dav_basic.c (handle_error, end_response, end_propstat): Pass
+       description and add to error string when present.
+
+Tue Jul  4 11:43:03 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c, dav_props.c, http_basic.c: Use
+       http_{add,print}_request_header rather than
+       http_get_request_header.
+
+Tue Jul  4 11:41:00 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.[ch] (http_add_request_header,
+       http_print_request_header): New functions.
+       (http_get_request_header): Removed function.
+
+Mon Jul  3 21:50:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c: Add basic support for TLS upgrade (RFC2817).
+       (http_set_request_secure_upgrade, http_set_accept_secure_upgrade):
+       New functions.  (send_request): If upgrade is requested, and a 101
+       response is received, negotiate the TLS connection.
+       (add_fixed_headers): Add Upgrade header if necessary.
+
+Mon Jul  3 21:46:00 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (send_request): Don't go into an infinite loop.
+       (read_message_header): Simplyify checking for end-of-line.
+
+Tue Jun 13 00:29:42 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_session_proxy, http_session_server): Allow
+       calling >1 time per session, to switch servers.  (send_request):
+       Only retry sending request once.
+
+Mon Jun 12 21:50:41 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_add_hooks): New function.
+
+Mon Jun 12 21:37:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_cookies.[ch]: Added basic cookies support.
+
+Mon Jun 12 21:33:33 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_create_ssl_context, sock_destroy_ssl_context,
+       sock_disable_tlsv1, sock_disable_sslv2, sock_disable_sslv3,
+       sock_make_secure): Added nssl_context handling.
+
+Mon Jun 12 21:29:52 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_request_dispatch, http_request_create,
+       http_request_destroy, http_set_request_body_buffer,
+       http_set_request_body_stream): Added hook support.
+
+Mon Jun 12 21:04:00 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_set_secure): Store an nssl_context.
+       (open_connection): Give the nssl_context.
+
+Sun Jun 11 16:37:52 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * sslcerts.c: Import of SSL support from mutt, relicensed under
+       the LGPL for use in neon by the author, Tommi Komulainen
+       <Tommi.Komulainen@iki.fi>.
+
+Sun Jun 11 11:30:16 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (set_sockerr): Updated to use sock_get_error.
+
+Sun Jun 11 11:29:29 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * uri.c (uri_parse): Allow scheme to be omitted.
+
+Fri Jun  9 20:39:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_get_error): New function.  (sock_*): Set
+       sock->error and SOCK_ERROR on error.
+
+Mon May 29 16:32:46 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * uri.c (uri_abspath_escape): Allocate the exact amount of memory
+       needed.
+
+Mon May 29 15:53:33 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (dav_simple_request): Correct invalid XML logic.
+
+Mon May 29 15:52:08 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (create_sock, sock_accept, sock_get_fd): New
+       functions.  (sock_connect_u): Use create_sock.
+
+Sun May 28 21:00:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c (neon_version_string): New function.
+
+Sun May 28 19:36:45 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_connect_u): Zero out allocated nsocket object.
+
+Thu May 25 01:27:04 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * *.h: Include other neon headers with <braces>.
+
+Thu May 25 01:02:12 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c: Include stdlib.h for 'free'.
+
+Wed May 24 20:15:08 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (read_message_header): Return HTTP_RETRY if more
+       headers to read, HTTP_OK on end-of-headers.
+       (read_response_headers): Changed accordingly.
+
+Wed May 24 19:56:29 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (send_request_body): Return a SOCK_* code.
+       (send_request): Re-send request if socket has been closed (due to
+       persistent connection timeout).
+
+Wed May 24 19:00:01 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c (find_element): Fix unknown element handling.
+
+Tue May 23 19:12:26 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (propfind): Destroy the request body sbuffer after
+       use.
+
+Tue May 23 15:43:42 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_make_secure) [SSL_ENABLE]: Conditionally compile
+       SSL code. [!SSL_ENABLE]: Return failure.  (sock_close)
+       [SSL_ENABLE]: Conditionally compile SSL code.
+
+Tue May 23 15:37:53 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_session_create): Renamed from
+       http_session_init.  (http_session_destroy): Renamed frmo
+       http_session_finish.
+
+Sun May 21 23:50:58 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (lookup_host): Use sock_name_lookup.
+
+Sun May 21 23:40:39 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_request_create): Allow passing NULL uri
+       (internal use only).  (http_set_secure): New function.
+       (read_response_block, read_message_headers): Redone for new socket
+       API.  (build_request): Moved http_auth_new_request calls here
+       (from http_request_dispatch).  (send_request): Always call
+       open_connection before sending the request.
+       (read_message_header, read_response_headers): Looser check for
+       empty line.  (normalize_response_length): Set response body length
+       to zero on 2xx class response whilst in CONNECT, if no other
+       response body length is given.  (http_request_dispatch): Don't
+       close the connection on a HTTP/1.0 2xx class response after a
+       CONNECT request.  (proxy_tunnel): New function.
+       (open_connection): Use an SSL connection where appropriate.  Use
+       proxy_tunnel for tunnelling through a proxy.
+
+Sun May 21 01:35:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * src/socket.c: Added 'nsocket' ADT for handling sockets.
+       (sock_*): All changed to take 'nsocket *' argument rather than
+       integer file descriptor.  Added 'sock_secure_details' to
+       sock_status enum.  (sock_make_secure, sock_init): New function.
+       (sock_peek): Renamed from sock_recv.  (send_file_*, recv_file_*):
+       Removed functions.  (sock_name_lookup): Renamed from host_lookup.
+       (sock_service_lookup): Renamed from get_tcp_port.  (sock_block,
+       sock_read, sock_fullwrite, sock_peek, sock_readline): Added SSL
+       support.  (sock_transfer): Use sock_fullwrite and sock_read.
+
+Sun May 21 01:25:03 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_request_destroy): Free header handlers and
+       body readers.
+
+Sun May 21 01:24:30 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.h: Removed obsolte got_property callback type.
+
+Sun May 21 01:23:59 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (propfind): Free handler object after use.
+
+Sun May 21 01:23:12 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c (dav_207_destroy): Don't try to free the 'response'
+       field.
+
+Sat May 20 21:45:32 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c: Changed 'te' enum of struct http_response to
+       'is_chunked' boolean.
+
+Sun May 14 01:00:42 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (propfind): Return error on parse error.
+
+Sun May 14 00:40:50 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h (NEON_VERSION): Bumped to 0.2.0.
+
+Sat May 13 23:31:28 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_defs.h (BEGIN_NEON_DECLS, END_NEON_DECLS): Added C++ safety
+       macros.
+
+       * *.h: Surround with C++ safety macros.
+
+Sat May 13 22:36:06 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c (hip_xml_destroy): Free handlers.
+
+Sat May 13 21:12:14 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c (hip_xml_valid): Reversed return value.
+
+Sat May 13 21:11:17 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c: Renamed http_debug_mask to neon_debug_mask,
+       similarly neon_debug_stream.  (neon_debug_init): Renamed from
+       http_debug_init.
+
+Sat May 13 19:24:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c: Initialize http_debug_mask to zero.
+       (http_debug_init): New function.
+
+       * http_utils.h: Fixed #ifdef DEBUGGING. Only define relevant
+       DEBUG_* constants.
+       
+Sat May 13 19:23:34 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_config.h: New file.
+
+       * http_request.c: Include neon_config.h for NEON_VERSION.
+
+Sat May 13 18:28:05 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (dav_propfind_create): Create a hip_xml_parser, a
+       207 parser, register start+end response handlers with 207 layer.
+       (propfind): Fix allprop (Michael Sobolev).
+
+       * dav_basic.c (dav_simple_request): Create and destroy
+       hip_xml_parser and 207 parser appropriately.
+
+Sat May 13 18:24:49 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c: Now takes an externally-declared hip_xml parser
+       pointer. (dav_207_create, dav_207_destroy): New functions.
+       (dav_207_init, dav_207_init_with_handler, dav_207_parse,
+       dav_207_error, dav_207_finish): Removed functions.
+
+Sat May 13 17:32:45 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.[ch]: Rewritten to use opaque hip_xml_parser pointer.
+       struct hip_xml_handler and struct hip_xml_state removed from
+       external interface. struct hip_xml_elm * passed to startelm_cb and
+       endelm_cb. (hip_xml_add_handler, hip_xml_valid, hip_xml_create,
+       hip_xml_destroy, hip_xml_set_error, hip_xml_get_error): New
+       functions.  (hip_xml_init, hip_xml_destroy): Removed functions.
+
+Sat May 13 13:43:56 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.h: Removed.
+
+Sat May 13 13:42:20 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * string_utils.h: Don't include config.h. (CONCAT*): Don't use
+       xmalloc, use malloc and abort manually.
+
+Sat May 13 13:32:46 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.h, dates.h, http_basic.h: Don't include config.h
+
+Sat May 13 13:31:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.[ch], dav_207.c: Use HIP_ERR_SIZE for size of parser
+       error string.
+
+Sat May 13 13:30:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.incl: Use obj_ext for object file extension.
+
+Thu May 11 18:21:53 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.h: Bumped version to 0.1.1.
+
+Thu May 11 18:16:08 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (get_to_fd): Fix short writes.
+
 Wed May 10 19:22:01 2000  Joe Orton  <joe@orton.demon.co.uk>
 
        * neon.h: Bumped version to 0.1.0.
index 534214a..a0fb73a 100644 (file)
@@ -1,48 +1,65 @@
 #
-# 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)
+# neon source directory Makefile
+# 
+# Use the NEON_NORMAL_BUILD or NEON_LIBTOOL_BUILD autoconf 
+# macros to set up this Makefile correctly.
 #
 
 SHELL = @SHELL@
 
-CFLAGS = @CFLAGS@
-LDFLAGS = @LDFLAGS@
-DEFS = @DEFS@
-INCLUDES = -I.. -I.
-top_builddir = @top_builddir@
+# Installation paths
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+
+# Build paths
+VPATH = @srcdir@
+top_builddir = ..
+top_srcdir = @top_srcdir@
+
+# Toolchain settings.
 CC = @CC@
-CCLD = $(CC)
 AR = @AR@
 RANLIB = @RANLIB@
+LIBTOOL = @LIBTOOL@
 
-LIBS = @LIBS@
-
-VPATH = @srcdir@
+# Flags
+CPPFLAGS = @DEFS@ @CPPFLAGS@
+CFLAGS = @CFLAGS@ @NEON_CFLAGS@
+LDFLAGS = @LDFLAGS@
+NEON_LINK_FLAGS = @NEON_LINK_FLAGS@
+# Note: don't substitute @LIBS@ in here; during a bundled
+# build of this directory, @LIBS@ may include -lneon.
+LIBS = @NEON_LIBS@ @NEON_LTLIBS@
 
-prefix = @prefix@
-exec_prefix = @exec_prefix@
-libdir = @libdir@
+COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS)
+LINK = $(LIBTOOL) --quiet --mode=link $(CC) $(LDFLAGS)
 
-LIBTOOL = @LIBTOOL@
-LINK = $(LIBTOOL) --quiet --mode=link $(CCLD) $(LDFLAGS)
-COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CFLAGS)
-NEON_INTERFACE_VERSION = @NEON_INTERFACE_VERSION@
+NEON_BASEOBJS = ne_request.@NEON_OBJEXT@ ne_session.@NEON_OBJEXT@          \
+       ne_basic.@NEON_OBJEXT@  ne_string.@NEON_OBJEXT@                     \
+       ne_uri.@NEON_OBJEXT@ ne_dates.@NEON_OBJEXT@ ne_alloc.@NEON_OBJEXT@  \
+       ne_md5.@NEON_OBJEXT@ ne_utils.@NEON_OBJEXT@    \
+       ne_socket.@NEON_OBJEXT@ ne_auth.@NEON_OBJEXT@                       \
+       ne_cookies.@NEON_OBJEXT@ ne_redirect.@NEON_OBJEXT@                  \
+       ne_compress.@NEON_OBJEXT@
 
-OBJECTS = @NEONOBJS@
+NEON_DAVOBJS = $(NEON_BASEOBJS) \
+       ne_207.@NEON_OBJEXT@ ne_xml.@NEON_OBJEXT@ \
+       ne_props.@NEON_OBJEXT@ ne_locks.@NEON_OBJEXT@
 
-obj_ext = @OBJ_EXT@
+OBJECTS = @NEONOBJS@ @NEON_EXTRAOBJS@
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o
 
 NEON_TARGET = @NEON_TARGET@
 
-all: $(NEON_TARGET)
+# Thanks to gettext for this neat trick.
+all: all-@NEON_BUILD_BUNDLED@
+
+all-yes: $(NEON_TARGET)
+all-no:
+       @echo "Bundled neon build not being used."
 
 .c.lo:
        $(LIBTOOL) --quiet --mode=compile $(COMPILE) -c $< -o $@
@@ -50,60 +67,81 @@ all: $(NEON_TARGET)
        $(COMPILE) -c $< -o $@
 
 libneon.la: $(OBJECTS)
-       $(LINK) -rpath $(libdir) -version-info $(NEON_INTERFACE_VERSION) -o $@ $(LDFLAGS) $(OBJECTS) $(LIBS)
+       $(LINK) -rpath $(libdir) $(NEON_LINK_FLAGS) -o $@ $(OBJECTS) $(LIBS)
 
 libneon.a: $(OBJECTS)
        $(AR) cru $@ $(OBJECTS)
        $(RANLIB) $@
 
 clean:
-       rm -f @NEONOBJS@ $(NEON_TARGET)
+       rm -f $(NEON_TARGET) *.o *.lo
+       rm -rf .libs
 
-neonreq = http_request.h http_utils.h string_utils.h nsocket.h \
-       ne_alloc.h $(top_builddir)/config.h
+c++.c:
+       find . -name ne_\*.h -print | sed 's/.*/#include "&"/;/ne_private/d' > $@
+       echo "int main(void) {}" >> $@
 
-http_request.$(obj_ext): http_request.c neon_config.h $(neonreq) \
-       neon_i18n.h http_private.h
+check-c++: c++.c
+       c++ -I. c++.c
 
-socket.$(obj_ext): socket.c nsocket.h $(top_builddir)/config.h string_utils.h
+check-incl:
+       @for f in ne_*.h; do \
+        echo Checking $$f...; \
+        echo "#include \"$$f\"" > checkincl.c; \
+        $(COMPILE) -c checkincl.c -o checkincl.o || exit 1; done
 
-http_auth.$(obj_ext): http_auth.c http_auth.h string_utils.h \
-       $(top_builddir)/config.h dates.h base64.h neon_md5.h
+neonreq = ne_request.h ne_session.h ne_utils.h ne_string.h ne_socket.h \
+       ne_alloc.h $(top_builddir)/config.h ne_private.h
 
-dav_basic.$(obj_ext): dav_basic.c $(neonreq) dav_207.h hip_xml.$(obj_ext)
+ne_request.@NEON_OBJEXT@: ne_request.c $(neonreq) ne_i18n.h ne_private.h \
+       ne_uri.h
 
-http_basic.$(obj_ext): http_basic.c http_basic.h $(neonreq)
+ne_session.@NEON_OBJEXT@: ne_session.c ne_session.h ne_alloc.h \
+       ne_utils.h ne_private.h $(top_builddir)/config.h
 
-http_utils.$(obj_ext): http_utils.c $(top_builddir)/config.h \
-       http_utils.h dates.h neon_config.h
+ne_openssl.@NEON_OBJEXT@: ne_openssl.c ne_session.h ne_ssl.h ne_privssl.h \
+       ne_private.h $(top_builddir)/config.h
 
-hip_xml.$(obj_ext): hip_xml.c hip_xml.h string_utils.h http_utils.h \
-       $(top_builddir)/config.h
+ne_socket.@NEON_OBJEXT@: ne_socket.c ne_socket.h $(top_builddir)/config.h \
+       ne_privssl.h ne_string.h
 
-dav_207.$(obj_ext): dav_207.c dav_207.h hip_xml.h \
-       $(top_builddir)/config.h http_utils.h neon_i18n.h
+ne_auth.@NEON_OBJEXT@: ne_auth.c ne_auth.h $(neonreq) \
+       ne_dates.h ne_md5.h ne_uri.h 
 
-string_utils.$(obj_ext): string_utils.c string_utils.h ne_alloc.h \
+ne_basic.@NEON_OBJEXT@: ne_basic.c ne_basic.h $(neonreq)
+
+ne_utils.@NEON_OBJEXT@: ne_utils.c $(top_builddir)/config.h \
+       ne_utils.h ne_dates.h
+
+ne_xml.@NEON_OBJEXT@: ne_xml.c ne_xml.h ne_string.h ne_utils.h \
        $(top_builddir)/config.h
 
-ne_alloc.$(obj_ext): ne_alloc.c ne_alloc.h $(top_builddir)/config.h
+ne_207.@NEON_OBJEXT@: ne_207.c ne_207.h ne_xml.h \
+       $(top_builddir)/config.h ne_utils.h ne_i18n.h
+
+ne_string.@NEON_OBJEXT@: ne_string.c ne_string.h ne_alloc.h \
+       $(top_builddir)/config.h
 
-dates.$(obj_ext): dates.c dates.h $(top_builddir)/config.h
+ne_alloc.@NEON_OBJEXT@: ne_alloc.c ne_alloc.h $(top_builddir)/config.h
 
-base64.$(obj_ext): base64.c base64.h $(top_builddir)/config.h
+ne_dates.@NEON_OBJEXT@: ne_dates.c ne_dates.h $(top_builddir)/config.h
 
-uri.$(obj_ext): uri.c uri.h http_utils.h string_utils.h ne_alloc.h \
+ne_uri.@NEON_OBJEXT@: ne_uri.c ne_uri.h ne_utils.h ne_string.h ne_alloc.h \
        $(top_builddir)/config.h
 
-md5.$(obj_ext): md5.c neon_md5.h $(top_builddir)/config.h
+ne_md5.@NEON_OBJEXT@: ne_md5.c ne_md5.h $(top_builddir)/config.h
+
+ne_props.@NEON_OBJEXT@: ne_props.c $(top_builddir)/config.h \
+       ne_props.h ne_207.h ne_xml.h $(neonreq)
+
+ne_locks.@NEON_OBJEXT@: ne_locks.c $(neonreq) ne_locks.h ne_207.h ne_xml.h
 
-dav_props.$(obj_ext): dav_props.c $(top_builddir)/config.h \
-       dav_props.h dav_207.h hip_xml.h
+ne_redirect.@NEON_OBJEXT@: ne_redirect.c $(neonreq) ne_redirect.h \
+       ne_uri.h ne_private.h
 
-dav_locks.$(obj_ext): dav_locks.c $(neonreq) dav_locks.h dav_207.h hip_xml.h
+ne_cookies.@NEON_OBJEXT@: ne_cookies.c $(neonreq) ne_cookies.h ne_uri.h \
+       ne_private.h
 
-http_redirect.$(obj_ext): http_redirect.c $(neonreq) http_redirect.h \
-       uri.h http_private.h
+ne_compress.@NEON_OBJEXT@: ne_compress.c $(neonreq) ne_compress.h
 
-http_cookies.$(obj_ext): http_cookies.c $(neonreq) http_cookies.h uri.h \
-       http_private.h
+ne_acl.@NEON_OBJEXT@: ne_acl.c ne_acl.h $(neonreq)
diff --git a/neon/src/memleak.h b/neon/src/memleak.h
new file mode 100644 (file)
index 0000000..8d77dcb
--- /dev/null
@@ -0,0 +1,55 @@
+/* 
+   Memory leak wrappers
+   Copyright (C) 2003, 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
+
+*/
+
+/* WARNING: THIS IS AN INTERNAL NEON INTERFACE AND MUST NOT BE USED
+ * from NEON APPLICATIONS. */
+
+/* This file contains an alternate interface to the memory allocation
+ * wrappers in ne_alloc.c, which perform simple leak detection.  It
+ * MUST NOT BE INSTALLED, or used from neon applications. */
+
+#ifndef MEMLEAK_H
+#define MEMLEAK_H
+
+#include <stdio.h>
+
+#define ne_malloc(s) ne_malloc_ml(s, __FILE__, __LINE__)
+#define ne_calloc(s) ne_calloc_ml(s, __FILE__, __LINE__)
+#define ne_realloc(p, s) ne_realloc_ml(p, s, __FILE__, __LINE__)
+#define ne_strdup(s) ne_strdup_ml(s, __FILE__, __LINE__)
+#define ne_strndup(s, n) ne_strndup_ml(s, n, __FILE__, __LINE__)
+#define ne_free ne_free_ml
+
+/* Prototypes of allocation functions: */
+void *ne_malloc_ml(size_t size, const char *file, int line);
+void *ne_calloc_ml(size_t size, const char *file, int line);
+void *ne_realloc_ml(void *ptr, size_t s, const char *file, int line);
+char *ne_strdup_ml(const char *s, const char *file, int line);
+char *ne_strndup_ml(const char *s, size_t n, const char *file, int line);
+void ne_free_ml(void *ptr);
+
+/* Dump the list of currently allocated blocks to 'f'. */
+void ne_alloc_dump(FILE *f);
+
+/* Current number of bytes in allocated but not free'd. */
+extern size_t ne_alloc_used;
+
+#endif /* MEMLEAK_H */
index ac6065b..039bc6b 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    WebDAV 207 multi-status response handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
 #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_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;
+
+    ne_buffer *cdata;
+
+    /* remember whether we are in a response: the validation
+     * doesn't encapsulate this since we only count as being 
+     * "in a response" when we've seen the href element. */
+    int in_response;
+
     /* current position */
     void *response, *propstat;
     /* caching */
     ne_status status;
-    char *description, *href, *status_line;
+    char *description, *href;
 };
 
-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 }
+#define ELM_multistatus 1
+#define ELM_response 2
+#define ELM_responsedescription 3
+#define ELM_href 4
+#define ELM_prop (NE_207_STATE_PROP)
+#define ELM_status 6
+#define ELM_propstat 7
+
+static const struct ne_xml_idmap map207[] = {
+    { "DAV:", "multistatus", ELM_multistatus },
+    { "DAV:", "response", ELM_response },
+    { "DAV:", "responsedescription", ELM_responsedescription },
+    { "DAV:", "href", ELM_href },
+    { "DAV:", "propstat", ELM_propstat },
+    { "DAV:", "prop",  ELM_prop },
+    { "DAV:", "status", ELM_status }
 };
 
 /* 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)
+                                  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)
+                                  ne_207_start_propstat *start,
+                                  ne_207_end_propstat *end)
 {
     p->start_propstat = start;
     p->end_propstat = end;
@@ -89,182 +103,138 @@ 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) 
+/* return non-zero if (child, parent) is an interesting element */
+static int can_handle(int parent, int child) 
+{
+    return (parent == 0 && child == ELM_multistatus) ||
+        (parent == ELM_multistatus && child == ELM_response) ||
+        (parent == ELM_response && 
+         (child == ELM_href || child == ELM_status || 
+          child == ELM_propstat || child == ELM_responsedescription)) ||
+        (parent == ELM_propstat &&
+         (child == ELM_prop || child == ELM_status ||
+          child == ELM_responsedescription));
+}
+
+static int cdata_207(void *userdata, int state, const char *buf, size_t len)
 {
     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;
-    }
+
+    if ((state == ELM_href || state == ELM_responsedescription ||
+         state == ELM_status) && p->cdata->used + len < 2048)
+        ne_buffer_append(p->cdata, buf, len);
+
     return 0;
 }
 
+static int start_element(void *userdata, int parent, 
+                         const char *nspace, const char *name, 
+                         const char **atts) 
+{
+    ne_207_parser *p = userdata;
+    int state = ne_xml_mapid(map207, NE_XML_MAPLEN(map207), nspace, name);
+
+    if (!can_handle(parent, state))
+        return NE_XML_DECLINE;
+
+    /* if not in a response, ignore everything. */
+    if (!p->in_response && state != ELM_response && state != ELM_multistatus &&
+        state != ELM_href)
+        return NE_XML_DECLINE;
+
+    if (state == ELM_propstat && p->start_propstat)
+        p->propstat = p->start_propstat(p->userdata, p->response);
+
+    ne_buffer_clear(p->cdata);
+
+    return state;
+}
+
+#define GIVE_STATUS(p) ((p)->status.reason_phrase?&(p)->status:NULL)
+
+#define HAVE_CDATA(p) ((p)->cdata->used > 1)
+
 static int 
-end_element(void *userdata, const struct ne_xml_elm *elm, const char *cdata)
+end_element(void *userdata, int state, const char *nspace, const char *name)
 {
     ne_207_parser *p = userdata;
+    const char *cdata = p->cdata->data;
 
-    switch (elm->id) {
-    case NE_ELM_responsedescription:
-       if (cdata != NULL) {
+    switch (state) {
+    case ELM_responsedescription:
+       if (HAVE_CDATA(p)) {
            NE_FREE(p->description);
            p->description = ne_strdup(cdata);
        }
        break;
-    case NE_ELM_href:
+    case ELM_href:
        /* Now we have the href, begin the response */
-       if (p->start_response && cdata != NULL) {
-           p->response = (*p->start_response)(p->userdata, cdata);
+       if (p->start_response && HAVE_CDATA(p)) {
+           p->response = p->start_response(p->userdata, cdata);
+           p->in_response = 1;
        }
        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)) {
+    case ELM_status:
+       if (HAVE_CDATA(p)) {
+           NE_FREE(p->status.reason_phrase);
+           if (ne_parse_statusline(cdata, &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_snprintf(buf, 500, 
+                           _("Invalid HTTP status line in status element "
+                              "at line %d of response:\nStatus line was: %s"),
+                           ne_xml_currentline(p->parser), cdata);
                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);
+               NE_DEBUG(NE_DBG_XML, "Decoded status line: %s\n", cdata);
            }
        }
        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);
-       }
+    case ELM_propstat:
+       if (p->end_propstat)
+           p->end_propstat(p->userdata, p->propstat, GIVE_STATUS(p),
+                           p->description);
        p->propstat = NULL;
        NE_FREE(p->description);
-       NE_FREE(p->status_line);
+       NE_FREE(p->status.reason_phrase);
        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);
-       }
+    case ELM_response:
+        if (!p->in_response) break;
+       if (p->end_response)
+           p->end_response(p->userdata, p->response, GIVE_STATUS(p),
+                           p->description);
        p->response = NULL;
-       NE_FREE(p->status_line);
+       p->in_response = 0;
+       NE_FREE(p->status.reason_phrase);
        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;
-    
+    p->cdata = ne_buffer_create();
+
     /* Add handler for the standard 207 elements */
-    ne_xml_push_handler(parser, elements, check_context, 
-                        start_element, end_element, p);
+    ne_xml_push_handler(parser, start_element, cdata_207, end_element, p);
     
     return p;
 }
 
 void ne_207_destroy(ne_207_parser *p) 
 {
-    free(p);
+    if (p->status.reason_phrase) ne_free(p->status.reason_phrase);
+    ne_buffer_destroy(p->cdata);
+    ne_free(p);
 }
 
-int ne_accept_207(void *userdata, ne_request *req, ne_status *status)
+int ne_accept_207(void *userdata, ne_request *req, const ne_status *status)
 {
     return (status->code == 207);
 }
@@ -291,13 +261,15 @@ static void *start_response(void *userdata, const char *href)
     return NULL;
 }
 
-static void handle_error(struct context *ctx,
-                        const char *status_line, const ne_status *status,
+static void handle_error(struct context *ctx, const ne_status *status,
                         const char *description)
 {
     if (status && status->klass != 2 && status->code != 424) {
+       char buf[50];
        ctx->is_error = 1;
-       ne_buffer_concat(ctx->buf, ctx->href, ": ", status_line, "\n", NULL);
+       sprintf(buf, "%d", status->code);
+       ne_buffer_concat(ctx->buf, ctx->href, ": ", 
+                        buf, " ", status->reason_phrase, "\n", NULL);
        if (description != NULL) {
            /* TODO: these can be multi-line. Would be good to
             * word-wrap this at col 80. */
@@ -307,26 +279,26 @@ static void handle_error(struct context *ctx,
 
 }
 
-static void end_response(void *userdata, void *response, const char *status_line,
+static void end_response(void *userdata, void *response,
                         const ne_status *status, const char *description)
 {
     struct context *ctx = userdata;
-    handle_error(ctx, status_line, status, description);
+    handle_error(ctx, status, description);
 }
 
 static void 
-end_propstat(void *userdata, void *propstat, const char *status_line,
+end_propstat(void *userdata, void *propstat,
             const ne_status *status, const char *description)
 {
     struct context *ctx = userdata;
-    handle_error(ctx, status_line, status, description);
+    handle_error(ctx, status, description);
 }
 
 /* Dispatch a DAV request and handle a 207 error response appropriately */
+/* TODO: hook up Content-Type parsing; passing charset to XML parser */
 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;
@@ -341,10 +313,6 @@ int ne_simple_request(ne_session *sess, ne_request *req)
     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);
 
@@ -352,12 +320,12 @@ int ne_simple_request(ne_session *sess, ne_request *req)
        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));
+               ne_set_error(sess, "%s", 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);
+               ne_set_error(sess, "%s", ctx.buf->data);
                ret = NE_ERROR;
            }
        } else if (ne_get_status(req)->klass != 2) {
@@ -365,7 +333,6 @@ int ne_simple_request(ne_session *sess, ne_request *req)
        }
     }
 
-    NE_FREE(ctype.value);
     ne_207_destroy(p207);
     ne_xml_destroy(p);
     ne_buffer_destroy(ctx.buf);
index 4b74880..65fe471 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    WebDAV 207 multi-status response handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
 #define DAV207_H
 
 #include "ne_xml.h"
-#include "ne_request.h" /* for http_req */
+#include "ne_request.h" /* for ne_request */
 
 BEGIN_NEON_DECLS
 
-#define NE_ELM_207_first (NE_ELM_UNUSED)
+/* The defined state integer for the '{DAV:}prop' element. */
+#define NE_207_STATE_PROP (50)
+/* This interface reserves the state integers 'x' where 0 < x < 100 */
+#define NE_207_STATE_TOP (100)
+
+/* Handling of 207 multistatus XML documents.  A "multistatus"
+ * document is made up of a set of responses, each concerned with a
+ * particular resource.  Each response may have an associated result
+ * status and failure description.  A response is made up of a set of
+ * propstats, each of which again may have an associated result status
+ * and failure description. */
+
+/* Start and end response callbacks trigger at the start and end of
+ * each "response" within the multistatus body. 'href' gives the URI
+ * of the resource which is subject of this response.  The return
+ * value of a 'start_response' callback is passed as the 'response'
+ * parameter to the corresponding 'end_response' parameter. */
+typedef void *ne_207_start_response(void *userdata, const char *href);
+typedef void ne_207_end_response(void *userdata, void *response,
+                                 const ne_status *status,
+                                 const char *description);
+
+/* Similarly, start and end callbacks for each propstat within the
+ * response.  The return value of the 'start_response' callback for
+ * the response in which this propstat is contains is passed as the
+ * 'response' parameter.  The return value of each 'start_propstat' is
+ * passed as the 'propstat' parameter' to the corresponding
+ * 'end_propstat' callback. */
+typedef void *ne_207_start_propstat(void *userdata, void *response);
+typedef void ne_207_end_propstat(void *userdata, void *propstat,
+                                 const ne_status *status,
+                                 const char *description);
 
-#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 */
-
+/* Create 207 parser an add the handlers the the given parser's
+ * handler stack. */
 ne_207_parser *ne_207_create(ne_xml_parser *parser, void *userdata);
 
-/* Set the callbacks for the parser */
+/* Register response handling callbacks. */
+void ne_207_set_response_handlers(ne_207_parser *p,
+                                  ne_207_start_response *start,
+                                  ne_207_end_response *end);
 
-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);
+/* Register propstat handling callbacks. */
+void ne_207_set_propstat_handlers(ne_207_parser *p, 
+                                  ne_207_start_propstat *start,
+                                  ne_207_end_propstat *end);
 
+/* Destroy the parser */
 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);
+int ne_accept_207(void *userdata, ne_request *req, const 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 */
+/* Dispatch request 'req', returning:
+ *  NE_ERROR: for a dispatch error, or a non-2xx response, or a
+ *            207 response which contained a non-2xx propstat
+ *  NE_OK: for a 2xx response or a 207 response which contained
+ *            only 2xx-class propstats.
+ * The request object is destroyed in both cases. */
 int ne_simple_request(ne_session *sess, ne_request *req);
 
 END_NEON_DECLS
index f376621..1bee5aa 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Replacement memory allocation handling etc.
-   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+   Copyright (C) 1999-2003, 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
    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>
+#include "config.h"
 
 #ifdef HAVE_STRING_H
 #include <string.h>
 #include <stdlib.h>
 #endif
 
+#include <stdio.h>
+
 #include "ne_alloc.h"
 
+static void (*oom)(void);
+
+void ne_oom_callback(void (*callback)(void))
+{
+    oom = callback;
+}
+
+#ifndef NEON_MEMLEAK
+
+#define DO_MALLOC(ptr, len) do {               \
+    ptr = malloc((len));                       \
+    if (!ptr) {                                        \
+       if (oom != NULL)                        \
+           oom();                              \
+       abort();                                \
+    }                                          \
+} while(0);
+
 void *ne_malloc(size_t len) 
 {
-    void *ptr = malloc(len);
-    if (!ptr) {
-       /* uh-oh */
-       abort();
-    }
+    void *ptr;
+    DO_MALLOC(ptr, len);
     return ptr;
 }
 
 void *ne_calloc(size_t len) 
 {
-    return memset(ne_malloc(len), 0, len);
+    void *ptr;
+    DO_MALLOC(ptr, len);
+    return memset(ptr, 0, len);
+}
+
+void *ne_realloc(void *ptr, size_t len)
+{
+    void *ret = realloc(ptr, len);
+    if (!ret) {
+       if (oom)
+           oom();
+       abort();
+    }
+    return ret;
 }
 
 char *ne_strdup(const char *s) 
 {
-    return strcpy(ne_malloc(strlen(s) + 1), s);
+    char *ret;
+    DO_MALLOC(ret, strlen(s) + 1);
+    return strcpy(ret, s);
 }
 
 char *ne_strndup(const char *s, size_t n)
 {
-    char *new = ne_malloc(n + 1);
+    char *new;
+    DO_MALLOC(new, n+1);
     new[n] = '\0';
     memcpy(new, s, n);
     return new;
 }
+
+#else /* NEON_MEMLEAK */
+
+/* Memory-leak detection implementation: ne_malloc and friends are
+ * #defined to ne_malloc_ml etc by memleak.h, which is conditionally
+ * included by config.h. */
+
+/* memory allocated be ne_*alloc, but not freed. */
+size_t ne_alloc_used = 0;
+
+static struct block {
+    void *ptr;
+    size_t len;
+    const char *file;
+    int line;
+    struct block *next;
+} *blocks = NULL;
+
+void ne_alloc_dump(FILE *f)
+{
+    struct block *b;
+
+    for (b = blocks; b != NULL; b = b->next)
+        fprintf(f, "%" NE_FMT_SIZE_T "b@%s:%d%s", b->len, b->file, b->line,
+                b->next?", ":"");
+}
+
+static void *tracking_malloc(size_t len, const char *file, int line)
+{
+    void *ptr = malloc((len));
+    struct block *block;
+
+    if (!ptr) {
+       if (oom) oom();
+       abort();
+    }
+    
+    block = malloc(sizeof *block);
+    if (block != NULL) {
+        block->ptr = ptr;
+        block->len = len;
+        block->file = file;
+        block->line = line;
+        block->next = blocks;
+        blocks = block;
+        ne_alloc_used += len;
+    }
+
+    return ptr;
+}
+
+void *ne_malloc_ml(size_t size, const char *file, int line)
+{
+    return tracking_malloc(size, file, line);
+}
+
+void *ne_calloc_ml(size_t size, const char *file, int line)
+{
+    return memset(tracking_malloc(size, file, line), 0, size);
+}
+
+void *ne_realloc_ml(void *ptr, size_t s, const char *file, int line)
+{
+    void *ret = realloc(ptr, s);
+    struct block *b;
+
+    if (!ret) {
+        if (oom) oom();
+        abort();
+    }
+
+    for (b = blocks; b != NULL; b = b->next) {
+        if (b->ptr == ptr) {
+            ne_alloc_used = ne_alloc_used + s - b->len;
+            b->ptr = ret;
+            b->len = s;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+char *ne_strdup_ml(const char *s, const char *file, int line)
+{
+    return strcpy(tracking_malloc(strlen(s) + 1, file, line), s);
+}
+
+char *ne_strndup_ml(const char *s, size_t n, const char *file, int line)
+{
+    char *ret = tracking_malloc(n + 1, file, line);
+    ret[n] = '\0';
+    return memcpy(ret, s, n);
+}
+
+void ne_free_ml(void *ptr)
+{
+    struct block *b, *last = NULL;
+
+    for (b = blocks; b != NULL; last = b, b = b->next) {
+        if (b->ptr == ptr) {
+            ne_alloc_used -= b->len;
+            if (last) 
+                last->next = b->next;
+            else
+                blocks = b->next;
+            free(b);
+            break;
+        }
+    }
+
+    free(ptr);
+}
+
+#endif /* NEON_MEMLEAK */
index e120c6b..1da4a38 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Replacement memory allocation handling etc.
-   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+   Copyright (C) 1999-2002, 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
 #ifndef NE_ALLOC_H
 #define NE_ALLOC_H
 
+#ifdef WIN32
+#include <stdlib.h>
+#else
 #include <sys/types.h>
+#endif
 
-void *ne_malloc(size_t len);
-void *ne_calloc(size_t len);
+#include "ne_defs.h"
 
-char *ne_strdup(const char *s);
+BEGIN_NEON_DECLS
+
+/* Set callback which is called if malloc() returns NULL. */
+void ne_oom_callback(void (*callback)(void));
 
+#ifndef NEON_MEMLEAK
+/* Replacements for standard C library memory allocation functions,
+ * which never return NULL. If the C library malloc() returns NULL,
+ * neon will abort(); calling an OOM callback beforehand if one is
+ * registered.  The C library will only ever return NULL if the
+ * operating system does not use optimistic memory allocation. */
+void *ne_malloc(size_t size);
+void *ne_calloc(size_t size);
+void *ne_realloc(void *ptr, size_t s);
+char *ne_strdup(const char *s);
 char *ne_strndup(const char *s, size_t n);
+#define ne_free free
+#endif
+
+/* Handy macro to free things: takes an lvalue, and sets to NULL
+ * afterwards. */
+#define NE_FREE(x) do { if ((x) != NULL) ne_free((x)); (x) = NULL; } while (0)
+
+END_NEON_DECLS
 
 #endif /* NE_ALLOC_H */
index e613726..fd2cfb7 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP Authentication routines
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
 /* HTTP Authentication, as per RFC2617.
  * 
  * TODO:
- *  - Improve cnonce? Make it really random using /dev/random or whatever?
  *  - Test auth-int support
  */
 
 #include "config.h"
 
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
-
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for getpid() */
+#endif
 
-#include <time.h>
+#ifdef WIN32
+#include <windows.h> /* for GetCurrentThreadId() etc */
+#endif
 
-#ifdef HAVE_SNPRINTF_H
-#include "snprintf.h"
+#ifdef NEON_SSL
+#include <openssl/rand.h>
 #endif
 
-#include "base64.h"
+#include <time.h>
 
 #include "ne_md5.h"
 #include "ne_dates.h"
 #include "ne_uri.h"
 #include "ne_i18n.h"
 
+#ifdef HAVE_GSSAPI
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+#else
+#include <gssapi.h>
+#endif
+#endif
+
 /* 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_digest,
+    auth_scheme_gssapi
 } auth_scheme;
 
 typedef enum { 
@@ -87,10 +107,9 @@ typedef enum {
 /* A challenge */
 struct auth_challenge {
     auth_scheme scheme;
-    char *realm;
-    char *domain;
-    char *nonce;
-    char *opaque;
+    const char *realm;
+    const char *nonce;
+    const 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 */
@@ -99,59 +118,60 @@ struct auth_challenge {
     struct auth_challenge *next;
 };
 
-static const char *qop_values[] = {
-    NULL,
-    "auth",
-    "auth-int"
-};
-static const char *algorithm_names[] = {
-    "MD5",
-    "MD5-sess",
-    NULL
+static const struct auth_class {
+    const char *id, *req_hdr, *resp_hdr, *resp_info_hdr, *fail_msg;
+    int status_code, fail_code;
+} ah_server_class = {
+    HOOK_SERVER_ID,
+    "Authorization", "WWW-Authenticate", "Authentication-Info",
+    N_("Server was not authenticated correctly."), 401, NE_AUTH
+}, ah_proxy_class = {
+    HOOK_PROXY_ID,
+    "Proxy-Authorization", "Proxy-Authenticate", "Proxy-Authentication-Info",
+    N_("Proxy server was not authenticated correctly."), 407, NE_PROXYAUTH 
 };
 
-/* 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;
-
+    /* Which context will auth challenges be accepted? */
+    enum {
+        AUTH_ANY, /* ignore nothing. */
+        AUTH_CONNECT, /* only in response to a CONNECT request. */
+        AUTH_NOTCONNECT /* only in non-CONNECT responsees */
+    } context;
+    
+    /* Specifics for server/proxy auth. FIXME: need a better field
+     * name! */
+    const struct auth_class *spec;
+    
     /* 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;
+    ne_auth_creds creds;
+    void *userdata;
 
     /*** Session details ***/
 
     /* The username and password we are using to authenticate with */
-    char *username;
+    char username[NE_ABUFSIZ];
     /* Whether we CAN supply authentication at the moment */
     unsigned int can_handle:1;
     /* This used for Basic auth */
     char *basic; 
+#ifdef HAVE_GSSAPI
+    /* This used for GSSAPI auth */
+    char *gssapi_token;
+#endif
     /* These all used for Digest auth */
-    char *unq_realm;
-    char *unq_nonce;
-    char *unq_cnonce;
+    char *realm;
+    char *nonce;
+    char *cnonce;
     char *opaque;
-    /* A list of domain strings */
-    unsigned int domain_count;
-    char **domain;
     auth_qop qop;
     auth_algorithm alg;
-    int nonce_count;
+    unsigned int nonce_count;
     /* The ASCII representation of the session's H(A1) value */
     char h_a1[33];
 
@@ -165,11 +185,10 @@ typedef struct {
     const char *uri_scheme;
     unsigned int port;
 
+    int attempt;
 } auth_session;
 
 struct auth_request {
-    auth_session *session;
-
     /*** Per-request details. ***/
     ne_request *request; /* the request object. */
 
@@ -182,158 +201,86 @@ struct auth_request {
     /* 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->nonce);
+    NE_FREE(sess->cnonce);
     NE_FREE(sess->opaque);
-    NE_FREE(sess->username);
-    if (sess->domain_count > 0) {
-       split_string_free(sess->domain);
-       sess->domain_count = 0;
-    }
+    NE_FREE(sess->realm);
+#ifdef HAVE_GSSAPI
+    NE_FREE(sess->gssapi_token);
+#endif
 }
 
-/* Returns cnonce-value. We just use base64(time).
- * TODO: Could improve this? */
+/* Returns client nonce string. */
 static char *get_cnonce(void) 
 {
-    char *ret, *tmp;
-    tmp = ne_rfc1123_date(time(NULL));
-    ret = ne_base64(tmp, strlen(tmp));
-    free(tmp);
-    return ret;
-}
+    char ret[33];
+    unsigned char data[256], tmp[16];
+    struct ne_md5_ctx hash;
 
-static int 
-get_credentials(auth_session *sess, char **password) 
-{
-    return (*sess->reqcreds)(sess->reqcreds_udata, sess->unq_realm,
-                            &sess->username, password);
-}
+    ne_md5_init_ctx(&hash);
 
-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;
-}
+#ifdef NEON_SSL
+    if (RAND_status() == 1 && RAND_pseudo_bytes(data, sizeof data) >= 0)
+       ne_md5_process_bytes(data, sizeof data, &hash);
+    else {
+#endif
+    /* Fallback sources of random data: all bad, but no good sources
+     * are available. */
 
-/* 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;
+    /* Uninitialized stack data; yes, happy valgrinders, this is
+     * supposed to be here. */
+    ne_md5_process_bytes(data, sizeof data, &hash);
+    
+#ifdef HAVE_GETTIMEOFDAY
+    {
+       struct timeval tv;
+       if (gettimeofday(&tv, NULL) == 0)
+           ne_md5_process_bytes(&tv, sizeof tv, &hash);
+    }
+#else /* HAVE_GETTIMEOFDAY */
+    {
+       time_t t = time(NULL);
+       ne_md5_process_bytes(&t, sizeof t, &hash);
+    }
+#endif
+    {
+#ifdef WIN32
+       DWORD pid = GetCurrentThreadId();
 #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;
+       pid_t pid = getpid();
+#endif
+       ne_md5_process_bytes(&pid, sizeof pid, &hash);
     }
-    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;
-       }
+
+#ifdef NEON_SSL
     }
-    return ret;
 #endif
+    
+    ne_md5_finish_ctx(&hash, tmp);
+    ne_md5_to_ascii(tmp, ret);
+
+    return ne_strdup(ret);
+}
+
+static int get_credentials(auth_session *sess, char *pwbuf) 
+{
+    return sess->creds(sess->userdata, sess->realm, sess->attempt++,
+                      sess->username, pwbuf);
 }
 
 /* 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) 
+static int basic_challenge(auth_session *sess, struct auth_challenge *parms) 
 {
-    char *tmp, *password;
+    char *tmp, password[NE_ABUFSIZ];
 
     /* Verify challenge... must have a realm */
     if (parms->realm == NULL) {
@@ -341,25 +288,25 @@ basic_challenge(auth_session *sess, struct auth_challenge *parms)
     }
 
     NE_DEBUG(NE_DBG_HTTPAUTH, "Got Basic challenge with realm [%s]\n", 
-          parms->realm);
+            parms->realm);
 
     clean_session(sess);
+    
+    sess->realm = ne_strdup(parms->realm);
 
-    sess->unq_realm = shave_string(parms->realm, '"');
-
-    if (get_credentials(sess, &password)) {
+    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);
+    tmp = ne_concat(sess->username, ":", password, NULL);
+    sess->basic = ne_base64((unsigned char *)tmp, strlen(tmp));
+    ne_free(tmp);
 
-    NE_FREE(password);
+    /* Paranoia. */
+    memset(password, 0, sizeof password);
 
     return 0;
 }
@@ -367,19 +314,86 @@ basic_challenge(auth_session *sess, struct auth_challenge *parms)
 /* 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;
+    return ne_concat("Basic ", sess->basic, "\r\n", NULL);
 }
 
+#ifdef HAVE_GSSAPI
+/* Add GSSAPI authentication credentials to a request */
+static char *request_gssapi(auth_session *sess) 
+{
+    return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL);
+}
+
+static int get_gss_name(gss_name_t *server, auth_session *sess)
+{
+    unsigned int major_status, minor_status;
+    gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+
+    token.value = ne_concat("HTTP@", sess->sess->server.hostname, NULL);
+    token.length = strlen(token.value);
+
+    major_status = gss_import_name(&minor_status, &token,
+                                   GSS_C_NT_HOSTBASED_SERVICE,
+                                   server);
+    return GSS_ERROR(major_status) ? -1 : 0;
+}
+
+/* Examine a GSSAPI auth challenge; returns 0 if a valid challenge,
+ * else non-zero. */
+static int 
+gssapi_challenge(auth_session *sess, struct auth_challenge *parms) 
+{
+    gss_ctx_id_t context;
+    gss_name_t server_name;
+    unsigned int major_status, minor_status;
+    gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+
+    clean_session(sess);
+
+    if (get_gss_name(&server_name, sess))
+        return -1;
+
+    major_status = gss_init_sec_context(&minor_status,
+                                        GSS_C_NO_CREDENTIAL,
+                                        &context,
+                                        server_name,
+                                        GSS_C_NO_OID,
+                                        0,
+                                        GSS_C_INDEFINITE,
+                                        GSS_C_NO_CHANNEL_BINDINGS,
+                                        &input_token,
+                                        NULL,
+                                        &output_token,
+                                        NULL,
+                                        NULL);
+    gss_release_name(&minor_status, &server_name);
+
+    if (GSS_ERROR(major_status)) {
+        NE_DEBUG(NE_DBG_HTTPAUTH, "gss_init_sec_context failed.\n");
+        return -1;
+    }
+    
+    if (output_token.length == 0)
+        return -1;
+
+    sess->gssapi_token = ne_base64(output_token.value, output_token.length);
+    gss_release_buffer(&major_status, &output_token);
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, 
+             "Base64 encoded GSSAPI challenge: %s.\n", sess->gssapi_token);
+    sess->scheme = auth_scheme_gssapi;
+    return 0;
+}
+#endif
+
 /* 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) 
+static int digest_challenge(auth_session *sess, struct auth_challenge *parms) 
 {
     struct ne_md5_ctx tmp;
     unsigned char tmp_md5[16];
-    char *password;
+    char password[NE_ABUFSIZ];
 
     /* Verify they've given us the right bits. */
     if (parms->alg == auth_alg_unknown ||
@@ -398,27 +412,20 @@ static int digest_challenge(auth_session *sess,
        NE_DEBUG(NE_DBG_HTTPAUTH, "In digest challenge.\n");
 
        clean_session(sess);
-       sess->unq_realm = shave_string(parms->realm, '"');
+
+       sess->realm = ne_strdup(parms->realm);
 
        /* Not a stale response: really need user authentication */
-       if (get_credentials(sess, &password)) {
+       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;
-    }
+    sess->nonce = ne_strdup(parms->nonce);
+    sess->cnonce = get_cnonce();
+    /* TODO: add domain handling. */
     if (parms->opaque != NULL) {
        sess->opaque = ne_strdup(parms->opaque); /* don't strip the quotes */
     }
@@ -445,10 +452,10 @@ static int digest_challenge(auth_session *sess,
        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(sess->realm, strlen(sess->realm), &tmp);
        ne_md5_process_bytes(":", 1, &tmp);
-       if (password != NULL)
-           ne_md5_process_bytes(password, strlen(password), &tmp);
+       ne_md5_process_bytes(password, strlen(password), &tmp);
+       memset(password, 0, sizeof password); /* done with that. */
        ne_md5_finish_ctx(&tmp, tmp_md5);
        if (sess->alg == auth_alg_md5_sess) {
            unsigned char a1_md5[16];
@@ -461,9 +468,9 @@ static int digest_challenge(auth_session *sess,
            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(sess->nonce, strlen(sess->nonce), &a1);
            ne_md5_process_bytes(":", 1, &a1);
-           ne_md5_process_bytes(sess->unq_cnonce, strlen(sess->unq_cnonce), &a1);
+           ne_md5_process_bytes(sess->cnonce, strlen(sess->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);
@@ -472,7 +479,6 @@ static int digest_challenge(auth_session *sess,
            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");
@@ -502,11 +508,12 @@ static char *request_digest(auth_session *sess, struct auth_request *req)
     /* 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);
+       ne_snprintf(nc_value, 9, "%08x", sess->nonce_count);
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Nonce count is %u, nc is [%s]\n", 
+                sess->nonce_count, nc_value);
     }
-    qop_value = qop_values[sess->qop];
+
+    qop_value = sess->qop == auth_qop_auth_int ? "auth-int" : "auth";
 
     /* Calculate H(A2). */
     ne_md5_init_ctx(&a2);
@@ -551,17 +558,17 @@ static char *request_digest(auth_session *sess, struct auth_request *req)
     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(sess->nonce, strlen(sess->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);
+                nc_value, sess->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(sess->cnonce, strlen(sess->cnonce), &rdig);
        ne_md5_process_bytes(":", 1, &rdig);
        /* Store a copy of this structure (see note below) */
        sess->stored_rdig = rdig;
@@ -581,24 +588,23 @@ static char *request_digest(auth_session *sess, struct auth_request *req)
     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);
+                    "Digest username=\"", sess->username, "\", "
+                    "realm=\"", sess->realm, "\", "
+                    "nonce=\"", sess->nonce, "\", "
+                    "uri=\"", req->uri, "\", "
+                    "response=\"", rdig_md5_ascii, "\", "
+                    "algorithm=\"", sess->alg == auth_alg_md5 ? "MD5" : "MD5-sess", "\"", 
+                    NULL);
     
     if (sess->opaque != NULL) {
-       /* We never unquote it, so it's still quoted here */
-       ne_buffer_concat(ret, ", opaque=", sess->opaque, NULL);
+       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_concat(ret, ", cnonce=\"", sess->cnonce, "\", "
+                        "nc=", nc_value, ", "
+                        "qop=\"", qop_value, "\"", NULL);
     }
 
     ne_buffer_zappend(ret, "\r\n");
@@ -608,22 +614,83 @@ static char *request_digest(auth_session *sess, struct auth_request *req)
     return ne_buffer_finish(ret);
 }
 
+/* Parse line of comma-separated key-value pairs.  If 'ischall' == 1,
+ * then also return a leading space-separated token, as *value == NULL.
+ * Otherwise, if return value is 0, *key and *value will be non-NULL.
+ * If return value is non-zero, parsing has ended. */
+static int tokenize(char **hdr, char **key, char **value, int ischall)
+{
+    char *pnt = *hdr;
+    enum { BEFORE_EQ, AFTER_EQ, AFTER_EQ_QUOTED } state = BEFORE_EQ;
+    
+    if (**hdr == '\0')
+       return 1;
+
+    *key = NULL;
+
+    do {
+       switch (state) {
+       case BEFORE_EQ:
+           if (*pnt == '=') {
+               if (*key == NULL)
+                   return -1;
+               *pnt = '\0';
+               *value = pnt + 1;
+               state = AFTER_EQ;
+           } else if (*pnt == ' ' && ischall && *key != NULL) {
+               *value = NULL;
+               *pnt = '\0';
+               *hdr = pnt + 1;
+               return 0;
+           } else if (*key == NULL && strchr(" \r\n\t", *pnt) == NULL) {
+               *key = pnt;
+           }
+           break;
+       case AFTER_EQ:
+           if (*pnt == ',') {
+               *pnt = '\0';
+               *hdr = pnt + 1;
+               return 0;
+           } else if (*pnt == '\"') {
+               state = AFTER_EQ_QUOTED;
+           }
+           break;
+       case AFTER_EQ_QUOTED:
+           if (*pnt == '\"') {
+               state = AFTER_EQ;
+           }
+           break;
+       }
+    } while (*++pnt != '\0');
+    
+    if (state == BEFORE_EQ && ischall && *key != NULL) {
+       *value = NULL;
+    }
+
+    *hdr = pnt;
+
+    /* End of string: */
+    return 0;
+}
+
 /* 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) 
+static int verify_response(struct auth_request *req, auth_session *sess,
+                          const char *value) 
 {
-    char **pairs;
+    char *hdr, *pnt, *key, *val;
     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;
+       *qop_value = NULL;
+    unsigned int nonce_count;
+    int okay;
     
     if (!req->will_handle) {
        /* Ignore it */
@@ -631,43 +698,42 @@ int verify_response(struct auth_request *req, auth_session *sess, const char *va
     }
     
     if (sess->scheme != auth_scheme_digest) {
-       NE_DEBUG(NE_DBG_HTTPAUTH, "Found Auth-Info header not in response to Digest credentials - dodgy.\n");
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Found Auth-Info header not in response "
+                " to Digest credentials - dodgy.\n");
        return -1;
     }
+
+    pnt = hdr = ne_strdup(value);
     
     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) {
+    while (tokenize(&pnt, &key, &val, 0) == 0) {
+       val = ne_shave(val, "\"");
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Pair: [%s] = [%s]\n", key, val);
+       if (strcasecmp(key, "qop") == 0) {
+           qop_value = val;
+           if (strcasecmp(val, "auth-int") == 0) {
                qop = auth_qop_auth_int;
-           } else if (strcasecmp(pairs[n+1], "auth") == 0) {
+           } else if (strcasecmp(val, "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 if (strcasecmp(key, "nextnonce") == 0) {
+           nextnonce = val;
+       } else if (strcasecmp(key, "rspauth") == 0) {
+           rspauth = val;
+       } else if (strcasecmp(key, "cnonce") == 0) {
+           cnonce = val;
+       } else if (strcasecmp(key, "nc") == 0) { 
+           nc = val;
+           if (sscanf(val, "%x", &nonce_count) != 1) {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Couldn't find nonce count.\n");
            } else {
-               NE_DEBUG(NE_DBG_HTTPAUTH, "Got nonce_count: %d\n", nonce_count);
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Got nonce_count: %u\n", nonce_count);
            }
        }
-       free(unquoted);
     }
-    pair_string_free(pairs);
 
     /* Presume the worst */
     okay = -1;
@@ -676,7 +742,7 @@ int verify_response(struct auth_request *req, auth_session *sess, const char *va
        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) {
+           if (strcmp(cnonce, sess->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");
@@ -717,9 +783,9 @@ int verify_response(struct auth_request *req, auth_session *sess, const char *va
                if (qop != auth_qop_none) {
                    /* Add in qop-value */
                    NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting qop-value [%s:].\n", 
-                          qop_value);
+                            qop_value);
                    ne_md5_process_bytes(qop_value, strlen(qop_value), 
-                                      &sess->stored_rdig);
+                                        &sess->stored_rdig);
                    ne_md5_process_bytes(":", 1, &sess->stored_rdig);
                }
                /* Digest ":" H(A2) */
@@ -728,20 +794,16 @@ int verify_response(struct auth_request *req, auth_session *sess, const char *va
                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);
+               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;
@@ -750,132 +812,148 @@ int verify_response(struct auth_request *req, auth_session *sess, const char *va
     /* 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;
+       if (sess->nonce != NULL)
+           ne_free(sess->nonce);
+       sess->nonce = ne_strdup(nextnonce);
     }
 
+    ne_free(hdr);
+
     return okay;
 }
 
-/* A new challenge presented by the server */
-int auth_challenge(auth_session *sess, const char *value) 
+/* Passed the value of a "(Proxy,WWW)-Authenticate: " header field.
+ * Returns 0 if valid challenge was accepted; non-zero if no valid
+ * challenge was found. */
+static int auth_challenge(auth_session *sess, const char *value) 
 {
-    char **pairs, *pnt, *unquoted, *key;
+    char *pnt, *key, *val, *hdr;
     struct auth_challenge *chall = NULL, *challenges = NULL;
-    int n, success;
+    int success;
+
+    pnt = hdr = ne_strdup(value); 
 
     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");
+    /* 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. */
+
+    while (!tokenize(&pnt, &key, &val, 1)) {
 
-    for (n = 0; pairs[n]!=NULL; n+=2) {
-       /* Look for an auth-scheme in the key */
-       pnt = strchr(pairs[n], ' ');
-       if (pnt != NULL) {
+       if (val == NULL) {
            /* We have a new challenge */
-           NE_DEBUG(NE_DBG_HTTPAUTH, "New challenge.\n");
+           NE_DEBUG(NE_DBG_HTTPAUTH, "New challenge for scheme [%s]\n", key);
            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) {
+           if (strcasecmp(key, "basic") == 0) {
                NE_DEBUG(NE_DBG_HTTPAUTH, "Basic scheme.\n");
                chall->scheme = auth_scheme_basic;
-           } else if (strncasecmp(pairs[n], "digest ", 7) == 0) {
+           } else if (strcasecmp(key, "digest") == 0) {
                NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n");
                chall->scheme = auth_scheme_digest;
+#ifdef HAVE_GSSAPI             
+           } else if (strcasecmp(key, "negotiate") == 0) {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "GSSAPI scheme.\n");
+               chall->scheme = auth_scheme_gssapi;
+#endif
            } else {
                NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n");
-               free(chall);
+               ne_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;
+           continue;
        } 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);
+
+       /* Strip quotes off value. */
+       val = ne_shave(val, "\"'");
+
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, val);
+
        if (strcasecmp(key, "realm") == 0) {
-           chall->realm = pairs[n+1];
+           chall->realm = val;
        } else if (strcasecmp(key, "nonce") == 0) {
-           chall->nonce = pairs[n+1];
+           chall->nonce = val;
        } else if (strcasecmp(key, "opaque") == 0) {
-           chall->opaque = pairs[n+1];
-       } else if (strcasecmp(key, "domain") == 0) {
-           chall->domain = pairs[n+1];
+           chall->opaque = val;
        } else if (strcasecmp(key, "stale") == 0) {
            /* Truth value */
-           chall->stale = 
-               (strcasecmp(unquoted, "true") == 0);
+           chall->stale = (strcasecmp(val, "true") == 0);
        } else if (strcasecmp(key, "algorithm") == 0) {
-           if (strcasecmp(unquoted, "md5") == 0) {
+           if (strcasecmp(val, "md5") == 0) {
                chall->alg = auth_alg_md5;
-           } else if (strcasecmp(unquoted, "md5-sess") == 0) {
+           } else if (strcasecmp(val, "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);
+            /* iterate over each token in the value */
+            do {
+                const char *tok = ne_shave(ne_token(&val, ','), " \t");
+                
+                if (strcasecmp(tok, "auth") == 0) {
+                    chall->qop_auth = 1;
+                } else if (strcasecmp(tok, "auth-int") == 0 ) {
+                    chall->qop_auth_int = 1;
+                }
+            } while (val);
+            
+            chall->got_qop = chall->qop_auth || chall->qop_auth_int;
        }
-       free(unquoted);
     }
 
     NE_DEBUG(NE_DBG_HTTPAUTH, "Finished parsing parameters.\n");
 
     /* Did we find any challenges */
     if (challenges == NULL) {
-       pair_string_free(pairs);
+       ne_free(hdr);
        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)) {
+#ifdef HAVE_GSSAPI
+    if (strcmp(ne_get_scheme(sess->sess), "https") == 0) {
+        NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for GSSAPI.\n");
+        /* Try a GSSAPI challenge */
+        for (chall = challenges; chall != NULL; chall = chall->next) {
+            if (chall->scheme == auth_scheme_gssapi) {
+           if (!gssapi_challenge(sess, chall)) {
                success = 1;
                break;
            }
-       }
+            }
+        }
+    }
+#endif
+
+    /* Try a digest challenge */
+    if (!success) {
+        NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for Digest challenges.\n");
+        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");
+       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)) {
@@ -897,12 +975,11 @@ int auth_challenge(auth_session *sess, const char *value)
 
     while (challenges != NULL) {
        chall = challenges->next;
-       free(challenges);
+       ne_free(challenges);
        challenges = chall;
     }
 
-    /* Free up the parsed header values */
-    pair_string_free(pairs);
+    ne_free(hdr);
 
     return !success;
 }
@@ -911,48 +988,50 @@ int auth_challenge(auth_session *sess, const char *value)
 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_DEBUG(NE_DBG_HTTPAUTH, 
+            "Digesting %" NE_FMT_SIZE_T " 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)
+static void ah_create(ne_request *req, void *session, 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);
-
+    int is_connect = strcmp(method, "CONNECT") == 0;
+
+    if (sess->context == AUTH_ANY ||
+        (is_connect && sess->context == AUTH_CONNECT) ||
+        (!is_connect && sess->context == AUTH_NOTCONNECT)) {
+        struct auth_request *areq = ne_calloc(sizeof *areq);
+        
+        NE_DEBUG(NE_DBG_HTTPAUTH, "ah_create, for %s\n", sess->spec->resp_hdr);
+        
+        areq->method = method;
+        areq->uri = uri;
+        areq->request = req;
+        
+        ne_add_response_header_handler(req, sess->spec->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;
+        ne_add_response_header_handler(req, sess->spec->resp_info_hdr,
+                                       ne_duplicate_header, 
+                                       &areq->auth_info_hdr);
+        
+        sess->attempt = 0;
+        
+        ne_set_request_private(req, sess->spec->id, areq);
+    }
 }
 
 
-static void ah_pre_send(void *cookie, ne_buffer *request)
+static void ah_pre_send(ne_request *r, void *cookie, ne_buffer *request)
 {
-    struct auth_request *req = cookie;
-    auth_session *sess = req->session;
-
+    auth_session *sess = cookie;
+    struct auth_request *req = ne_get_request_private(r, sess->spec->id);
 
-    if (!sess->can_handle) {
+    if (!sess->can_handle || !req) {
        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;
 
@@ -974,50 +1053,56 @@ static void ah_pre_send(void *cookie, ne_buffer *request)
        case auth_scheme_digest:
            value = request_digest(sess, req);
            break;
+#ifdef HAVE_GSSAPI         
+       case auth_scheme_gssapi:
+           value = request_gssapi(sess);
+           break;
+#endif
        default:
            value = NULL;
            break;
        }
 
        if (value != NULL) {
-           ne_buffer_concat(request, sess->req_hdr, ": ", value, NULL);
-           free(value);
+           ne_buffer_concat(request, sess->spec->req_hdr, ": ", value, NULL);
+           ne_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)
+static int ah_post_send(ne_request *req, void *cookie, const ne_status *status)
 {
-    struct auth_request *areq = cookie;
-    auth_session *sess = areq->session;
+    auth_session *sess = cookie;
+    struct auth_request *areq = ne_get_request_private(req, sess->spec->id);
     int ret = NE_OK;
 
+    if (!areq) return 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));
+            "ah_post_send (#%d), code is %d (want %d), %s is %s\n",
+            sess->attempt, status->code, sess->spec->status_code, 
+            sess->spec->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);
+       ne_set_error(sess->sess, "%s", _(sess->spec->fail_msg));
        ret = NE_ERROR;
-    } else if (status->code == sess->status_code && 
-              areq->auth_hdr != NULL && areq->tries == 0) {
+    } else if ((status->code == sess->spec->status_code ||
+                (status->code == 401 && sess->context == AUTH_CONNECT)) &&
+              areq->auth_hdr != NULL) {
+        /* note above: allow a 401 in response to a CONNECT request
+         * from a proxy since some buggy proxies send that. */
        NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge with code %d.\n", status->code);
        if (!auth_challenge(sess, areq->auth_hdr)) {
            ret = NE_RETRY;
+       } else {
+           clean_session(sess);
+           ret = sess->spec->fail_code;
        }
-    } 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);
@@ -1026,70 +1111,63 @@ static int ah_post_send(void *cookie, const ne_status *status)
     return ret;
 }
 
-static void ah_destroy(void *cookie)
+static void ah_destroy(ne_request *req, void *session)
 {
-    free(cookie);
+    auth_session *sess = session;
+    struct auth_request *areq = ne_get_request_private(req, sess->spec->id);
+    if (areq) ne_free(areq);
 }
 
-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);
+    ne_free(sess);
 }
 
-void ne_set_server_auth(ne_session *sess, ne_request_auth callback, 
-                         void *userdata)
+static void auth_register(ne_session *sess, int isproxy,
+                         const struct auth_class *ahc, const char *id, 
+                         ne_auth_creds creds, void *userdata) 
 {
-    auth_session *auth_sess = auth_create(sess, callback, userdata);
+    auth_session *ahs = ne_calloc(sizeof *ahs);
 
-    /* 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.");
+    ahs->creds = creds;
+    ahs->userdata = userdata;
+    ahs->sess = sess;
+    ahs->spec = ahc;
 
-    ne_add_hooks(sess, &ah_server_hooks, auth_sess, free_auth);
+    if (strcmp(ne_get_scheme(sess), "https") == 0)
+        ahs->context = isproxy ? AUTH_CONNECT : AUTH_NOTCONNECT;
+    else
+        ahs->context = AUTH_ANY;
+    
+    /* Register hooks */
+    ne_hook_create_request(sess, ah_create, ahs);
+    ne_hook_pre_send(sess, ah_pre_send, ahs);
+    ne_hook_post_send(sess, ah_post_send, ahs);
+    ne_hook_destroy_request(sess, ah_destroy, ahs);
+    ne_hook_destroy_session(sess, free_auth, ahs);
+
+    ne_set_session_private(sess, id, ahs);
 }
 
-void ne_set_proxy_auth(ne_session *sess, ne_request_auth callback, 
-                        void *userdata)
+void ne_set_server_auth(ne_session *sess, ne_auth_creds creds, 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.");
+    auth_register(sess, 0, &ah_server_class, HOOK_SERVER_ID, creds, userdata);
+}
 
-    ne_add_hooks(sess, &ah_proxy_hooks, auth_sess, free_auth);
+void ne_set_proxy_auth(ne_session *sess, ne_auth_creds creds, void *userdata)
+{
+    auth_register(sess, 1, &ah_proxy_class, HOOK_PROXY_ID, creds, userdata);
 }
 
 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));
+    auth_session *as;
+    if ((as = ne_get_session_private(sess, HOOK_SERVER_ID)) != NULL)
+       clean_session(as);
+    if ((as = ne_get_session_private(sess, HOOK_PROXY_ID)) != NULL)
+       clean_session(as);
 }
 
index 12be1ef..fe1370c 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP authentication routines
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2002, 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
 #ifndef NE_AUTH_H
 #define NE_AUTH_H
 
-#include "ne_session.h" /* for http_session */
+#include "ne_session.h" /* for ne_session */
 
 BEGIN_NEON_DECLS
 
+/* Size of username/password buffers passed to ne_auth_creds
+ * callback. */
+#define NE_ABUFSIZ (256)
+
 /* 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);
+ * realm. The username and password must be copied into the buffers
+ * which are both of size NE_ABUFSIZ.  The 'attempt' parameter is zero
+ * on the first call to the callback, and increases by one each time
+ * an attempt to authenticate fails.
+ *
+ * The callback must return zero to indicate that authentication
+ * should be attempted with the username/password, or non-zero to
+ * cancel the request. (if non-zero, username and password are
+ * ignored.)  */
+typedef int (*ne_auth_creds)(void *userdata, const char *realm, int attempt,
+                            char *username, char *password);
+
+/* TOP TIP: if you just wish to try authenticating once (even if the
+ * user gets the username/password wrong), have your implementation of
+ * the callback return the 'attempt' value.  */
+
+/* Set callbacks to provide credentials for server and proxy
+ * authentication.  userdata is passed as the first argument to the
+ * callback.  The callback is called *indefinitely* until either it
+ * returns non-zero, or authentication is successful.  */
+void ne_set_server_auth(ne_session *sess, ne_auth_creds creds, void *userdata);
+void ne_set_proxy_auth(ne_session *sess, ne_auth_creds creds, void *userdata);
 
 /* Clear any stored authentication details for the given session. */
 void ne_forget_auth(ne_session *sess);
index 22ff2ee..d3b4eb3 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Basic HTTP and WebDAV methods
-   Copyright (C) 1999-2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 1999-2003, 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
@@ -130,7 +130,7 @@ ne_put_if_unmodified(ne_session *sess, const char *uri, int fd,
     date = ne_rfc1123_date(since);
     /* Add in the conditionals */
     ne_add_request_header(req, "If-Unmodified-Since", date);
-    free(date);
+    ne_free(date);
     
 #ifdef USE_DAV_LOCKS
     ne_lock_using_resource(req, uri, 0);
@@ -157,33 +157,12 @@ ne_put_if_unmodified(ne_session *sess, const char *uri, int fd,
 
 struct get_context {
     int error;
-    size_t total, progress;
+    ne_session *session;
+    off_t total;
     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;
@@ -193,16 +172,21 @@ static void get_to_fd(void *userdata, const char *block, size_t length)
        while (length > 0) {
            ret = write(ctx->fd, block, length);
            if (ret < 0) {
-               ctx->error = errno;
+               char err[200];
+               ctx->error = 1;
+               ne_strerror(errno, err, sizeof err);
+               ne_set_error(ctx->session, _("Could not write to file: %s"),
+                            err);
                break;
            } else {
                length -= ret;
+               block += ret;
            }
        }
     }
 }
 
-static int accept_206(void *ud, ne_request *req, ne_status *st)
+static int accept_206(void *ud, ne_request *req, const ne_status *st)
 {
     return (st->code == 206);
 }
@@ -216,10 +200,12 @@ static void clength_hdr_handler(void *ud, const char *value)
        ctx->range->end = ctx->range->start + len - 1;
        ctx->range->total = len;
     }
-    else if (len != ctx->range->total) {
+    else if (len != ctx->total) {
        NE_DEBUG(NE_DBG_HTTP, 
-                "Expecting %ld bytes, got entity of length %ld\n", 
-                (long int) ctx->range->total, (long int) len);
+                "Expecting %" NE_FMT_OFF_T " bytes, "
+                "got entity of length %" NE_FMT_OFF_T "\n", 
+                ctx->total, len);
+       ne_set_error(ctx->session, _("Response not of expected length"));
        ctx->error = 1;
     }
 }
@@ -229,6 +215,7 @@ static void content_range_hdr_handler(void *ud, const char *value)
     struct get_context *ctx = ud;
 
     if (strncmp(value, "bytes ", 6) != 0) {
+       ne_set_error(ctx->session, ("Response range using unrecognized unit"));
        ctx->error = 1;
     }
 
@@ -240,6 +227,7 @@ int ne_get_range(ne_session *sess, const char *uri,
 {
     ne_request *req = ne_request_create(sess, "GET", uri);
     struct get_context ctx;
+    const ne_status *status;
     int ret;
 
     if (range->end == -1) {
@@ -249,9 +237,12 @@ int ne_get_range(ne_session *sess, const char *uri,
        ctx.total = (range->end - range->start) + 1;
     }
 
+    NE_DEBUG(NE_DBG_HTTP, "Range total: %" NE_FMT_OFF_T "\n", ctx.total);
+
     ctx.fd = fd;
     ctx.error = 0;
     ctx.range = range;
+    ctx.session = sess;
 
     ne_add_response_header_handler(req, "Content-Length",
                                     clength_hdr_handler, &ctx);
@@ -261,28 +252,38 @@ int ne_get_range(ne_session *sess, const char *uri,
 
     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);
+       ne_print_request_header(req, "Range", "bytes=%" NE_FMT_OFF_T "-", 
+                               range->start);
     }
     else {
-       ne_print_request_header(req, "Range", "bytes=%ld-%ld",
-                                 (long int) range->start, 
-                                 (long int)range->end);
+       ne_print_request_header(req, "Range", 
+                               "bytes=%" NE_FMT_OFF_T "-%" NE_FMT_OFF_T,
+                               range->start, 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) {
+
+    status = ne_get_status(req);
+
+    if (ctx.error) {
        ret = NE_ERROR;
-    }
-    else if (ne_get_status(req)->code != 206) {
-       ne_set_error(sess, _("Server does not allow partial GETs."));
+    } else if (status && status->code == 416) {
+       /* connection is terminated too early with Apache/1.3, so we check
+        * this even if ret == NE_ERROR... */
+       ne_set_error(sess, _("Range is not satisfiable"));
        ret = NE_ERROR;
     }
+    else if (ret == NE_OK) {
+       if (status->klass == 2 && status->code != 206) {
+           ne_set_error(sess, _("Resource does not support ranged GETs."));
+           ret = NE_ERROR;
+       }
+       else if (status->klass != 2) {
+           ret = NE_ERROR;
+       }
+    } 
     
     ne_request_destroy(req);
 
@@ -298,9 +299,9 @@ int ne_get(ne_session *sess, const char *uri, int fd)
     int ret;
 
     ctx.total = -1;
-    ctx.progress = 0;
     ctx.fd = fd;
     ctx.error = 0;
+    ctx.session = sess;
 
     /* Read the value of the Content-Length header into ctx.total */
     ne_add_response_header_handler(req, "Content-Length",
@@ -312,14 +313,8 @@ int ne_get(ne_session *sess, const char *uri, int fd)
     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) {
+    } else if (ret == NE_OK && ne_get_status(req)->klass != 2) {
        ret = NE_ERROR;
     }
 
@@ -339,6 +334,7 @@ int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer)
     ctx.total = -1;
     ctx.fd = fd;
     ctx.error = 0;
+    ctx.session = sess;
 
     /* Read the value of the Content-Length header into ctx.total */
     ne_add_response_header_handler(req, "Content-Length",
@@ -351,14 +347,9 @@ int ne_post(ne_session *sess, const char *uri, int fd, const char *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) {
+    else if (ret == NE_OK && ne_get_status(req)->klass != 2) {
        ret = NE_ERROR;
     }
 
@@ -367,74 +358,70 @@ int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer)
     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;
+    char *sep, *stype;
 
     ct->value = ne_strdup(value);
     
-    sep = strchr(ct->value, '/');
-    if (!sep) {
+    stype = strchr(ct->value, '/');
+    if (!stype) {
        NE_FREE(ct->value);
        return;
     }
 
-    *++sep = '\0';
+    *stype++ = '\0';
     ct->type = ct->value;
-    ct->subtype = sep;
     
-    parms = strchr(ct->value, ';');
-
-    if (parms) {
-       *parms = '\0';
-       /* TODO: handle charset. */
+    sep = strchr(stype, ';');
+
+    if (sep) {
+       char *tok;
+       /* look for the charset parameter. TODO; probably better to
+        * hand-carve a parser than use ne_token/strstr/shave here. */
+       *sep++ = '\0';
+       do {
+           tok = ne_qtoken(&sep, ';', "\"\'");
+           if (tok) {
+               tok = strstr(tok, "charset=");
+               if (tok)
+                   ct->charset = ne_shave(tok+8, "\"\'");
+           } else {
+               break;
+           }
+       } while (sep != NULL);
     }
+
+    /* set subtype, losing any trailing whitespace */
+    ct->subtype = ne_shave(stype, " \t");
+    
+    /* 2616#3.7.1: subtypes of text/ default to charset ISO-8859-1. */
+    if (ct->charset == NULL && strcasecmp(ct->type, "text") == 0)
+       ct->charset = "ISO-8859-1";
 }
 
 static void dav_hdr_handler(void *userdata, const char *value)
 {
-    char **classes, **class;
+    char *tokens = ne_strdup(value), *pnt = tokens;
     ne_server_capabilities *caps = userdata;
     
-    classes = split_string(value, ',', "\"'", " \r\t\n");
-    for (class = classes; *class!=NULL; class++) {
+    do {
+       char *tok = ne_qtoken(&pnt, ',',  "\"'");
+       if (!tok) break;
+       
+       tok = ne_shave(tok, " \r\t\n");
 
-       if (strcmp(*class, "1") == 0) {
+       if (strcmp(tok, "1") == 0) {
            caps->dav_class1 = 1;
-       } else if (strcmp(*class, "2") == 0) {
+       } else if (strcmp(tok, "2") == 0) {
            caps->dav_class2 = 1;
-       } else if (strcmp(*class, "<http://apache.org/dav/propset/fs/1>") == 0) {
+       } else if (strcmp(tok, "<http://apache.org/dav/propset/fs/1>") == 0) {
            caps->dav_executable = 1;
        }
-    }
+    } while (pnt != NULL);
     
-    split_string_free(classes);
+    ne_free(tokens);
 
 }
 
@@ -445,7 +432,6 @@ int ne_options(ne_session *sess, const char *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);
@@ -479,10 +465,15 @@ void ne_add_depth_header(ne_request *req, int depth)
 }
 
 static int copy_or_move(ne_session *sess, int is_move, int overwrite,
-                       const char *src, const char *dest 
+                       int depth, const char *src, const char *dest
 {
     ne_request *req = ne_request_create( sess, is_move?"MOVE":"COPY", src );
 
+    /* 2518 S8.9.2 says only use Depth: infinity with MOVE. */
+    if (!is_move) {
+       ne_add_depth_header(req, depth);
+    }
+
 #ifdef USE_DAV_LOCKS
     if (is_move) {
        ne_lock_using_resource(req, src, NE_DEPTH_INFINITE);
@@ -501,16 +492,16 @@ static int copy_or_move(ne_session *sess, int is_move, int overwrite,
     return ne_simple_request(sess, req);
 }
 
-int ne_copy(ne_session *sess, int overwrite, 
+int ne_copy(ne_session *sess, int overwrite, int depth,
             const char *src, const char *dest) 
 {
-    return copy_or_move(sess, 0, overwrite, src, dest);
+    return copy_or_move(sess, 0, overwrite, depth, 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);
+    return copy_or_move(sess, 1, overwrite, 0, src, dest);
 }
 
 /* Deletes the specified resource. (and in only two lines of code!) */
@@ -540,10 +531,10 @@ int ne_mkcol(ne_session *sess, const char *uri)
     char *real_uri;
     int ret;
 
-    if (uri_has_trailing_slash(uri)) {
+    if (ne_path_has_trailing_slash(uri)) {
        real_uri = ne_strdup(uri);
     } else {
-       CONCAT2(real_uri, uri, "/");
+       real_uri = ne_concat(uri, "/", NULL);
     }
 
     req = ne_request_create(sess, "MKCOL", real_uri);
@@ -555,7 +546,7 @@ int ne_mkcol(ne_session *sess, const char *uri)
     
     ret = ne_simple_request(sess, req);
 
-    free(real_uri);
+    ne_free(real_uri);
 
     return ret;
 }
index 6478fb9..f35dc9e 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Handling of compressed HTTP responses
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2004, 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
 #include <stdlib.h>
 #endif
 
-#include <assert.h>
-
 #include "ne_request.h"
 #include "ne_compress.h"
 #include "ne_utils.h"
+#include "ne_i18n.h"
+
+#include "ne_private.h"
+
+#ifdef NEON_ZLIB
 
 #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. */
+ * implements DEFLATE: we have to unwrap the gzip format (specified in
+ * RFC1952) 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;
+    int zstrinit; /* non-zero if zstr has been initialized */
     char *enchdr; /* value of Content-Enconding response header. */
 
     /* pass blocks back to this. */
     ne_block_reader reader;
+    ne_accept_response acceptor;
     void *userdata;
 
     /* buffer for gzip header bytes. */
@@ -60,24 +66,31 @@ struct ne_decompress_s {
            unsigned char id2;
            unsigned char cmeth; /* compression method. */
            unsigned char flags;
-           unsigned int mtime;
+           unsigned int mtime; /* breaks when sizeof int != 4 */
            unsigned char xflags;
            unsigned char os;
        } hdr;
     } in;
     size_t incount;    /* bytes in in.buf */
+    
+    unsigned char footer[8];
+    size_t footcount; /* bytes in footer. */
+
+    /* CRC32 checksum: odd that zlib uses uLong for this since it is a
+     * 64-bit integer on LP64 platforms. */
+    uLong checksum;
 
     /* 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. */
+       NE_Z_BEFORE_DATA, /* not received any response blocks yet. */
+       NE_Z_PASSTHROUGH, /* response not compressed: passing through. */
+       NE_Z_IN_HEADER, /* received a few bytes of response data, but not
+                        * got past the gzip header yet. */
+       NE_Z_POST_HEADER, /* waiting for the end of the NUL-terminated bits. */
+       NE_Z_INFLATING, /* inflating response bytes. */
+       NE_Z_AFTER_DATA, /* after data; reading CRC32 & ISIZE */
+       NE_Z_FINISHED, /* stream is finished. */
+       NE_Z_ERROR /* inflate bombed. */
     } state;
 };
 
@@ -102,7 +115,7 @@ static int parse_header(ne_decompress *ctx)
            h->id1, h->id2, h->cmeth, h->flags);
     
     if (h->id1 != ID1 || h->id2 != ID2 || h->cmeth != 8) {
-       ctx->state = ERROR;
+       ctx->state = NE_Z_ERROR;
        ne_set_error(ctx->session, "Compressed stream invalid");
        return HDR_ERROR;
     }
@@ -115,50 +128,86 @@ static int parse_header(ne_decompress *ctx)
      * skip as many fields as bits set (bailing if any reserved bits
      * are set. */
     if (h->flags == 8) {
-       ctx->state = POST_HEADER;
+       ctx->state = NE_Z_POST_HEADER;
        return HDR_EXTENDED;
     } else if (h->flags != 0) {
-       ctx->state = ERROR;
+       ctx->state = NE_Z_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;
+    ctx->state = NE_Z_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;
+/* Convert 'buf' to unsigned int; 'buf' must be 'unsigned char *' */
+#define BUF2UINT(buf) ((buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0])
 
-    while ((tok = ne_token(&pnt, ',', NULL)) != NULL)
-    {
-       if (strcasecmp(tok, wanted) == 0) {
-           ret = 1;
-           break;
+/* Process extra 'len' bytes of 'buf' which were received after the
+ * DEFLATE data. */
+static void process_footer(ne_decompress *ctx, 
+                          const unsigned char *buf, size_t len)
+{
+    if (len + ctx->footcount > 8) {
+        ne_set_error(ctx->session, 
+                     "Too many bytes (%" NE_FMT_SIZE_T ") in gzip footer",
+                     len);
+       ctx->state = NE_Z_ERROR;
+    } else {
+       memcpy(ctx->footer + ctx->footcount, buf, len);
+       ctx->footcount += len;
+       if (ctx->footcount == 8) {
+           uLong crc = BUF2UINT(ctx->footer) & 0xFFFFFFFF;
+           if (crc == ctx->checksum) {
+               ctx->state = NE_Z_FINISHED;
+                /* reader requires a size=0 call at end-of-response */
+                ctx->reader(ctx->userdata, NULL, 0);
+               NE_DEBUG(NE_DBG_HTTP, "compress: Checksum match.\n");
+           } else {
+               NE_DEBUG(NE_DBG_HTTP, "compress: Checksum mismatch: "
+                        "given %lu vs computed %lu\n", crc, ctx->checksum);
+               ne_set_error(ctx->session, 
+                            "Checksum invalid for compressed stream");
+               ctx->state = NE_Z_ERROR;
+           }
        }
     }
+}
 
-    free(buf);
-    
-    return ret;
+/* A zlib function failed with 'code'; set the session error string
+ * appropriately. */
+static void set_zlib_error(ne_decompress *ctx, const char *msg, int code)
+{
+    if (ctx->zstr.msg)
+        ne_set_error(ctx->session, _("%s: %s"), msg, ctx->zstr.msg);
+    else {
+        const char *err;
+        switch (code) {
+        case Z_STREAM_ERROR: err = "stream error"; break;
+        case Z_DATA_ERROR: err = "data corrupt"; break;
+        case Z_MEM_ERROR: err = "out of memory"; break;
+        case Z_BUF_ERROR: err = "buffer error"; break;
+        case Z_VERSION_ERROR: err = "library version mismatch"; break;
+        default: err = "unknown error"; break;
+        }
+        ne_set_error(ctx->session, _("%s: %s (code %d)"), msg, err, code);
+    }
 }
 
-/* inflate it baby. */
+/* Inflate response buffer 'buf' of length 'len'. */
 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.next_in = (unsigned char *)buf;
     ctx->zstr.total_in = 0;
     
     do {
-       ctx->zstr.avail_out = BUFSIZ;
-       ctx->zstr.next_out = ctx->outbuf;
+       ctx->zstr.avail_out = sizeof ctx->outbuf;
+       ctx->zstr.next_out = (unsigned char *)ctx->outbuf;
        ctx->zstr.total_out = 0;
        
        ret = inflate(&ctx->zstr, Z_NO_FLUSH);
@@ -172,23 +221,29 @@ static void do_inflate(ne_decompress *ctx, const char *buf, size_t len)
                 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);
+       /* update checksum. */
+       ctx->checksum = crc32(ctx->checksum, (unsigned char *)ctx->outbuf, 
+                             ctx->zstr.total_out);
+
+       /* pass on the inflated data, if any */
+        if (ctx->zstr.total_out > 0) {
+            ctx->reader(ctx->userdata, ctx->outbuf, ctx->zstr.total_out);
+        }      
+    } while (ret == Z_OK && ctx->zstr.avail_in > 0);
     
     if (ret == Z_STREAM_END) {
-       NE_DEBUG(NE_DBG_HTTP, "compress: end of stream.\n");
-       ctx->state = FINISHED;
+       NE_DEBUG(NE_DBG_HTTP, "compress: end of data stream, remaining %d.\n",
+                ctx->zstr.avail_in);
+       /* process the footer. */
+       ctx->state = NE_Z_AFTER_DATA;
+       process_footer(ctx, ctx->zstr.next_in, ctx->zstr.avail_in);
     } 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);
+       ctx->state = NE_Z_ERROR;
+        set_zlib_error(ctx, _("Could not inflate data"), ret);
     }
 }
 
+/* Callback which is passed blocks of the response body. */
 static void gz_reader(void *ud, const char *buf, size_t len)
 {
     ne_decompress *ctx = ud;
@@ -196,49 +251,53 @@ static void gz_reader(void *ud, const char *buf, size_t len)
     size_t count;
 
     switch (ctx->state) {
-    case PASSTHROUGH:
+    case NE_Z_PASSTHROUGH:
        /* move along there. */
        ctx->reader(ctx->userdata, buf, len);
        return;
 
-    case ERROR:
+    case NE_Z_ERROR:
        /* beyond hope. */
-       return;
+       break;
 
-    case FINISHED:
-       NE_DEBUG(NE_DBG_HTTP, 
-                "compress: %d bytes in after end of stream.\n", len);
-       return;
+    case NE_Z_FINISHED:
+       /* Could argue for tolerance, and ignoring trailing content;
+        * but it could mean something more serious. */
+       if (len > 0) {
+           ctx->state = NE_Z_ERROR;
+           ne_set_error(ctx->session,
+                        "Unexpected content received after compressed stream");
+       }
+       break;
 
-    case BEFORE_DATA:
+    case NE_Z_BEFORE_DATA:
        /* work out whether this is a compressed response or not. */
-       if (ctx->enchdr != NULL && find_token(ctx->enchdr, "gzip")) {
+       if (ctx->enchdr && strcasecmp(ctx->enchdr, "gzip") == 0) {
+            int ret;
            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;
-           }
+            /* inflateInit2() works here where inflateInit() doesn't. */
+            ret = inflateInit2(&ctx->zstr, -MAX_WBITS);
+            if (ret != Z_OK) {
+                set_zlib_error(ctx, _("Could not initialize zlib"), ret);
+                return;
+            }
+           ctx->zstrinit = 1;
 
        } 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->state = NE_Z_PASSTHROUGH;
            ctx->reader(ctx->userdata, buf, len);
            return;
        }
 
-       ctx->state = IN_HEADER;
+       ctx->state = NE_Z_IN_HEADER;
        /* FALLTHROUGH */
 
-    case IN_HEADER:
+    case NE_Z_IN_HEADER:
        /* copy as many bytes as possible into the buffer. */
        if (len + ctx->incount > 10) {
            count = 10 - ctx->incount;
@@ -256,8 +315,6 @@ static void gz_reader(void *ud, const char *buf, size_t len)
        len -= count;
 
        switch (parse_header(ctx)) {
-       case HDR_ERROR:
-           return;
        case HDR_EXTENDED:
            if (len == 0)
                return;
@@ -266,12 +323,13 @@ static void gz_reader(void *ud, const char *buf, size_t len)
            if (len > 0) {
                do_inflate(ctx, buf, len);
            }
+        default:
            return;
        }
 
        /* FALLTHROUGH */
 
-    case POST_HEADER:
+    case NE_Z_POST_HEADER:
        /* eating the filename string. */
        zbuf = memchr(buf, '\0', len);
        if (zbuf == NULL) {
@@ -279,12 +337,13 @@ static void gz_reader(void *ud, const char *buf, size_t len)
            return;
        }
 
-       NE_DEBUG(NE_DBG_HTTP, "compresss: skipped %d header bytes.\n", 
+       NE_DEBUG(NE_DBG_HTTP,
+                "compresss: skipped %" NE_FMT_SIZE_T " header bytes.\n", 
                 zbuf - buf);
        /* found end of string. */
        len -= (1 + zbuf - buf);
        buf = zbuf + 1;
-       ctx->state = INFLATING;
+       ctx->state = NE_Z_INFLATING;
        if (len == 0) {
            /* end of string was at end of buffer. */
            return;
@@ -292,23 +351,80 @@ static void gz_reader(void *ud, const char *buf, size_t len)
 
        /* FALLTHROUGH */
 
-    case INFLATING:
+    case NE_Z_INFLATING:
        do_inflate(ctx, buf, len);
        break;
-    }    
-    
+
+    case NE_Z_AFTER_DATA:
+       process_footer(ctx, (unsigned char *)buf, len);
+       break;
+    }
+
 }
 
 int ne_decompress_destroy(ne_decompress *ctx)
 {
-    if (ctx->state != PASSTHROUGH && ctx->state != BEFORE_DATA) {
-       /* stream must have been initialized */
+    int ret;
+
+    if (ctx->zstrinit)
+       /* inflateEnd only fails if it's passed NULL etc; ignore
+        * return value. */
        inflateEnd(&ctx->zstr);
-    }
+
     if (ctx->enchdr)
-       free(ctx->enchdr);
-    free(ctx);
-    return (ctx->state == ERROR);
+       ne_free(ctx->enchdr);
+
+    switch (ctx->state) {
+    case NE_Z_BEFORE_DATA:
+    case NE_Z_PASSTHROUGH:
+    case NE_Z_FINISHED:
+       ret = NE_OK;
+       break;
+    case NE_Z_ERROR:
+       /* session error already set. */
+       ret = NE_ERROR;
+       break;
+    default:
+       /* truncated response. */
+       ne_set_error(ctx->session, "Compressed response was truncated");
+       ret = NE_ERROR;
+       break;
+    }
+
+    ne_free(ctx);
+    return ret;
+}
+
+/* Prepare for a compressed response */
+static void gz_pre_send(ne_request *r, void *ud, ne_buffer *req)
+{
+    ne_decompress *ctx = ud;
+
+    NE_DEBUG(NE_DBG_HTTP, "compress: Initialization.\n");
+
+    /* (Re-)Initialize the context */
+    ctx->state = NE_Z_BEFORE_DATA;
+    if (ctx->zstrinit) inflateEnd(&ctx->zstr);
+    ctx->zstrinit = 0;
+    ctx->incount = ctx->footcount = 0;
+    ctx->checksum = crc32(0L, Z_NULL, 0);
+    if (ctx->enchdr) {
+        ne_free(ctx->enchdr);
+        ctx->enchdr = NULL;
+    }
+}
+
+/* Kill the pre-send hook */
+static void gz_destroy(ne_request *req, void *userdata)
+{
+    ne_kill_pre_send(ne_get_session(req), gz_pre_send, userdata);
+}
+
+/* Wrapper for user-passed acceptor function. */
+static int gz_acceptor(void *userdata, ne_request *req, const ne_status *st)
+{
+    ne_decompress *ctx = userdata;
+    return ctx->acceptor(ctx->userdata, req, st);
 }
 
 ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt,
@@ -321,12 +437,34 @@ ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt,
     ne_add_response_header_handler(req, "Content-Encoding", 
                                   ne_duplicate_header, &ctx->enchdr);
 
-    ne_add_response_body_reader(req, acpt, gz_reader, ctx);
+    ne_add_response_body_reader(req, gz_acceptor, gz_reader, ctx);
 
-    ctx->state = BEFORE_DATA;
     ctx->reader = rdr;
     ctx->userdata = userdata;
     ctx->session = ne_get_session(req);
+    ctx->acceptor = acpt;
+
+    ne_hook_pre_send(ctx->session, gz_pre_send, ctx);
+    ne_hook_destroy_request(ctx->session, gz_destroy, ctx);
 
     return ctx;    
 }
+
+#else /* !NEON_ZLIB */
+
+/* Pass-through interface present to provide ABI compatibility. */
+
+ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt,
+                                   ne_block_reader rdr, void *userdata)
+{
+    ne_add_response_body_reader(req, acpt, rdr, userdata);
+    /* an arbitrary return value: don't confuse them by returning NULL. */
+    return (ne_decompress *)req;
+}
+
+int ne_decompress_destroy(ne_decompress *dc)
+{
+    return 0;
+}
+
+#endif /* NEON_ZLIB */
index 4d99ef6..64b9311 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Basic cookie support for neon
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
 #include "ne_string.h"
 #include "ne_alloc.h"
 
+#define COOKIE_ID "http://www.webdav.org/neon/hooks/cookies"
+
 static void set_cookie_hdl(void *userdata, const char *value)
 {
-    char **pairs = pair_string(value, ';', '=', HTTP_QUOTES, HTTP_WHITESPACE);
+    char **pairs = pair_string(value, ';', '=', "\"'", " \r\n\t");
     ne_cookie *cook;
     ne_cookie_cache *cache = userdata;
     int n;
@@ -66,29 +68,28 @@ static void set_cookie_hdl(void *userdata, const char *value)
     
     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 = ne_malloc(sizeof *cook);
+       memset(cook, 0, sizeof *cook);
+       cook->name = ne_strdup(ne_shave(pairs[0], " \t\r\n"));
        cook->next = cache->cookies;
        cache->cookies = cook;
     } else {
        /* Free the old value */
-       free(cook->value);
+       ne_free(cook->value);
     }
 
-    cook->value = pairs[1];
+    cook->value = ne_strdup(ne_shave(pairs[1], " \t\r\n"));
 
     for (n = 2; pairs[n] != NULL; n+=2) {
+        if (!pairs[n+1]) continue;
        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;
+           cook->path = ne_strdup(pairs[n+1]);
        } 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;
+           cook->domain = ne_strdup(pairs[n+1]);
        }
     }
 
@@ -97,17 +98,17 @@ static void set_cookie_hdl(void *userdata, const char *value)
     pair_string_free(pairs);
 }
 
-static void *create(void *session, ne_request *req, const char *method, const char *uri)
+static void create(ne_request *req, void *session,
+                  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)
+static void pre_send(ne_request *req, void *session, ne_buffer *request)
 {
-    ne_cookie_cache *cache = private;
+    ne_cookie_cache *cache = session;
     ne_cookie *cook;
     
     if (cache->cookies == NULL) {
@@ -123,20 +124,13 @@ static void pre_send(void *private, ne_buffer *request)
        }
     }
     
-    ne_buffer_zappend(request, EOL);
-    
+    ne_buffer_zappend(request, "\r\n");
 }
 
-static void destroy(void *private)
+/* Register cookie handling for given session using given cache. */
+void ne_cookie_register(ne_session *sess, ne_cookie_cache *cache)
 {
-    /* FIXME */
-    return;
+    ne_hook_create_request(sess, create, cache);
+    ne_hook_pre_send(sess, pre_send, cache);
 }
 
-ne_request_hooks ne_cookie_hooks = {
-    "http://www.webdav.org/neon/hooks/cookies",
-    create,
-    pre_send,
-    NULL,
-    destroy
-};
index 34f3539..27c3517 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Date manipulation routines
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
@@ -19,9 +19,7 @@
 
 */
 
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include <sys/types.h>
 
 #include <string.h>
 #endif
 
-#ifdef HAVE_SNPRINTF_H
-#include "snprintf.h"
-#endif
-
 #include "ne_alloc.h"
 #include "ne_dates.h"
+#include "ne_utils.h"
 
 /* Generic date manipulation routines. */
 
+/* ISO8601: 2001-01-01T12:30:00Z */
+#define ISO8601_FORMAT_Z "%04d-%02d-%02dT%02d:%02d:%lfZ"
+#define ISO8601_FORMAT_M "%04d-%02d-%02dT%02d:%02d:%lf-%02d:%02d"
+#define ISO8601_FORMAT_P "%04d-%02d-%02dT%02d:%02d:%lf+%02d:%02d"
+
 /* 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"
+#define RFC1036_FORMAT "%10s %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"
 
@@ -59,6 +59,13 @@ static const char *short_months[12] = {
     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 };
 
+#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
+#define GMTOFF(t) ((t).tm_gmtoff)
+#else
+/* FIXME: work out the offset anyway. */
+#define GMTOFF(t) (0)
+#endif
+
 /* 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) {
@@ -69,20 +76,64 @@ char *ne_rfc1123_date(time_t anytime) {
        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);
+    ne_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 ISO-8601-formatted date string and returns the time_t.
+ * Returns (time_t)-1 if the parse fails. */
+time_t ne_iso8601_parse(const char *date) 
+{
+    struct tm gmt = {0};
+    int off_hour, off_min;
+    double sec;
+    off_t fix;
+    int n;
+
+    /*  it goes: ISO8601: 2001-01-01T12:30:00+03:30 */
+    if ((n = sscanf(date, ISO8601_FORMAT_P,
+                   &gmt.tm_year, &gmt.tm_mon, &gmt.tm_mday,
+                   &gmt.tm_hour, &gmt.tm_min, &sec,
+                   &off_hour, &off_min)) == 8) {
+      gmt.tm_sec = (int)sec;
+      fix = - off_hour * 3600 - off_min * 60;
+    }
+    /*  it goes: ISO8601: 2001-01-01T12:30:00-03:30 */
+    else if ((n = sscanf(date, ISO8601_FORMAT_M,
+                        &gmt.tm_year, &gmt.tm_mon, &gmt.tm_mday,
+                        &gmt.tm_hour, &gmt.tm_min, &sec,
+                        &off_hour, &off_min)) == 8) {
+      gmt.tm_sec = (int)sec;
+      fix = off_hour * 3600 + off_min * 60;
+    }
+    /*  it goes: ISO8601: 2001-01-01T12:30:00Z */
+    else if ((n = sscanf(date, ISO8601_FORMAT_Z,
+                        &gmt.tm_year, &gmt.tm_mon, &gmt.tm_mday,
+                        &gmt.tm_hour, &gmt.tm_min, &sec)) == 6) {
+      gmt.tm_sec = (int)sec;
+      fix = 0;
+    }
+    else {
+      return (time_t)-1;
+    }
+
+    gmt.tm_year -= 1900;
+    gmt.tm_isdst = -1;
+    gmt.tm_mon--;
+
+    return mktime(&gmt) + fix + GMTOFF(gmt);
+}
+
 /* 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];
+    char wkday[4], mon[4];
     int n;
 /*  it goes: Sun, 06 Nov 1994 08:49:37 GMT */
     n = sscanf(date, RFC1123_FORMAT,
@@ -97,7 +148,7 @@ time_t ne_rfc1123_parse(const char *date)
      * since the mktime will then fail */
     gmt.tm_mon = n;
     gmt.tm_isdst = -1;
-    return mktime(&gmt);
+    return mktime(&gmt) + GMTOFF(gmt);
 }
 
 /* Takes a string containing a RFC1036-style date and returns the time_t */
@@ -105,20 +156,29 @@ time_t ne_rfc1036_parse(const char *date)
 {
     struct tm gmt = {0};
     int n;
-    static char wkday[10], mon[4];
+    char wkday[11], 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);
+    if (n != 7) {
+       return (time_t)-1;
+    }
+
     /* 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 */
+
+    /* Defeat Y2K bug. */
+    if (gmt.tm_year < 50)
+       gmt.tm_year += 100;
+
     gmt.tm_mon = n;
     gmt.tm_isdst = -1;
-    return mktime(&gmt);
+    return mktime(&gmt) + GMTOFF(gmt);
 }
 
 
@@ -129,7 +189,7 @@ time_t ne_asctime_parse(const char *date)
 {
     struct tm gmt = {0};
     int n;
-    static char wkday[4], mon[4];
+    char wkday[4], mon[4];
     n = sscanf(date, ASCTIME_FORMAT,
                wkday, mon, &gmt.tm_mday, 
                &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec,
@@ -142,7 +202,7 @@ time_t ne_asctime_parse(const char *date)
      * since the mktime will then fail */
     gmt.tm_mon = n;
     gmt.tm_isdst = -1;
-    return mktime(&gmt);
+    return mktime(&gmt) + GMTOFF(gmt);
 }
 
 /* HTTP-date parser */
@@ -157,35 +217,3 @@ time_t ne_httpdate_parse(const char *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
-
-
index f029edf..4dacbda 100644 (file)
@@ -1,3 +1,23 @@
+/* 
+   Standard definitions for neon headers
+   Copyright (C) 2003, 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
+
+*/
 
 #undef BEGIN_NEON_DECLS
 #undef END_NEON_DECLS
@@ -8,3 +28,14 @@
 # define BEGIN_NEON_DECLS /* empty */
 # define END_NEON_DECLS /* empty */
 #endif
+
+/* define ssize_t for Win32 */
+#if defined(WIN32) && !defined(ssize_t)
+#define ssize_t int
+#endif
+
+#ifdef __GNUC__
+#define ne_attribute(x) __attribute__(x)
+#else
+#define ne_attribute(x)
+#endif
index a0df0ac..afbb3f9 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Internationalization of neon
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
index b6f4686..8863bd4 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    WebDAV Class 2 locking operations
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
 
 #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;
+/* A list of lock objects. */
+struct lock_list {
+    struct ne_lock *lock;
+    struct lock_list *next, *prev;
 };
 
-struct ne_lock_session_s {
-    struct ne_lock *locks;
+struct ne_lock_store_s {
+    struct lock_list *locks;
+    struct lock_list *cursor; /* current position in 'locks' */
 };
 
-/* Per-request lock structure */
-struct request_locks {
-    struct submit_locks *locks; /* for the If header */
-    ne_lock_session *session;
+struct lh_req_cookie {
+    const ne_lock_store *store;
+    struct lock_list *submit;
 };
 
 /* Context for PROPFIND/lockdiscovery callbacks */
 struct discover_ctx {
+    ne_session *session;
     ne_lock_result results;
     void *userdata;
+    ne_buffer *cdata;
 };
 
-/* 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
+/* Context for handling LOCK response */
+struct lock_ctx {
+    struct ne_lock active; /* activelock */
+    char *token; /* the token we're after. */
+    int found;
+    ne_buffer *cdata;
 };
 
-/* 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 }
+/* use the "application" state space. */
+#define ELM_LOCK_FIRST (NE_PROPS_STATE_TOP + 66)
+
+#define ELM_lockdiscovery (ELM_LOCK_FIRST)
+#define ELM_activelock (ELM_LOCK_FIRST + 1)
+#define ELM_lockscope (ELM_LOCK_FIRST + 2)
+#define ELM_locktype (ELM_LOCK_FIRST + 3)
+#define ELM_depth (ELM_LOCK_FIRST + 4)
+#define ELM_owner (ELM_LOCK_FIRST + 5)
+#define ELM_timeout (ELM_LOCK_FIRST + 6)
+#define ELM_locktoken (ELM_LOCK_FIRST + 7)
+#define ELM_lockinfo (ELM_LOCK_FIRST + 8)
+#define ELM_write (ELM_LOCK_FIRST + 9)
+#define ELM_exclusive (ELM_LOCK_FIRST + 10)
+#define ELM_shared (ELM_LOCK_FIRST + 11)
+#define ELM_href (ELM_LOCK_FIRST + 12)
+#define ELM_prop (NE_207_STATE_PROP)
+
+static const struct ne_xml_idmap element_map[] = {
+#define ELM(x) { "DAV:", #x, ELM_ ## x }
+    ELM(lockdiscovery), ELM(activelock), ELM(prop), ELM(lockscope),
+    ELM(locktype), ELM(depth), ELM(owner), ELM(timeout), ELM(locktoken),
+    ELM(lockinfo), ELM(lockscope), ELM(locktype), ELM(write), ELM(exclusive),
+    ELM(shared), ELM(href)
+    /* no "lockentry" */
+#undef ELM
 };
 
 static const ne_propname lock_props[] = {
@@ -128,237 +113,288 @@ static const ne_propname lock_props[] = {
     { NULL }
 };
 
-static void *create(void *session, ne_request *req, 
-                   const char *method, const char *uri)
+/* this simply registers the accessor for the function. */
+static void lk_create(ne_request *req, void *session, 
+                      const char *method, const char *uri)
 {
-    struct request_locks *rl = ne_calloc(sizeof *rl);
-    rl->session = session;
-    return rl;
+    struct lh_req_cookie *lrc = ne_malloc(sizeof *lrc);
+    lrc->store = session;
+    lrc->submit = NULL;
+    ne_set_request_private(req, HOOK_ID, lrc);
 }
 
-static void pre_send(void *private, ne_buffer *req)
+static void lk_pre_send(ne_request *r, void *userdata, ne_buffer *req)
 {
-    struct request_locks *rl = private;
-    
-    if (rl->locks != NULL) {
-       struct submit_locks *item;
+    struct lh_req_cookie *lrc = ne_get_request_private(r, HOOK_ID);
+
+    if (lrc->submit != NULL) {
+       struct lock_list *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);
+       for (item = lrc->submit; item != NULL; item = item->next) {
+           char *uri = ne_uri_unparse(&item->lock->uri);
+           ne_buffer_concat(req, " <", uri, "> (<",
+                            item->lock->token, ">)", NULL);
+           ne_free(uri);
        }
        ne_buffer_zappend(req, EOL);
     }
 }
 
-static void destroy(void *priv)
+/* Insert 'lock' into lock list *list. */
+static void insert_lock(struct lock_list **list, struct ne_lock *lock)
 {
-    struct request_locks *rl = priv;
-    struct submit_locks *lock, *next;
-    
-    for (lock = rl->locks; lock != NULL; lock = next) {
-       next = lock->next;
-       free(lock);
+    struct lock_list *item = ne_malloc(sizeof *item);
+    if (*list != NULL) {
+       (*list)->prev = item;
     }
-    
-    free(rl);
+    item->prev = NULL;
+    item->next = *list;
+    item->lock = lock;
+    *list = item;
 }
 
-static void free_locks(void *session)
+static void free_list(struct lock_list *list, int destroy)
 {
-    ne_lock_session *sess = session;
-    struct ne_lock *lk = sess->locks;
+    struct lock_list *next;
+
+    while (list != NULL) {
+       next = list->next;
+       if (destroy)
+           ne_lock_destroy(list->lock);
+       ne_free(list);
+       list = next;
+    }
+}
 
-    while (lk) {
-       struct ne_lock *nextlk = lk->next;
-       ne_lock_free(lk);
-       lk = nextlk;
-    }  
+static void lk_destroy(ne_request *req, void *userdata)
+{
+    struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
+    free_list(lrc->submit, 0);
+    ne_free(lrc);
+}
 
-    free(sess);
+void ne_lockstore_destroy(ne_lock_store *store)
+{
+    free_list(store->locks, 1);
+    ne_free(store);
 }
 
-ne_lock_session *ne_lock_register(ne_session *sess)
+ne_lock_store *ne_lockstore_create(void)
 {
-    ne_lock_session *locksess = ne_calloc(sizeof *locksess);
+    return ne_calloc(sizeof(ne_lock_store));
+}
 
+#define CURSOR_RET(s) ((s)->cursor?(s)->cursor->lock:NULL)
+
+struct ne_lock *ne_lockstore_first(ne_lock_store *store)
+{
+    store->cursor = store->locks;
+    return CURSOR_RET(store);
+}
+
+struct ne_lock *ne_lockstore_next(ne_lock_store *store)
+{
+    store->cursor = store->cursor->next;
+    return CURSOR_RET(store);
+}
+
+void ne_lockstore_register(ne_lock_store *store, ne_session *sess)
+{
     /* Register the hooks */
-    ne_add_hooks(sess, &lock_hooks, locksess, free_locks);
-    
-    return locksess;
+    ne_hook_create_request(sess, lk_create, store);
+    ne_hook_pre_send(sess, lk_pre_send, store);
+    ne_hook_destroy_request(sess, lk_destroy, store);
 }
 
 /* Submit the given lock for the given URI */
-static void submit_lock(struct request_locks *rl, struct ne_lock *lock, 
-                       const char *uri)
+static void submit_lock(struct lh_req_cookie *lrc, struct ne_lock *lock)
 {
-    struct submit_locks *slock;
+    struct lock_list *item;
 
     /* Check for dups */
-    for (slock = rl->locks; slock != NULL; slock = slock->next) {
-       if (strcasecmp(slock->lock->token, lock->token) == 0)
+    for (item = lrc->submit; item != NULL; item = item->next) {
+       if (strcasecmp(item->lock->token, lock->token) == 0)
            return;
     }
 
-    slock = ne_calloc(sizeof *slock);
-    slock->lock = lock;
-    slock->uri = uri;
-    slock->next = rl->locks;
-    rl->locks = slock;
+    insert_lock(&lrc->submit, lock);
 }
 
-struct ne_lock *ne_lock_find(ne_lock_session *sess, const char *uri)
+struct ne_lock *ne_lockstore_findbyuri(ne_lock_store *store,
+                                      const ne_uri *uri)
 {
-    struct ne_lock *cur;
-    for (cur = sess->locks; cur != NULL; cur = cur->next) {
-       if (uri_compare(uri, cur->uri) == 0) 
-           return cur;
+    struct lock_list *cur;
+
+    for (cur = store->locks; cur != NULL; cur = cur->next) {
+       if (ne_uri_cmp(&cur->lock->uri, uri) == 0) {
+           return cur->lock;
+       }
     }
+
     return NULL;
 }
 
-void ne_lock_using_parent(ne_request *req, const char *uri)
+void ne_lock_using_parent(ne_request *req, const char *path)
 {
-    struct request_locks *rl = ne_request_hook_private(req, HOOK_ID);
+    struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
+    ne_uri u;
+    struct lock_list *item;
     char *parent;
 
-    if (rl == NULL)
-       return; 
+    if (lrc == NULL)
+       return;
+    
+    parent = ne_path_parent(path);
+    if (parent == NULL)
+       return;
+    
+    u.authinfo = NULL;
+    ne_fill_server_uri(ne_get_session(req), &u);
 
-    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);
-    }
-}
+    for (item = lrc->store->locks; item != NULL; item = item->next) {
 
-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);
+       /* Only care about locks which are on this server. */
+       u.path = item->lock->uri.path;
+       if (ne_uri_cmp(&u, &item->lock->uri))
+           continue;
+       
+       /* This lock is needed if it is an infinite depth lock which
+        * covers the parent, or a lock on the parent itself. */
+       if ((item->lock->depth == NE_DEPTH_INFINITE && 
+            ne_path_childof(item->lock->uri.path, parent)) ||
+           ne_path_compare(item->lock->uri.path, parent) == 0) {
+           NE_DEBUG(NE_DBG_LOCKS, "Locked parent, %s on %s\n",
+                    item->lock->token, item->lock->uri.path);
+           submit_lock(lrc, item->lock);
        }
-       count++;
     }
-    
-    return count;
+
+    u.path = parent; /* handy: makes u.path valid and ne_free(parent). */
+    ne_uri_free(&u);
 }
 
 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 */
+    struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
+    struct lock_list *item;
     int match;
 
-    if (rl == NULL)
+    if (lrc == 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) {
+    /* Iterate over the list of stored locks to see if any of them
+     * apply to this resource */
+    for (item = lrc->store->locks; item != NULL; item = item->next) {
        
        match = 0;
        
-       if (depth == NE_DEPTH_INFINITE && uri_childof(uri, lock->uri)) {
+       if (depth == NE_DEPTH_INFINITE &&
+           ne_path_childof(uri, item->lock->uri.path)) {
            /* 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);
+           NE_DEBUG(NE_DBG_LOCKS, "Has child: %s\n", item->lock->token);
            match = 1;
        } 
-       else if (uri_compare(uri, lock->uri) == 0) {
+       else if (ne_path_compare(uri, item->lock->uri.path) == 0) {
            /* Case 2: this request is directly on a locked resource */
-           NE_DEBUG(NE_DBG_LOCKS, "Has direct lock: %s\n", lock->token);
+           NE_DEBUG(NE_DBG_LOCKS, "Has direct lock: %s\n", item->lock->token);
            match = 1;
        }
-       else if (lock->depth == NE_DEPTH_INFINITE && 
-                uri_childof(lock->uri, uri)) {
+       else if (item->lock->depth == NE_DEPTH_INFINITE && 
+                ne_path_childof(item->lock->uri.path, 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);
+           NE_DEBUG(NE_DBG_LOCKS, "Is child of: %s\n", item->lock->token);
            match = 1;
        }
        
        if (match) {
-           submit_lock(rl, lock, uri);
+           submit_lock(lrc, item->lock);
        }
     }
 
 }
 
-void ne_lock_add(ne_lock_session *sess, struct ne_lock *lock)
+void ne_lockstore_add(ne_lock_store *store, struct ne_lock *lock)
 {
-    if (sess->locks != NULL) {
-       sess->locks->prev = lock;
-    }
-    lock->prev = NULL;
-    lock->next = sess->locks;
-    sess->locks = lock;
+    insert_lock(&store->locks, lock);
 }
 
-void ne_lock_remove(ne_lock_session *sess, struct ne_lock *lock)
+void ne_lockstore_remove(ne_lock_store *store, struct ne_lock *lock)
 {
-    if (lock->prev != NULL) {
-       lock->prev->next = lock->next;
+    struct lock_list *item;
+
+    /* Find the lock */
+    for (item = store->locks; item != NULL; item = item->next)
+       if (item->lock == lock)
+           break;
+    
+    if (item->prev != NULL) {
+       item->prev->next = item->next;
     } else {
-       sess->locks = lock->next;
+       store->locks = item->next;
     }
-    if (lock->next != NULL) {
-       lock->next->prev = lock->prev;
+    if (item->next != NULL) {
+       item->next->prev = item->prev;
     }
+    ne_free(item);
 }
 
 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->uri.path = ne_strdup(lock->uri.path);
+    ret->uri.host = ne_strdup(lock->uri.host);
+    ret->uri.scheme = ne_strdup(lock->uri.scheme);
+    ret->uri.port = lock->uri.port;
+    ret->token = ne_strdup(lock->token);
     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;
 }
 
+struct ne_lock *ne_lock_create(void)
+{
+    struct ne_lock *lock = ne_calloc(sizeof *lock);
+    lock->depth = NE_DEPTH_ZERO;
+    lock->type = ne_locktype_write;
+    lock->scope = ne_lockscope_exclusive;
+    lock->timeout = NE_TIMEOUT_INVALID;
+    return lock;
+}
+
 void ne_lock_free(struct ne_lock *lock)
 {
-    NE_FREE(lock->uri);
+    ne_uri_free(&lock->uri);
     NE_FREE(lock->owner);
     NE_FREE(lock->token);
-    free(lock);
 }
 
-int ne_unlock(ne_session *sess, struct ne_lock *lock)
+void ne_lock_destroy(struct ne_lock *lock)
 {
-    ne_request *req = ne_request_create(sess, "UNLOCK", lock->uri);
+    ne_lock_free(lock);
+    ne_free(lock);
+}
+
+int ne_unlock(ne_session *sess, const struct ne_lock *lock)
+{
+    ne_request *req = ne_request_create(sess, "UNLOCK", lock->uri.path);
     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);
+    /* UNLOCK of a lock-null resource removes the resource from the
+     * parent collection; so an UNLOCK may modify the parent
+     * collection. (somewhat counter-intuitive, and not easily derived
+     * from 2518.) */
+    ne_lock_using_parent(req, lock->uri.path);
 
     ret = ne_request_dispatch(req);
     
@@ -373,58 +409,8 @@ int ne_unlock(ne_session *sess, struct ne_lock *lock)
     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) {
+static int parse_depth(const char *depth)
+{
     if (strcasecmp(depth, "infinity") == 0) {
        return NE_DEPTH_INFINITE;
     } else if (isdigit(depth[0])) {
@@ -434,11 +420,12 @@ static int parse_depth(const char *depth) {
     }
 }
 
-static long parse_timeout(const char *timeout) {
+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);
+       long to = strtol(timeout+7, NULL, 10);
        if (to == LONG_MIN || to == LONG_MAX)
            return NE_TIMEOUT_INVALID;
        return to;
@@ -452,53 +439,56 @@ static void discover_results(void *userdata, const char *href,
 {
     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);
+    const ne_status *status = ne_propset_status(set, &lock_props[0]);
+
+    /* Require at least that the lock has a token. */
+    if (lock->token) {
+       if (status && status->klass != 2) {
+           ctx->results(ctx->userdata, NULL, lock->uri.path, status);
+       } else {
+           ctx->results(ctx->userdata, lock, lock->uri.path, NULL);
+       }
+    }
+    else if (status) {
+       ctx->results(ctx->userdata, NULL, href, status);
+    }
+       
+    ne_lock_destroy(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)
+end_element_common(struct ne_lock *l, int state, const char *cdata)
 {
-    switch (elm->id)
-    case NE_ELM_write:
+    switch (state) 
+    case ELM_write:
        l->type = ne_locktype_write;
        break;
-    case NE_ELM_exclusive:
+    case ELM_exclusive:
        l->scope = ne_lockscope_exclusive;
        break;
-    case NE_ELM_shared:
+    case ELM_shared:
        l->scope = ne_lockscope_shared;
        break;
-    case NE_ELM_depth:
+    case 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:
+    case 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:
+    case ELM_owner:
        l->owner = strdup(cdata);
        break;
-    case NE_ELM_href:
+    case ELM_href:
        l->token = strdup(cdata);
        break;
     }
@@ -506,32 +496,141 @@ end_element_common(struct ne_lock *l, const struct ne_xml_elm *elm,
 }
 
 /* End-element handler for lock discovery PROPFIND response */
-static int 
-end_element_ldisc(void *userdata, const struct ne_xml_elm *elm, 
-                 const char *cdata) 
+static int end_element_ldisc(void *userdata, int state, 
+                             const char *nspace, const char *name)
 {
     struct ne_lock *lock = ne_propfind_current_private(userdata);
+    struct discover_ctx *ctx = userdata;
+
+    return end_element_common(lock, state, ctx->cdata->data);
+}
 
-    return end_element_common(lock, elm, cdata);
+static inline int can_accept(int parent, int id)
+{
+    return (parent == NE_XML_STATEROOT && id == ELM_prop) ||
+        (parent == ELM_prop && id == ELM_lockdiscovery) ||
+        (parent == ELM_lockdiscovery && id == ELM_activelock) ||
+        (parent == ELM_activelock && 
+         (id == ELM_lockscope || id == ELM_locktype ||
+          id == ELM_depth || id == ELM_owner ||
+          id == ELM_timeout || id == ELM_locktoken)) ||
+        (parent == ELM_lockscope &&
+         (id == ELM_exclusive || id == ELM_shared)) ||
+        (parent == ELM_locktype && id == ELM_write) ||
+        (parent == ELM_locktoken && id == ELM_href);
+}
+
+static int ld_startelm(void *userdata, int parent,
+                       const char *nspace, const char *name,
+                      const char **atts)
+{
+    struct discover_ctx *ctx = userdata;
+    int id = ne_xml_mapid(element_map, NE_XML_MAPLEN(element_map),
+                          nspace, name);
+    
+    ne_buffer_clear(ctx->cdata);
+    
+    if (can_accept(parent, id))
+        return id;
+    else
+        return NE_XML_DECLINE;
+}    
+
+#define MAX_CDATA (256)
+
+static int lk_cdata(void *userdata, int state,
+                    const char *cdata, size_t len)
+{
+    struct lock_ctx *ctx = userdata;
+
+    if (ctx->cdata->used + len < MAX_CDATA)
+        ne_buffer_append(ctx->cdata, cdata, len);
+    
+    return 0;
+}
+
+static int ld_cdata(void *userdata, int state,
+                    const char *cdata, size_t len)
+{
+    struct discover_ctx *ctx = userdata;
+
+    if (ctx->cdata->used + len < MAX_CDATA)
+        ne_buffer_append(ctx->cdata, cdata, len);
+    
+    return 0;
+}
+
+static int lk_startelm(void *userdata, int parent,
+                       const char *nspace, const char *name,
+                      const char **atts)
+{
+    struct lock_ctx *ctx = userdata;
+    int id;
+
+    id = ne_xml_mapid(element_map, NE_XML_MAPLEN(element_map), nspace, name);
+
+    NE_DEBUG(NE_DBG_LOCKS, "lk_startelm: %s => %d\n", name, id);
+
+    /* Lock-Token header is a MUST requirement: if the token
+     * is not known, bail out. */
+    if (id == 0 || ctx->token == NULL)
+       return NE_XML_DECLINE;
+
+    /* TODO: only accept 'prop' as root for LOCK response */
+    if (!can_accept(parent, id))
+        return NE_XML_DECLINE;
+
+    if (id == ELM_activelock && !ctx->found) {
+       /* a new activelock */
+       ne_lock_free(&ctx->active);
+       memset(&ctx->active, 0, sizeof ctx->active);
+    }
+
+    ne_buffer_clear(ctx->cdata);
+
+    return id;
 }
 
 /* End-element handler for LOCK response */
-static int
-end_element_lock(void *userdata, const struct ne_xml_elm *elm, 
-                const char *cdata)
+static int lk_endelm(void *userdata, int state,
+                     const char *nspace, const char *name)
 {
-    struct ne_lock *lock = userdata;
-    return end_element_common(lock, elm, cdata);
+    struct lock_ctx *ctx = userdata;
+
+    if (ctx->found)
+       return 0;
+
+    if (end_element_common(&ctx->active, state, ctx->cdata->data))
+       return -1;
+
+    if (state == ELM_activelock) {
+       if (ctx->active.token && strcmp(ctx->active.token, ctx->token) == 0) {
+           ctx->found = 1;
+       }
+    }
+
+    return 0;
 }
 
-static void *create_private(void *userdata, const char *uri)
+static void *ld_create(void *userdata, const char *href)
 {
-    return ne_calloc(sizeof(struct ne_lock));
+    struct discover_ctx *ctx = userdata;
+    struct ne_lock *lk = ne_lock_create();
+
+    if (ne_uri_parse(href, &lk->uri) != 0) {
+       ne_lock_destroy(lk);
+       return NULL;
+    }
+    
+    if (!lk->uri.host)
+       ne_fill_server_uri(ctx->session, &lk->uri);
+
+    return lk;
 }
 
 /* Discover all locks on URI */
 int ne_lock_discover(ne_session *sess, const char *uri, 
-                     ne_lock_result callback, void *userdata)
+                    ne_lock_result callback, void *userdata)
 {
     ne_propfind_handler *handler;
     struct discover_ctx ctx = {0};
@@ -539,30 +638,57 @@ int ne_lock_discover(ne_session *sess, const char *uri,
     
     ctx.results = callback;
     ctx.userdata = userdata;
+    ctx.session = sess;
+    ctx.cdata = ne_buffer_create();
 
     handler = ne_propfind_create(sess, uri, NE_DEPTH_ZERO);
 
-    ne_propfind_set_private(handler, create_private, NULL);
+    ne_propfind_set_private(handler, ld_create, &ctx);
     
-    ne_xml_push_handler(ne_propfind_get_parser(handler), lock_elms, 
-                        check_context, NULL, end_element_ldisc, handler);
+    ne_xml_push_handler(ne_propfind_get_parser(handler), 
+                        ld_startelm, ld_cdata, end_element_ldisc, handler);
     
     ret = ne_propfind_named(handler, lock_props, discover_results, &ctx);
     
+    ne_buffer_destroy(ctx.cdata);
     ne_propfind_destroy(handler);
 
     return ret;
 }
 
+static void add_timeout_header(ne_request *req, long timeout)
+{
+    if (timeout == NE_TIMEOUT_INFINITE) {
+       ne_add_request_header(req, "Timeout", "Infinite");
+    } 
+    else if (timeout != NE_TIMEOUT_INVALID && timeout > 0) {
+       ne_print_request_header(req, "Timeout", "Second-%ld", timeout);
+    }
+    /* just ignore it if timeout == 0 or invalid. */
+}
+
+/* Parse a Lock-Token response header. */
+static void get_ltoken_hdr(void *ud, const char *value)
+{
+    struct lock_ctx *ctx = ud;
+    
+    if (value[0] == '<') value++;
+    ctx->token = ne_strdup(value);
+    ne_shave(ctx->token, ">");
+}
+
 int ne_lock(ne_session *sess, struct ne_lock *lock) 
 {
-    ne_request *req = ne_request_create(sess, "LOCK", lock->uri);
+    ne_request *req = ne_request_create(sess, "LOCK", lock->uri.path);
     ne_buffer *body = ne_buffer_create();
     ne_xml_parser *parser = ne_xml_create();
     int ret, parse_failed;
+    struct lock_ctx ctx;
+
+    memset(&ctx, 0, sizeof ctx);
+    ctx.cdata = ne_buffer_create();    
 
-    ne_xml_push_handler(parser, lock_elms, check_context, 
-                        NULL, end_element_lock, lock);
+    ne_xml_push_handler(parser, lk_startelm, lk_cdata, lk_endelm, &ctx);
     
     /* Create the body */
     ne_buffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
@@ -579,28 +705,104 @@ int ne_lock(ne_session *sess, struct ne_lock *lock)
 
     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_xml_parse_v, parser);
+    ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
     ne_add_depth_header(req, lock->depth);
+    add_timeout_header(req, lock->timeout);
+    
+    ne_add_response_header_handler(req, "Lock-Token", get_ltoken_hdr, &ctx);
 
     /* 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);
+    ne_lock_using_parent(req, lock->uri.path);
     /* This one is clearer from 2518 sec 8.10.4. */
-    ne_lock_using_resource(req, lock->uri, lock->depth);
+    ne_lock_using_resource(req, lock->uri.path, lock->depth);
 
     ret = ne_request_dispatch(req);
 
     ne_buffer_destroy(body);
+    ne_buffer_destroy(ctx.cdata);
+    parse_failed = !ne_xml_valid(parser);
+    
+    if (ret == NE_OK && ne_get_status(req)->klass == 2) {
+       if (ctx.token == NULL) {
+           ret = NE_ERROR;
+           ne_set_error(sess, _("No Lock-Token header given"));
+       }
+       else if (parse_failed) {
+           ret = NE_ERROR;
+           ne_set_error(sess, "%s", ne_xml_get_error(parser));
+       }
+       else if (ne_get_status(req)->code == 207) {
+           ret = NE_ERROR;
+           /* TODO: set the error string appropriately */
+       }
+       else if (ctx.found) {
+           /* it worked: copy over real lock details if given. */
+           NE_FREE(lock->token);
+           lock->token = ctx.token;
+           ctx.token = NULL;
+           if (ctx.active.timeout != NE_TIMEOUT_INVALID)
+               lock->timeout = ctx.active.timeout;
+           lock->scope = ctx.active.scope;
+           lock->type = ctx.active.type;
+           if (ctx.active.depth >= 0)
+               lock->depth = ctx.active.depth;
+           if (ctx.active.owner) {
+               NE_FREE(lock->owner);
+               lock->owner = ctx.active.owner;
+               ctx.active.owner = NULL;
+           }
+       } else {
+           ret = NE_ERROR;
+           ne_set_error(sess, _("Response missing activelock for %s"), 
+                        ctx.token);
+       }
+    } else {
+       ret = NE_ERROR;
+    }
+
+    if (ctx.token)
+       ne_free(ctx.token);
+    ne_lock_free(&ctx.active);
+
+    ne_request_destroy(req);
+    ne_xml_destroy(parser);
+
+    return ret;
+}
+
+int ne_lock_refresh(ne_session *sess, struct ne_lock *lock)
+{
+    ne_request *req = ne_request_create(sess, "LOCK", lock->uri.path);
+    ne_xml_parser *parser = ne_xml_create();
+    int ret, parse_failed;
+
+    /* Handle the response and update *lock appropriately. */
+    ne_xml_push_handler(parser, lk_startelm, NULL, lk_endelm, lock);
+    
+    ne_add_response_body_reader(req, ne_accept_2xx, 
+                               ne_xml_parse_v, parser);
+
+    /* Probably don't need to submit any other lock-tokens for this
+     * resource? If it's an exclusive lock, then there can be no other
+     * locks covering the resource. If it's a shared lock, then this
+     * lock (which is being refreshed) is sufficient to modify the
+     * resource state? */
+    ne_print_request_header(req, "If", "(<%s>)", lock->token);
+    add_timeout_header(req, lock->timeout);
+
+    ret = ne_request_dispatch(req);
+
     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));
+           ne_set_error(sess, "%s", ne_xml_get_error(parser));
        }
        else if (ne_get_status(req)->code == 207) {
            ret = NE_ERROR;
@@ -613,6 +815,5 @@ int ne_lock(ne_session *sess, struct ne_lock *lock)
     ne_request_destroy(req);
     ne_xml_destroy(parser);
 
-    /* TODO: free the list */
     return ret;
 }
index 208d831..8a33054 100644 (file)
@@ -20,9 +20,7 @@
 
 /* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.  */
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "config.h"
 
 #include <sys/types.h>
 
@@ -41,6 +39,7 @@
 #include <ctype.h> /* for tolower */
 
 #include "ne_md5.h"
+#include "ne_string.h" /* for NE_ASC2HEX */
 
 #ifdef _LIBC
 # include <endian.h>
@@ -55,7 +54,6 @@
 #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
@@ -193,29 +191,6 @@ md5_stream (stream, 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;
@@ -433,17 +408,14 @@ md5_process_block (buffer, len, ctx)
   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[count*2] = NE_HEX2ASC(md5_buf[count] >> 4);
+       buffer[count*2+1] = NE_HEX2ASC(md5_buf[count] & 0x0f);
     }
     buffer[32] = '\0';
 }
@@ -454,7 +426,7 @@ 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]);
+       md5_buf[count] = ((NE_ASC2HEX(buffer[count*2])) << 4) |
+           NE_ASC2HEX(buffer[count*2+1]);
     }
 }
index 7033b8b..9714ec3 100644 (file)
@@ -137,13 +137,6 @@ extern void *ne_md5_read_ctx __P ((const struct ne_md5_ctx *ctx, void *resbuf));
    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]);
diff --git a/neon/src/ne_openssl.c b/neon/src/ne_openssl.c
new file mode 100644 (file)
index 0000000..a19d3d4
--- /dev/null
@@ -0,0 +1,859 @@
+/* 
+   neon SSL/TLS support using OpenSSL
+   Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk>
+   Portions are:
+   Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+   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 <stdio.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+#include <openssl/rand.h>
+
+#include "ne_ssl.h"
+#include "ne_string.h"
+#include "ne_session.h"
+#include "ne_i18n.h"
+
+#include "ne_private.h"
+#include "ne_privssl.h"
+
+/* OpenSSL 0.9.6 compatibility */
+#if OPENSSL_VERSION_NUMBER < 0x0090700fL
+#define PKCS12_unpack_authsafes M_PKCS12_unpack_authsafes
+#define PKCS12_unpack_p7data M_PKCS12_unpack_p7data
+/* cast away lack of const-ness */
+#define OBJ_cmp(a,b) OBJ_cmp((ASN1_OBJECT *)(a), (ASN1_OBJECT *)(b))
+#endif
+
+struct ne_ssl_dname_s {
+    X509_NAME *dn;
+};
+
+struct ne_ssl_certificate_s {
+    ne_ssl_dname subj_dn, issuer_dn;
+    X509 *subject;
+    ne_ssl_certificate *issuer;
+    char *identity;
+};
+
+struct ne_ssl_client_cert_s {
+    PKCS12 *p12;
+    int decrypted; /* non-zero if successfully decrypted. */
+    ne_ssl_certificate cert;
+    EVP_PKEY *pkey;
+    char *friendly_name;
+};
+
+char *ne_ssl_readable_dname(const ne_ssl_dname *name)
+{
+    int n, flag = 0;
+    ne_buffer *dump = ne_buffer_create();
+    const ASN1_OBJECT * const cname = OBJ_nid2obj(NID_commonName),
+       * const email = OBJ_nid2obj(NID_pkcs9_emailAddress);
+
+    for (n = X509_NAME_entry_count(name->dn); n > 0; n--) {
+       X509_NAME_ENTRY *ent = X509_NAME_get_entry(name->dn, n-1);
+       
+        /* Skip commonName or emailAddress except if there is no other
+         * attribute in dname. */
+       if ((OBJ_cmp(ent->object, cname) && OBJ_cmp(ent->object, email)) ||
+            (!flag && n == 1)) {
+           if (flag++)
+               ne_buffer_append(dump, ", ", 2);
+
+            switch (ent->value->type) {
+            case V_ASN1_UTF8STRING:
+            case V_ASN1_IA5STRING: /* definitely ASCII */
+            case V_ASN1_VISIBLESTRING: /* probably ASCII */
+            case V_ASN1_PRINTABLESTRING: /* subset of ASCII */
+                ne_buffer_append(dump, ent->value->data, ent->value->length);
+                break;
+            case V_ASN1_UNIVERSALSTRING:
+            case V_ASN1_T61STRING: /* let OpenSSL convert it as ISO-8859-1 */
+            case V_ASN1_BMPSTRING: {
+                unsigned char *tmp = ""; /* initialize to workaround 0.9.6 bug */
+                int len;
+
+                len = ASN1_STRING_to_UTF8(&tmp, ent->value);
+                if (len > 0) {
+                    ne_buffer_append(dump, tmp, len);
+                    OPENSSL_free(tmp);
+                    break;
+                } else {
+                    ERR_clear_error();
+                    /* and fall through */
+                }
+            }
+            default:
+                ne_buffer_zappend(dump, "???");
+                break;
+            }                
+       }
+    }
+
+    return ne_buffer_finish(dump);
+}
+
+int ne_ssl_dname_cmp(const ne_ssl_dname *dn1, const ne_ssl_dname *dn2)
+{
+    return X509_NAME_cmp(dn1->dn, dn2->dn);
+}
+
+void ne_ssl_clicert_free(ne_ssl_client_cert *cc)
+{
+    if (cc->p12)
+        PKCS12_free(cc->p12);
+    if (cc->decrypted) {
+        if (cc->cert.identity) ne_free(cc->cert.identity);
+        EVP_PKEY_free(cc->pkey);
+        X509_free(cc->cert.subject);
+    }
+    if (cc->friendly_name) ne_free(cc->friendly_name);
+    ne_free(cc);
+}
+
+/* Map a server cert verification into a string. */
+static void verify_err(ne_session *sess, int failures)
+{
+    struct {
+       int bit;
+       const char *str;
+    } reasons[] = {
+       { NE_SSL_NOTYETVALID, N_("certificate is not yet valid") },
+       { NE_SSL_EXPIRED, N_("certificate has expired") },
+       { NE_SSL_IDMISMATCH, N_("certificate issued for a different hostname") },
+       { NE_SSL_UNTRUSTED, N_("issuer is not trusted") },
+       { 0, NULL }
+    };
+    int n, flag = 0;
+
+    strcpy(sess->error, _("Server certificate verification failed: "));
+
+    for (n = 0; reasons[n].bit; n++) {
+       if (failures & reasons[n].bit) {
+           if (flag) strncat(sess->error, ", ", sizeof sess->error);
+           strncat(sess->error, _(reasons[n].str), sizeof sess->error);
+           flag = 1;
+       }
+    }
+
+}
+
+/* Format an ASN1 time to a string. 'buf' must be at least of size
+ * 'NE_SSL_VDATELEN'. */
+static void asn1time_to_string(ASN1_TIME *tm, char *buf)
+{
+    BIO *bio;
+    
+    strncpy(buf, _("[invalid date]"), NE_SSL_VDATELEN-1);
+    
+    bio = BIO_new(BIO_s_mem());
+    if (bio) {
+       if (ASN1_TIME_print(bio, tm))
+           BIO_read(bio, buf, NE_SSL_VDATELEN-1);
+       BIO_free(bio);
+    }
+}
+
+void ne_ssl_cert_validity(const ne_ssl_certificate *cert,
+                          char *from, char *until)
+{
+    ASN1_TIME *notBefore = X509_get_notBefore(cert->subject);
+    ASN1_TIME *notAfter = X509_get_notAfter(cert->subject);
+    
+    if (from) asn1time_to_string(notBefore, from);
+    if (until) asn1time_to_string(notAfter, until);
+}
+
+/* Return non-zero if hostname from certificate (cn) matches hostname
+ * used for session (hostname).  (Wildcard matching is no longer
+ * mandated by RFC3280, but certs are deployed which use wildcards) */
+static int match_hostname(char *cn, const char *hostname)
+{
+    const char *dot;
+    NE_DEBUG(NE_DBG_SSL, "Match %s on %s...\n", cn, hostname);
+    dot = strchr(hostname, '.');
+    if (dot == NULL) {
+       char *pnt = strchr(cn, '.');
+       /* hostname is not fully-qualified; unqualify the cn. */
+       if (pnt != NULL) {
+           *pnt = '\0';
+       }
+    }
+    else if (strncmp(cn, "*.", 2) == 0) {
+       hostname = dot + 1;
+       cn += 2;
+    }
+    return !strcasecmp(cn, hostname);
+}
+
+/* Check certificate identity.  Returns zero if identity matches; 1 if
+ * identity does not match, or <0 if the certificate had no identity.
+ * If 'identity' is non-NULL, store the malloc-allocated identity in
+ * *identity.  If 'server' is non-NULL, it must be the network address
+ * of the server in use, and identity must be NULL. */
+static int check_identity(const char *hostname, X509 *cert, char **identity,
+                          const ne_inet_addr *server)
+{
+    STACK_OF(GENERAL_NAME) *names;
+    int match = 0, found = 0;
+    
+    names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+    if (names) {
+       int n;
+
+        /* subjectAltName contains a sequence of GeneralNames */
+       for (n = 0; n < sk_GENERAL_NAME_num(names) && !match; n++) {
+           GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, n);
+           
+            /* handle dNSName and iPAddress name extensions only. */
+           if (nm->type == GEN_DNS) {
+               char *name = ne_strndup(nm->d.ia5->data, nm->d.ia5->length);
+                if (identity && !found) *identity = ne_strdup(name);
+               match = match_hostname(name, hostname);
+               ne_free(name);
+               found = 1;
+            } else if (nm->type == GEN_IPADD && server) {
+                /* compare IP address with server IP address. */
+                ne_inet_addr *ia;
+                if (nm->d.ip->length == 4)
+                    ia = ne_iaddr_make(ne_iaddr_ipv4, nm->d.ip->data);
+                else if (nm->d.ip->length == 16)
+                    ia = ne_iaddr_make(ne_iaddr_ipv6, nm->d.ip->data);
+                else
+                    ia = NULL;
+                /* ne_iaddr_make returns NULL if address type is unsupported */
+                if (ia != NULL) { /* address type was supported. */
+                    match = ne_iaddr_cmp(server, ia) == 0;
+                    found = 1;
+                    ne_iaddr_free(ia);
+                } else {
+                    NE_DEBUG(NE_DBG_SSL, "iPAddress name with unsupported "
+                             "address type (length %d), skipped.\n",
+                             nm->d.ip->length);
+                }
+            } /* TODO: handle uniformResourceIdentifier too */
+
+       }
+        /* free the whole stack. */
+        sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+    }
+    
+    /* Check against the commonName if no DNS alt. names were found,
+     * as per RFC3280. */
+    if (!found) {
+       X509_NAME *subj = X509_get_subject_name(cert);
+       X509_NAME_ENTRY *entry;
+       ASN1_STRING *str;
+       int idx = -1, lastidx;
+       char *name;
+
+       /* find the most specific commonName attribute. */
+       do {
+           lastidx = idx;
+           idx = X509_NAME_get_index_by_NID(subj, NID_commonName, lastidx);
+       } while (idx >= 0);
+       
+       if (lastidx < 0)
+           return -1;
+
+       /* extract the string from the entry */
+       entry = X509_NAME_get_entry(subj, lastidx);
+       str = X509_NAME_ENTRY_get_data(entry);
+
+       name = ne_strndup(str->data, str->length);
+        if (identity) *identity = ne_strdup(name);
+       match = match_hostname(name, hostname);
+       ne_free(name);
+    }
+
+    NE_DEBUG(NE_DBG_SSL, "Identity match: %s\n", match ? "good" : "bad");
+    return match ? 0 : 1;
+}
+
+/* Populate an ne_ssl_certificate structure from an X509 object. */
+static ne_ssl_certificate *populate_cert(ne_ssl_certificate *cert, X509 *x5)
+{
+    cert->subj_dn.dn = X509_get_subject_name(x5);
+    cert->issuer_dn.dn = X509_get_issuer_name(x5);
+    cert->issuer = NULL;
+    cert->subject = x5;
+    /* Retrieve the cert identity; pass a dummy hostname to match. */
+    cert->identity = NULL;
+    check_identity("", x5, &cert->identity, NULL);
+    return cert;
+}
+
+/* Return a linked list of certificate objects from an OpenSSL chain. */
+static ne_ssl_certificate *make_chain(STACK_OF(X509) *chain)
+{
+    int n, count = sk_X509_num(chain);
+    ne_ssl_certificate *top = NULL, *current = NULL;
+    
+    NE_DEBUG(NE_DBG_SSL, "Chain depth: %d\n", count);
+
+    for (n = 0; n < count; n++) {
+        ne_ssl_certificate *cert = ne_malloc(sizeof *cert);
+        populate_cert(cert, X509_dup(sk_X509_value(chain, n)));
+#if NE_DEBUGGING
+        if (ne_debug_mask & NE_DBG_SSL) {
+            fprintf(ne_debug_stream, "Cert #%d:\n", n);
+            X509_print_fp(ne_debug_stream, cert->subject);
+        }
+#endif
+        if (top == NULL) {
+            current = top = cert;
+        } else {
+            current->issuer = cert;
+            current = cert;
+        }
+    }
+
+    return top;
+}
+
+/* Verifies an SSL server certificate. */
+static int check_certificate(ne_session *sess, SSL *ssl, ne_ssl_certificate *chain)
+{
+    X509 *cert = chain->subject;
+    ASN1_TIME *notBefore = X509_get_notBefore(cert);
+    ASN1_TIME *notAfter = X509_get_notAfter(cert);
+    int ret, failures = 0;
+    long result;
+
+    /* check expiry dates */
+    if (X509_cmp_current_time(notBefore) >= 0)
+       failures |= NE_SSL_NOTYETVALID;
+    else if (X509_cmp_current_time(notAfter) <= 0)
+       failures |= NE_SSL_EXPIRED;
+
+    /* Check certificate was issued to this server; pass network
+     * address of server if a proxy is not in use. */
+    ret = check_identity(sess->server.hostname, cert, NULL, 
+                         sess->use_proxy ? NULL : sess->server.current);
+    if (ret < 0) {
+        ne_set_error(sess, _("Server certificate was missing commonName "
+                             "attribute in subject name"));
+        return NE_ERROR;
+    } else if (ret > 0) failures |= NE_SSL_IDMISMATCH;
+
+    /* get the result of the cert verification out of OpenSSL */
+    result = SSL_get_verify_result(ssl);
+
+    NE_DEBUG(NE_DBG_SSL, "Verify result: %ld = %s\n", result,
+            X509_verify_cert_error_string(result));
+
+    switch (result) {
+    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+       /* TODO: and probably more result codes here... */
+       failures |= NE_SSL_UNTRUSTED;
+       break;
+       /* ignore these, since we've already noticed them: */
+    case X509_V_ERR_CERT_NOT_YET_VALID:
+    case X509_V_ERR_CERT_HAS_EXPIRED:
+        /* cert was trusted: */
+    case X509_V_OK:
+       break;
+    default:
+       /* TODO: tricky to handle the 30-odd failure cases OpenSSL
+        * presents here (see x509_vfy.h), and present a useful API to
+        * the application so it in turn can then present a meaningful
+        * UI to the user.  The only thing to do really would be to
+        * pass back the error string, but that's not localisable.  So
+        * just fail the verification here - better safe than
+        * sorry. */
+       ne_set_error(sess, _("Certificate verification error: %s"),
+                    X509_verify_cert_error_string(result));
+       return NE_ERROR;
+    }
+
+    if (failures == 0) {
+        /* verified OK! */
+        ret = NE_OK;
+    } else {
+        /* Set up the error string. */
+       verify_err(sess, failures);
+        ret = NE_ERROR;
+        /* Allow manual override */
+        if (sess->ssl_verify_fn && 
+            sess->ssl_verify_fn(sess->ssl_verify_ud, failures, chain) == 0)
+            ret = NE_OK;
+    }
+
+    return ret;
+}
+
+/* Duplicate a client certificate, which must be in the decrypted state. */
+static ne_ssl_client_cert *dup_client_cert(const ne_ssl_client_cert *cc)
+{
+    ne_ssl_client_cert *newcc = ne_calloc(sizeof *newcc);
+    
+    newcc->decrypted = 1;
+    newcc->pkey = cc->pkey;
+    if (cc->friendly_name)
+        newcc->friendly_name = ne_strdup(cc->friendly_name);
+
+    populate_cert(&newcc->cert, cc->cert.subject);
+
+    cc->cert.subject->references++;
+    cc->pkey->references++;
+    return newcc;
+}
+
+/* Callback invoked when the SSL server requests a client certificate.  */
+static int provide_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
+{
+    ne_ssl_context *ctx = SSL_get_app_data(ssl);
+    ne_session *sess = SSL_CTX_get_app_data(ctx->ctx);
+
+    if (!sess->client_cert && sess->ssl_provide_fn) {
+       ne_ssl_dname **dnames = NULL;
+        int n, count = 0;
+       STACK_OF(X509_NAME) *ca_list = SSL_get_client_CA_list(ssl);
+
+        count = ca_list ? sk_X509_NAME_num(ca_list) : 0;
+
+        if (count > 0) {
+            dnames = ne_malloc(count * sizeof(ne_ssl_dname *));
+            
+            for (n = 0; n < count; n++) {
+                dnames[n] = ne_malloc(sizeof(ne_ssl_dname));
+                dnames[n]->dn = sk_X509_NAME_value(ca_list, n);
+            }
+        }
+
+       NE_DEBUG(NE_DBG_SSL, "Calling client certificate provider...\n");
+       sess->ssl_provide_fn(sess->ssl_provide_ud, sess, 
+                             (const ne_ssl_dname *const *)dnames, count);
+        if (count) {
+            for (n = 0; n < count; n++)
+                ne_free(dnames[n]);
+            ne_free(dnames);
+        }
+    }
+
+    if (sess->client_cert) {
+        ne_ssl_client_cert *const cc = sess->client_cert;
+       NE_DEBUG(NE_DBG_SSL, "Supplying client certificate.\n");
+       cc->pkey->references++;
+       cc->cert.subject->references++;
+       *cert = cc->cert.subject;
+       *pkey = cc->pkey;
+       return 1;
+    } else {
+       NE_DEBUG(NE_DBG_SSL, "No client certificate supplied.\n");
+       return 0;
+    }
+}
+
+void ne_ssl_set_clicert(ne_session *sess, const ne_ssl_client_cert *cc)
+{
+    sess->client_cert = dup_client_cert(cc);
+}
+
+ne_ssl_context *ne_ssl_context_create(void)
+{
+    ne_ssl_context *ctx = ne_malloc(sizeof *ctx);
+    ctx->ctx = SSL_CTX_new(SSLv23_client_method());
+    ctx->sess = NULL;
+    /* set client cert callback. */
+    SSL_CTX_set_client_cert_cb(ctx->ctx, provide_client_cert);
+    /* enable workarounds for buggy SSL server implementations */
+    SSL_CTX_set_options(ctx->ctx, SSL_OP_ALL);
+    return ctx;
+}
+
+void ne_ssl_context_destroy(ne_ssl_context *ctx)
+{
+    SSL_CTX_free(ctx->ctx);
+    if (ctx->sess)
+        SSL_SESSION_free(ctx->sess);
+    ne_free(ctx);
+}
+
+/* For internal use only. */
+int ne_negotiate_ssl(ne_request *req)
+{
+    ne_session *sess = ne_get_session(req);
+    ne_ssl_context *ctx = sess->ssl_context;
+    ne_ssl_socket *sock;
+    STACK_OF(X509) *chain;
+    int freechain = 0; /* non-zero if chain should be free'd. */
+
+    NE_DEBUG(NE_DBG_SSL, "Doing SSL negotiation.\n");
+
+    /* Rather a hack: link the ssl_context back to the ne_session, so
+     * provide_client_cert can get to the ne_session. */
+    SSL_CTX_set_app_data(ctx->ctx, sess);
+
+    if (ne_sock_connect_ssl(sess->socket, ctx)) {
+       if (ctx->sess) {
+           /* remove cached session. */
+           SSL_SESSION_free(ctx->sess);
+           ctx->sess = NULL;
+       }
+       ne_set_error(sess, _("SSL negotiation failed: %s"),
+                    ne_sock_error(sess->socket));
+       return NE_ERROR;
+    }  
+    
+    sock = ne_sock_sslsock(sess->socket);
+
+    chain = SSL_get_peer_cert_chain(sock->ssl);
+    /* For an SSLv2 connection, the cert chain will always be NULL. */
+    if (chain == NULL) {
+        X509 *cert = SSL_get_peer_certificate(sock->ssl);
+        if (cert) {
+            chain = sk_X509_new_null();
+            sk_X509_push(chain, cert);
+        }
+    }
+
+    if (chain == NULL || sk_X509_num(chain) == 0) {
+       ne_set_error(sess, _("SSL server did not present certificate"));
+       return NE_ERROR;
+    }
+
+    if (sess->server_cert) {
+        int diff = X509_cmp(sk_X509_value(chain, 0), sess->server_cert->subject);
+        if (freechain) sk_X509_free(chain); /* no longer need the chain */
+       if (diff) {
+           /* This could be a MITM attack: fail the request. */
+           ne_set_error(sess, _("Server certificate changed: "
+                                "connection intercepted?"));
+           return NE_ERROR;
+       } 
+       /* certificate has already passed verification: no need to
+        * verify it again. */
+    } else {
+       /* new connection: create the chain. */
+        ne_ssl_certificate *cert = make_chain(chain);
+
+        if (freechain) sk_X509_free(chain); /* no longer need the chain */
+
+       if (check_certificate(sess, sock->ssl, cert)) {
+           NE_DEBUG(NE_DBG_SSL, "SSL certificate checks failed: %s\n",
+                    sess->error);
+           ne_ssl_cert_free(cert);
+           return NE_ERROR;
+       }
+       /* remember the chain. */
+        sess->server_cert = cert;
+    }
+    
+    if (!ctx->sess) {
+       /* store the session. */
+       ctx->sess = SSL_get1_session(sock->ssl);
+    }
+
+    if (sess->notify_cb) {
+       sess->notify_cb(sess->notify_ud, ne_conn_secure,
+                        SSL_get_version(sock->ssl));
+    }
+
+    return NE_OK;
+}
+
+const ne_ssl_dname *ne_ssl_cert_issuer(const ne_ssl_certificate *cert)
+{
+    return &cert->issuer_dn;
+}
+
+const ne_ssl_dname *ne_ssl_cert_subject(const ne_ssl_certificate *cert)
+{
+    return &cert->subj_dn;
+}
+
+const ne_ssl_certificate *ne_ssl_cert_signedby(const ne_ssl_certificate *cert)
+{
+    return cert->issuer;
+}
+
+const char *ne_ssl_cert_identity(const ne_ssl_certificate *cert)
+{
+    return cert->identity;
+}
+
+void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert)
+{
+    X509_STORE *store = SSL_CTX_get_cert_store(ctx->ctx);
+    
+    X509_STORE_add_cert(store, cert->subject);
+}
+
+void ne_ssl_trust_default_ca(ne_session *sess)
+{
+    X509_STORE *store = SSL_CTX_get_cert_store(sess->ssl_context->ctx);
+    
+    X509_STORE_set_default_paths(store);
+}
+
+/* Find a friendly name in a PKCS12 structure the hard way, without
+ * decrypting the parts which are encrypted.. */
+static char *find_friendly_name(PKCS12 *p12)
+{
+    STACK_OF(PKCS7) *safes = PKCS12_unpack_authsafes(p12);
+    int n, m;
+    char *name = NULL;
+
+    if (safes == NULL) return NULL;
+    
+    /* Iterate over the unpacked authsafes: */
+    for (n = 0; n < sk_PKCS7_num(safes) && !name; n++) {
+        PKCS7 *safe = sk_PKCS7_value(safes, n);
+        STACK_OF(PKCS12_SAFEBAG) *bags;
+    
+        /* Only looking for unencrypted authsafes. */
+        if (OBJ_obj2nid(safe->type) != NID_pkcs7_data) continue;
+
+        bags = PKCS12_unpack_p7data(safe);
+        if (!bags) continue;
+
+        /* Iterate through the bags, picking out a friendly name */
+        for (m = 0; m < sk_PKCS12_SAFEBAG_num(bags) && !name; m++) {
+            PKCS12_SAFEBAG *bag = sk_PKCS12_SAFEBAG_value(bags, m);
+            name = PKCS12_get_friendlyname(bag);
+        }
+    
+        sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+    }
+
+    sk_PKCS7_pop_free(safes, PKCS7_free);
+    return name;
+}
+
+ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename)
+{
+    PKCS12 *p12;
+    FILE *fp;
+    X509 *cert;
+    EVP_PKEY *pkey;
+    ne_ssl_client_cert *cc;
+
+    fp = fopen(filename, "rb");
+    if (fp == NULL)
+        return NULL;
+
+    p12 = d2i_PKCS12_fp(fp, NULL);
+
+    fclose(fp);
+    
+    if (p12 == NULL) {
+        ERR_clear_error();
+        return NULL;
+    }
+
+    /* Try parsing with no password. */
+    if (PKCS12_parse(p12, NULL, &pkey, &cert, NULL) == 1) {
+        /* Success - no password needed for decryption. */
+        unsigned int len = 0;
+        unsigned char *name = X509_alias_get0(cert, &len);
+        
+        cc = ne_calloc(sizeof *cc);
+        cc->pkey = pkey;
+        cc->decrypted = 1;
+        if (name && len)
+            cc->friendly_name = ne_strndup((char *)name, len);
+        populate_cert(&cc->cert, cert);
+        PKCS12_free(p12);
+        return cc;
+    } else {
+        /* Failed to parse the file */
+        int err = ERR_get_error();
+        ERR_clear_error();
+        if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
+            ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
+            /* Decryption error due to bad password. */
+            cc = ne_calloc(sizeof *cc);
+            cc->friendly_name = find_friendly_name(p12);
+            cc->p12 = p12;
+            return cc;
+        } else {
+            /* Some parse error, give up. */
+            PKCS12_free(p12);
+            return NULL;
+        }
+    }
+}
+
+int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *cc)
+{
+    return !cc->decrypted;
+}
+
+int ne_ssl_clicert_decrypt(ne_ssl_client_cert *cc, const char *password)
+{
+    X509 *cert;
+    EVP_PKEY *pkey;
+
+    if (PKCS12_parse(cc->p12, password, &pkey, &cert, NULL) != 1) {
+        ERR_clear_error();
+        return -1;
+    }
+    
+    PKCS12_free(cc->p12);
+    populate_cert(&cc->cert, cert);
+    cc->pkey = pkey;
+    cc->decrypted = 1;
+    cc->p12 = NULL;
+    return 0;
+}
+
+const ne_ssl_certificate *ne_ssl_clicert_owner(const ne_ssl_client_cert *cc)
+{
+    return &cc->cert;
+}
+
+const char *ne_ssl_clicert_name(ne_ssl_client_cert *ccert)
+{
+    return ccert->friendly_name;
+}
+
+ne_ssl_certificate *ne_ssl_cert_read(const char *filename)
+{
+    FILE *fp = fopen(filename, "r");
+    X509 *cert;
+
+    if (fp == NULL)
+        return NULL;
+
+    cert = PEM_read_X509(fp, NULL, NULL, NULL);
+    fclose(fp);
+
+    if (cert == NULL) {
+        NE_DEBUG(NE_DBG_SSL, "d2i_X509_fp failed: %s\n", 
+                 ERR_reason_error_string(ERR_get_error()));
+        ERR_clear_error();
+        return NULL;
+    }
+
+    return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), cert);
+}
+
+int ne_ssl_cert_write(const ne_ssl_certificate *cert, const char *filename)
+{
+    FILE *fp = fopen(filename, "w");
+
+    if (fp == NULL) return -1;
+
+    if (PEM_write_X509(fp, cert->subject) != 1) {
+        ERR_clear_error();
+        fclose(fp);
+        return -1;
+    }
+    
+    if (fclose(fp) != 0)
+        return -1;
+
+    return 0;
+}
+
+void ne_ssl_cert_free(ne_ssl_certificate *cert)
+{
+    X509_free(cert->subject);
+    if (cert->issuer)
+        ne_ssl_cert_free(cert->issuer);
+    if (cert->identity)
+        ne_free(cert->identity);
+    ne_free(cert);
+}
+
+int ne_ssl_cert_cmp(const ne_ssl_certificate *c1, const ne_ssl_certificate *c2)
+{
+    return X509_cmp(c1->subject, c2->subject);
+}
+
+/* The certificate import/export format is the base64 encoding of the
+ * raw DER; PEM without the newlines and wrapping. */
+
+ne_ssl_certificate *ne_ssl_cert_import(const char *data)
+{
+    unsigned char *der, *p;
+    size_t len;
+    X509 *x5;
+    
+    /* decode the base64 to get the raw DER representation */
+    len = ne_unbase64(data, &der);
+    if (len == 0) return NULL;
+
+    p = der;
+    x5 = d2i_X509(NULL, &p, len); /* p is incremented */
+    ne_free(der);
+    if (x5 == NULL) {
+        ERR_clear_error();
+        return NULL;
+    }
+
+    return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), x5);
+}
+
+char *ne_ssl_cert_export(const ne_ssl_certificate *cert)
+{
+    int len;
+    unsigned char *der, *p;
+    
+    /* find the length of the DER encoding. */
+    len = i2d_X509(cert->subject, NULL);
+
+    p = der = ne_malloc(len);
+    i2d_X509(cert->subject, &p); /* p is incremented */
+
+    p = ne_base64(der, len);
+    ne_free(der);
+    return p;
+}
+
+#if SHA_DIGEST_LENGTH != 20
+# error SHA digest length is not 20 bytes
+#endif
+
+int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest)
+{
+    unsigned char sha1[EVP_MAX_MD_SIZE];
+    unsigned int len, j;
+    char *p;
+
+    if (!X509_digest(cert->subject, EVP_sha1(), sha1, &len) || len != 20) {
+        ERR_clear_error();
+        return -1;
+    }
+    
+    for (j = 0, p = digest; j < 20; j++) {
+        *p++ = NE_HEX2ASC((sha1[j] >> 4) & 0x0f);
+        *p++ = NE_HEX2ASC(sha1[j] & 0x0f);
+        *p++ = ':';
+    }
+
+    *--p = '\0';
+    return 0;
+}
index 38f2e3a..49f9d64 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP Request Handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
 #define NE_PRIVATE_H
 
 #include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_ssl.h"
 
 struct host_info {
-    /* hostname is not const since it changes on redirects. */
     char *hostname;
-    int port;
-    struct in_addr addr;
+    unsigned int port;
+    ne_sock_addr *address; /* if non-NULL, result of resolving 'hostname'. */
+    /* current network address obtained from 'address' being used. */
+    const ne_inet_addr *current;
     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 */
+/* Store every registered callback in a generic container, and cast
+ * the function pointer when calling it.  */
 struct hook {
-    const ne_request_hooks *hooks;
-    void *private;
-    ne_free_hooks free;
+    void (*fn)(void);
+    void *userdata;
+    const char *id; /* non-NULL for accessors. */
     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;
+    ne_socket *socket;
 
-    struct host_info server, proxy;
+    /* non-zero if connection has been established. */
+    int connected;
+    
+    /* non-zero if connection has persisted beyond one request. */
+    int persisted;
 
-    /* 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;
+    int is_http11; /* >0 if connected server is known to be
+                   * HTTP/1.1 compliant. */
+
+    char *scheme;
+    struct host_info server, proxy;
 
     /* Settings */
-    unsigned int have_proxy:1; /* do we have a proxy server? */
+    unsigned int use_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 use_ssl:1; /* whether a secure connection is required */
     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;
+    int expect100_works; /* known state of 100-continue support */
 
-    nssl_context *ssl_context;
-
-    sock_progress progress_cb;
+    ne_progress progress_cb;
     void *progress_ud;
 
     ne_notify_status notify_cb;
     void *notify_ud;
 
-    struct hook *hooks;
+    int rdtimeout; /* read timeout. */
 
-    char *user_agent; /* full User-Agent string */
+    struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks;
+    struct hook *destroy_req_hooks, *destroy_sess_hooks, *private;
 
-    /* The last HTTP-Version returned by the server */
-    int version_major;
-    int version_minor;
+    char *user_agent; /* full User-Agent: header field */
 
-    /* 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;
+#ifdef NEON_SSL
+    ne_ssl_client_cert *client_cert;
+    ne_ssl_certificate *server_cert;
+    ne_ssl_context *ssl_context;
 #endif
 
-};
+    /* Server cert verification callback: */
+    ne_ssl_verify_fn ssl_verify_fn;
+    void *ssl_verify_ud;
+    /* Client cert provider callback: */
+    ne_ssl_provide_fn ssl_provide_fn;
+    void *ssl_provide_ud;
 
-/* Set a new URI for the given request. */
-void ne_set_request_uri(ne_request *req, const char *uri);
+    /* Error string */
+    char error[BUFSIZ];
+};
 
 typedef int (*ne_push_fn)(void *userdata, const char *buf, size_t count);
 
@@ -201,4 +112,10 @@ typedef int (*ne_push_fn)(void *userdata, const char *buf, size_t count);
  */
 int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud);
 
+/* Do the SSL negotiation. */
+int ne_negotiate_ssl(ne_request *req);
+
+/* 0.24.x hack to fix ne_compress layer problems */
+void ne_kill_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);
+
 #endif /* HTTP_PRIVATE_H */
diff --git a/neon/src/ne_privssl.h b/neon/src/ne_privssl.h
new file mode 100644 (file)
index 0000000..1f63759
--- /dev/null
@@ -0,0 +1,48 @@
+/* 
+   SSL interface definitions internal to neon.
+   Copyright (C) 2003, 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
+
+*/
+
+/* THIS IS NOT A PUBLIC INTERFACE. You CANNOT include this header file
+ * from an application.  */
+#ifndef NE_PRIVSSL_H
+#define NE_PRIVSSL_H
+
+/* This is the private interface between ne_socket and ne_openssl. */
+
+#ifdef NEON_SSL
+
+#include <openssl/ssl.h>
+
+#include "ne_ssl.h"
+
+/* SSL context */
+struct ne_ssl_context_s {
+    SSL_CTX *ctx;
+    SSL_SESSION *sess;
+};
+
+struct ne_ssl_socket_s {
+    SSL *ssl;
+};
+
+#endif /* NEON_SSL */
+
+#endif
index ff27d99..057085e 100644 (file)
@@ -1,6 +1,6 @@
 /* 
-   WebDAV Properties manipulation
-   Copyright (C) 2000-2001, Joe Orton <joe@light.plus.com>
+   WebDAV property manipulation
+   Copyright (C) 2000-2004, 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
 #include "ne_xml.h"
 #include "ne_props.h"
 #include "ne_basic.h"
+#include "ne_locks.h"
+
+/* don't store flat props with a value > 10K */
+#define MAX_FLATPROP_LEN (102400)
 
 struct ne_propfind_handler_s {
     ne_session *sess;
@@ -48,19 +52,17 @@ struct ne_propfind_handler_s {
     ne_props_create_complex private_creator;
     void *private_userdata;
     
-    /* Current propset. */
+    /* Current propset, or NULL if none being processed. */
     ne_prop_result_set *current;
 
+    ne_buffer *value; /* current flat property value */
+    int depth; /* nesting depth within a flat property */
+
     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 }
-};
+#define ELM_flatprop (NE_207_STATE_TOP - 1)
 
 /* We build up the results of one 'response' element in memory. */
 struct prop {
@@ -70,11 +72,13 @@ struct prop {
     ne_propname pname;
 };
 
+#define NSPACE(x) ((x) ? (x) : "")
+
 struct propstat {
     struct prop *props;
     int numprops;
     ne_status status;
-} propstat;
+};
 
 /* Results set. */
 struct ne_prop_result_set_s {
@@ -86,11 +90,21 @@ struct ne_prop_result_set_s {
 
 
 static int 
-startelm(void *userdata, const struct ne_xml_elm *elm, 
+startelm(void *userdata, int state, const char *name, const char *nspace,
         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);
+endelm(void *userdata, int state, const char *name, const char *nspace);
+
+/* Handle character data; flat property value. */
+static int chardata(void *userdata, int state, const char *data, size_t len)
+{
+    ne_propfind_handler *hdl = userdata;
+
+    if (state == ELM_flatprop && hdl->value->length < MAX_FLATPROP_LEN)
+        ne_buffer_append(hdl->value, data, len);
+
+    return 0;
+}
 
 ne_xml_parser *ne_propfind_get_parser(ne_propfind_handler *handler)
 {
@@ -110,20 +124,15 @@ static int propfind(ne_propfind_handler *handler,
 
     /* 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);
+    ne_xml_push_handler(handler->parser, startelm, chardata, 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_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
     
     ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, 
                                  handler->parser);
@@ -133,7 +142,7 @@ static int propfind(ne_propfind_handler *handler,
     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));
+       ne_set_error(handler->sess, "%s", ne_xml_get_error(handler->parser));
        ret = NE_ERROR;
     }
 
@@ -152,7 +161,7 @@ static void set_body(ne_propfind_handler *hdl, const ne_propname *names)
 
     for (n = 0; names[n].name != NULL; n++) {
        ne_buffer_concat(body, "<", names[n].name, " xmlns=\"", 
-                      names[n].nspace, "\"/>" EOL, NULL);
+                        NSPACE(names[n].nspace), "\"/>" EOL, NULL);
     }
 
 }
@@ -175,7 +184,7 @@ int ne_propfind_named(ne_propfind_handler *handler, const ne_propname *props,
 
 /* The easy one... PROPPATCH */
 int ne_proppatch(ne_session *sess, const char *uri, 
-                 const ne_proppatch_operation *items)
+                const ne_proppatch_operation *items)
 {
     ne_request *req = ne_request_create(sess, "PROPPATCH", uri);
     ne_buffer *body = ne_buffer_create();
@@ -183,34 +192,38 @@ int ne_proppatch(ne_session *sess, const char *uri,
     
     /* Create the request body */
     ne_buffer_zappend(body, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
-                    "<propertyupdate xmlns=\"DAV:\">");
+                    "<D:propertyupdate xmlns:D=\"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;
+       const char *elm = (items[n].type == ne_propset) ? "set" : "remove";
+
+       /* <set><prop><prop-name>value</prop-name></prop></set> */
+       ne_buffer_concat(body, "<D:", elm, "><D:prop>"
+                        "<", items[n].name->name, NULL);
+       
+       if (items[n].name->nspace) {
+           ne_buffer_concat(body, " xmlns=\"", items[n].name->nspace, "\"", NULL);
        }
+
+       if (items[n].type == ne_propset) {
+           ne_buffer_concat(body, ">", items[n].value, NULL);
+       } else {
+           ne_buffer_append(body, ">", 1);
+       }
+
+       ne_buffer_concat(body, "</", items[n].name->name, "></D:prop></D:", elm, ">"
+                        EOL, NULL);
     }  
 
-    ne_buffer_zappend(body, "</propertyupdate>" EOL);
+    ne_buffer_zappend(body, "</D: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? */
+    ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
     
+#ifdef USE_DAV_LOCKS
+    ne_lock_using_resource(req, uri, NE_DEPTH_ZERO);
+#endif
+
     ret = ne_simple_request(sess, req);
     
     ne_buffer_destroy(body);
@@ -221,8 +234,16 @@ int ne_proppatch(ne_session *sess, const char *uri,
 /* 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));
+    if (pn1->nspace == NULL && pn2->nspace != NULL) {
+       return 1;
+    } else if (pn1->nspace != NULL && pn2->nspace == NULL) {
+       return -1;
+    } else if (pn1->nspace == NULL) {
+       return strcmp(pn1->name, pn2->name);
+    } else {
+       return (strcmp(pn1->nspace, pn2->nspace) ||
+               strcmp(pn1->name, pn2->name));
+    }
 }
 
 /* Find property in 'set' with name 'pname'.  If found, set pstat_ret
@@ -277,7 +298,7 @@ const char *ne_propset_lang(const ne_prop_result_set *set,
 
 void *ne_propfind_current_private(ne_propfind_handler *handler)
 {
-    return handler->current->private;
+    return handler->current ? handler->current->private : NULL;
 }
 
 void *ne_propset_private(const ne_prop_result_set *set)
@@ -320,16 +341,6 @@ const ne_status *ne_propset_status(const ne_prop_result_set *set,
     }
 }
 
-
-/* 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));
@@ -353,7 +364,7 @@ static void *start_propstat(void *userdata, void *response)
     struct propstat *pstat;
 
     n = set->numpstats;
-    set->pstats = realloc(set->pstats, sizeof(struct propstat) * (n+1));
+    set->pstats = ne_realloc(set->pstats, sizeof(struct propstat) * (n+1));
     set->numpstats = n+1;
 
     pstat = &set->pstats[n];
@@ -363,9 +374,8 @@ static void *start_propstat(void *userdata, void *response)
     return &set->pstats[n];
 }
 
-static int 
-startelm(void *userdata, const struct ne_xml_elm *elm, 
-        const char **atts)
+static int startelm(void *userdata, int parent,
+                    const char *nspace, const char *name, const char **atts)
 {
     ne_propfind_handler *hdl = userdata;
     struct propstat *pstat = ne_207_get_current_propstat(hdl->parser207);
@@ -373,34 +383,45 @@ startelm(void *userdata, const struct ne_xml_elm *elm,
     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;
-    }
+    /* Just handle all children of propstat and their descendants. */
+    if ((parent != NE_207_STATE_PROP && parent != ELM_flatprop) 
+        || pstat == NULL)
+        return NE_XML_DECLINE;
+
+    if (parent == ELM_flatprop) {
+        /* collecting the flatprop value. */
+        hdl->depth++;
+        if (hdl->value->used < MAX_FLATPROP_LEN)
+            ne_buffer_concat(hdl->value, "<", name, ">", NULL);
+        return ELM_flatprop;
+    }        
 
     /* Add a property to this propstat */
     n = pstat->numprops;
 
-    pstat->props = realloc(pstat->props, sizeof(struct prop) * (n + 1));
+    pstat->props = ne_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->pname.name = prop->name = ne_strdup(name);
+    if (nspace[0] == '\0') {
+       prop->pname.nspace = prop->nspace = NULL;
+    } else {
+       prop->pname.nspace = prop->nspace = ne_strdup(nspace);
+    }
     prop->value = NULL;
 
-    NE_DEBUG(NE_DBG_XML, "Got property #%d: %s@@%s.\n", n, 
-         prop->nspace, prop->name);
+    NE_DEBUG(NE_DBG_XML, "Got property #%d: {%s}%s.\n", n, 
+            NSPACE(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");
+    lang = ne_xml_get_attr(hdl->parser, atts, NULL, "xml:lang");
     if (lang != NULL) {
        prop->lang = ne_strdup(lang);
        NE_DEBUG(NE_DBG_XML, "Property language is %s\n", prop->lang);
@@ -408,36 +429,41 @@ startelm(void *userdata, const struct ne_xml_elm *elm,
        prop->lang = NULL;
     }
 
-    return 0;
+    hdl->depth = 0;
+
+    return ELM_flatprop;
 }
 
-static int 
-endelm(void *userdata, const struct ne_xml_elm *elm, const char *cdata)
+static int endelm(void *userdata, int state,
+                  const char *nspace, const char *name)
 {
     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;
+    if (hdl->depth > 0) {
+        /* nested. */
+        if (hdl->value->used < MAX_FLATPROP_LEN)
+            ne_buffer_concat(hdl->value, "</", name, ">", NULL);
+        hdl->depth--;
+    } else {
+        /* end of the current property value */
+        n = pstat->numprops - 1;
+        pstat->props[n].value = ne_buffer_finish(hdl->value);
+        hdl->value = ne_buffer_create();
     }
-
-    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 ne_status *status,
                         const char *description)
 {
     struct propstat *pstat = pstat_v;
 
+    /* Nothing to do if no status was given. */
+    if (!status) return;
+
     /* 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
@@ -454,12 +480,14 @@ static void end_propstat(void *userdata, void *pstat_v,
        int n;
        
        for (n = 0; n < pstat->numprops; n++) {
-           free(pstat->props[n].value);
+           ne_free(pstat->props[n].value);
            pstat->props[n].value = NULL;
        }
     }
 
+    /* copy the status structure, and dup the reason phrase. */
     pstat->status = *status;
+    pstat->status.reason_phrase = ne_strdup(status->reason_phrase);
 }
 
 /* Frees up a results set */
@@ -472,39 +500,38 @@ static void free_propset(ne_prop_result_set *set)
        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].nspace);
+           ne_free(p->props[m].name);
            NE_FREE(p->props[m].lang);
            NE_FREE(p->props[m].value);
        }
 
-       free(set->pstats[n].props);
+       if (p->status.reason_phrase)
+           ne_free(p->status.reason_phrase);
+       if (p->props)
+           ne_free(p->props);
     }
 
-    free(set->pstats);
-    free(set);  
+    if (set->pstats)
+       ne_free(set->pstats);
+    ne_free(set->href);
+    ne_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) {
+    if (handler->callback && set->numpstats > 0)
        handler->callback(handler->userdata, set->href, set);
-    }
-
-    free(set->href);
 
     /* Clean up the propset tree we've just built. */
     free_propset(set);
+    handler->current = NULL;
 }
 
 ne_propfind_handler *
@@ -517,6 +544,7 @@ ne_propfind_create(ne_session *sess, const char *uri, int depth)
     ret->sess = sess;
     ret->body = ne_buffer_create();
     ret->request = ne_request_create(sess, "PROPFIND", uri);
+    ret->value = ne_buffer_create();
 
     ne_add_depth_header(ret->request, depth);
 
@@ -537,11 +565,14 @@ ne_propfind_create(ne_session *sess, const char *uri, int depth)
 /* Destroy a propfind handler */
 void ne_propfind_destroy(ne_propfind_handler *handler)
 {
+    ne_buffer_destroy(handler->value);
+    if (handler->current)
+        free_propset(handler->current);
     ne_207_destroy(handler->parser207);
     ne_xml_destroy(handler->parser);
     ne_buffer_destroy(handler->body);
     ne_request_destroy(handler->request);
-    free(handler);    
+    ne_free(handler);    
 }
 
 int ne_simple_propfind(ne_session *sess, const char *href, int depth,
index 0e8e2b2..4713156 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    WebDAV Properties manipulation
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
@@ -52,6 +52,10 @@ BEGIN_NEON_DECLS
  *   - the properties results set (const ne_prop_result_set *results)
  * */
 
+/* The name of a WebDAV property. 'nspace' may be NULL. */
+typedef struct {
+    const char *nspace, *name;
+} ne_propname;
 
 typedef struct ne_prop_result_set_s ne_prop_result_set;
 
@@ -119,33 +123,43 @@ typedef void (*ne_props_result)(void *userdata, const char *href,
  * the request.
  *
  * Returns NE_*.  */
-int ne_simple_propfind(ne_session *sess, const char *uri, int depth,
+int ne_simple_propfind(ne_session *sess, const char *path, 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.  */
+/* The properties of a resource can be manipulated using ne_proppatch.
+ * A single proppatch request may include any number of individual
+ * "set" and "remove" operations, and is defined to have
+ * "all-or-nothing" semantics, so either all the operations succeed,
+ * or none do. */
+
+/* A proppatch operation may either set a property to have a new
+ * value, in which case 'type' must be ne_propset, and 'value' must be
+ * non-NULL; or it can remove a property; in which case 'type' must be
+ * ne_propremove, and 'value' is ignored.  In both cases, 'name' must
+ * be set to the name of the property to alter. */
+enum ne_proppatch_optype {
+    ne_propset,
+    ne_propremove
+};
 typedef struct {
     const ne_propname *name;
-    enum {
-       ne_propset,
-       ne_propremove
-    } type;
+    enum ne_proppatch_optype type;
     const char *value;
 } ne_proppatch_operation;
 
-int ne_proppatch(ne_session *sess, const char *uri,
-                 const ne_proppatch_operation *items);
+/* Execute a set of property operations 'ops' on 'path'. 'ops' is an
+ * array terminated by an operation with a NULL 'name' field. Returns
+ * NE_*. */
+int ne_proppatch(ne_session *sess, const char *path,
+                const ne_proppatch_operation *ops);
 
-/* Retrieve property names for the resources at 'href'.  'results'
+/* Retrieve property names for the resources at 'path'.  '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 passed results object to retrieve the list of property names.
+ * */
+int ne_propnames(ne_session *sess, const char *path, int depth,
+                ne_props_result results, void *userdata);
 
 /* The complex, you-do-all-the-work, property fetch interface:
  */
@@ -164,16 +178,21 @@ void *ne_propfind_current_private(ne_propfind_handler *handler);
  *
  * Depth must be one of NE_DEPTH_*. */
 ne_propfind_handler *
-ne_propfind_create(ne_session *sess, const char *uri, int depth);
+ne_propfind_create(ne_session *sess, const char *path, 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);
 
+/* This interface reserves the state integer range 'x' where 0 < x
+ * and x < NE_PROPS_STATE_TOP. */
+#define NE_PROPS_STATE_TOP (NE_207_STATE_TOP + 100)
+
 /* 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).  */
+ * PROPFIND request).  The result of using the request pointer after
+ * ne_propfind_destroy(handler) has been called is undefined. */
 ne_request *ne_propfind_get_request(ne_propfind_handler *handler);
 
 /* A "complex property" has a value which is structured XML. To handle
@@ -196,25 +215,27 @@ ne_request *ne_propfind_get_request(ne_propfind_handler *handler);
  * results callback, simply call 'ne_propset_private'.
  * */
 typedef void *(*ne_props_create_complex)(void *userdata,
-                                         const char *uri);
+                                        const char *href);
 
 void ne_propfind_set_private(ne_propfind_handler *handler,
-                             ne_props_create_complex creator,
-                             void *userdata);
+                            ne_props_create_complex creator,
+                            void *userdata);
 
-/* Find all properties.
+/* Fetch all properties.
  *
  * Returns NE_*. */
 int ne_propfind_allprop(ne_propfind_handler *handler, 
-                        ne_props_result result, void *userdata);
+                       ne_props_result result, void *userdata);
 
-/* Find properties named in a call to ne_propfind_set_flat and/or
- * ne_propfind_set_complex.
+/* Fetch all properties with names listed in array 'names', which is
+ * terminated by a property with a NULL name field.  For each resource
+ * encountered, the result callback will be invoked, passing in
+ * 'userdata' as the first argument.
  *
  * Returns NE_*. */
 int ne_propfind_named(ne_propfind_handler *handler, 
-                      const ne_propname *prop,
-                      ne_props_result result, void *userdata);
+                     const ne_propname *names,
+                     ne_props_result result, void *userdata);
 
 /* Destroy a propfind handler after use. */
 void ne_propfind_destroy(ne_propfind_handler *handler);
index bb07a48..1985793 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP-redirect support
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
 #include "ne_uri.h"
 #include "ne_redirect.h"
 #include "ne_i18n.h"
-
-/* TODO: should remove this. */
-#include "ne_private.h"
+#include "ne_string.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;
+    char *requri;
+    int valid; /* non-zero if .uri contains a redirect */
+    ne_uri uri;
+    ne_session *sess;
 };
 
-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)
+static void
+create(ne_request *req, void *session, 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;
+    NE_FREE(red->requri);
+    red->requri = ne_strdup(uri);
+    ne_add_response_header_handler(req, "Location", ne_duplicate_header,
+                                  &red->location);
 }
 
-/* 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);
-}
+#define REDIR(n) ((n) == 301 || (n) == 302 || (n) == 303 || \
+                 (n) == 307)
 
-static int post_send(void *private, const ne_status *status)
+static int post_send(ne_request *req, 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. */
+    /* Don't do anything for non-redirect status or no Location header. */
+    if (!REDIR(status->code) || red->location == NULL)
        return NE_OK;
+
+    if (strstr(red->location, "://") == NULL && red->location[0] != '/') {
+       /* FIXME: remove this relative URI resolution hack. */
+       ne_buffer *path = ne_buffer_create();
+       char *pnt;
+       
+       ne_buffer_zappend(path, red->requri);
+       pnt = strrchr(path->data, '/');
+
+       if (pnt && *(pnt+1) != '\0') {
+           /* Chop off last path segment. */
+           *(pnt+1) = '\0';
+           ne_buffer_altered(path);
+       }
+       ne_buffer_zappend(path, red->location);
+       ne_free(red->location);
+       red->location = ne_buffer_finish(path);
     }
+
+    /* free last uri. */
+    ne_uri_free(&red->uri);
     
-    if (uri_parse(red->location, &uri, NULL)) {
-       /* Couldn't parse the URI */
-       ne_set_error(red->session, _("Could not parse redirect location."));
+    /* Parse the Location header */
+    if (ne_uri_parse(red->location, &red->uri) || red->uri.path == NULL) {
+        red->valid = 0;
+       ne_set_error(red->sess, _("Could not parse redirect location."));
        return NE_ERROR;
     }
+
+    /* got a valid redirect. */
+    red->valid = 1;
     
-    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);
-           }
-       }
+    if (!red->uri.host) {
+       /* Not an absoluteURI: breaks 2616 but everybody does it. */
+       ne_fill_server_uri(red->sess, &red->uri);
     }
 
-    /* Free up the URI. */
-    uri_free(&uri);
-
-    return ret;
+    return NE_REDIRECT;
 }
 
 static void free_redirect(void *cookie)
 {
     struct redirect *red = cookie;
     NE_FREE(red->location);
-    free(red);
+    ne_uri_free(&red->uri);
+    if (red->requri)
+        ne_free(red->requri);
+    ne_free(red);
 }
 
-void ne_redirect_register(ne_session *sess, 
-                         ne_redirect_confirm confirm,
-                         ne_redirect_notify notify,
-                         void *userdata)
+void ne_redirect_register(ne_session *sess)
 {
     struct redirect *red = ne_calloc(sizeof *red);
     
-    red->confirm = confirm;
-    red->notify = notify;
-    red->userdata = userdata;
-    red->session = sess;
+    red->sess = sess;
 
-    ne_add_hooks(sess, &redirect_hooks, red, free_redirect);
+    ne_hook_create_request(sess, create, red);
+    ne_hook_post_send(sess, post_send, red);
+    ne_hook_destroy_session(sess, free_redirect, red);
+
+    ne_set_session_private(sess, REDIRECT_ID, red);
 }
 
-const char *ne_redirect_location(ne_session *sess)
+const ne_uri *ne_redirect_location(ne_session *sess)
 {
-    struct redirect *red = ne_session_hook_private(sess, REDIRECT_ID);
-    return red->location;
+    struct redirect *red = ne_get_session_private(sess, REDIRECT_ID);
+
+    if (red && red->valid)
+        return &red->uri;
+    else
+        return NULL;
 }
 
index d18baf5..d0dd7b5 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP-redirect support
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2002, 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
 
 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).
- * */
+/* Register redirect handling: if a redirection response is given, the
+ * request will fail with the NE_REDIRECT code, and the destinsation
+ * of the redirect can be retrieved using ne_redirect_location(). */
+void ne_redirect_register(ne_session *sess);
 
-/* 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);
+/* Returns location of last redirect.  Will return NULL if no redirect
+ * has been encountered for given session, or the last redirect
+ * encountered could not be parsed. */
+const ne_uri *ne_redirect_location(ne_session *sess);
 
 END_NEON_DECLS
 
index af52449..7233cf3 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP request/response handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
@@ -32,7 +32,7 @@
 #endif
 
 #ifdef HAVE_LIMITS_H
-#include <limits.h> /* just for Win32? */
+#include <limits.h> /* for UINT_MAX etc */
 #endif
 
 #include <ctype.h>
 #endif 
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
-#endif /* HAVE_STDLIB_H */
+#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#ifdef HAVE_SNPRINTF_H
-#include "snprintf.h"
 #endif
 
 #include "ne_i18n.h"
 /* 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)
 
+#define SOCK_ERR(req, op, msg) do { ssize_t sret = (op); \
+if (sret < 0) return aborted(req, msg, sret); } while (0)
+
+/* 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;
+};
+
+struct ne_request_s {
+    char *method, *uri; /* method and Request-URI */
+
+    ne_buffer *headers; /* request headers */
+
+    /* Request body. */
+    ne_provide_body body_cb;
+    void *body_ud;
+
+    /* Comes from either an fd or a buffer. */
+    union {
+       int fd;
+       struct {
+           const char *buffer, *pnt;
+           size_t left;
+       } buf;
+    } body;
+           
+    size_t body_size, body_progress;
+
+    /* temporary store for response lines. */
+    char respbuf[BUFSIZ];
+
+    /**** 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. */
+       /* how the message length is detemined: */
+       enum {
+           R_TILLEOF = 0, /* read till eof */
+           R_NO_BODY, /* implicitly no body (HEAD, 204, 205, 304) */
+           R_CHUNKED, /* using chunked transfer-encoding */
+           R_CLENGTH  /* using given content-length */
+       } mode;
+    } resp;
+
+    /* List of callbacks which are passed response headers */
+    struct header_handler *header_catchers;
+    
+    struct hook *private;
+
+    /* We store response header handlers in a hash table.  The hash is
+     * derived from the header name in lower case. */
+
+    /* FIXME: this comment is WAY out of date, and no longer in any
+     * way true. */
+           
+    /* 53 is magic, of course.  For a standard ne_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_expect100:1;
+    unsigned int can_persist:1;
+
+    ne_session *session;
+    ne_status status;
+};
+
 static int open_connection(ne_request *req);
 
 /* The iterative step used to produce the hash value.  This is DJB's
@@ -84,66 +172,59 @@ static int open_connection(ne_request *req);
  * 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);
+#define HH_ITERATE(hash, ch) (((hash)*33 + (ch)) % HH_HASHSIZE);
 
-/* Produce the hash value for a header name, which MUST be in lower
- * case.  */
-static unsigned int hdr_hash(const char *name)
+/* Returns hash value for header 'name', converting it to lower-case
+ * in-place. */
+static inline unsigned int hash_and_lower(char *name)
 {
-    const char *pnt;
+    char *pnt;
     unsigned int hash = 0;
 
     for (pnt = name; *pnt != '\0'; pnt++) {
+       *pnt = tolower(*pnt);
        hash = HH_ITERATE(hash,*pnt);
     }
 
     return hash;
 }
 
-static int set_sockerr(ne_request *req, const char *doing, int code)
+/* Abort a request due to an non-recoverable HTTP protocol error,
+ * whilst doing 'doing'.  'code', if non-zero, is the socket error
+ * code, NE_SOCK_*, or if zero, is ignored. */
+static int aborted(ne_request *req, const char *doing, ssize_t code)
 {
+    ne_session *sess = req->session;
+    int ret = NE_ERROR;
+
+    NE_DEBUG(NE_DBG_HTTP, "Aborted request (%" NE_FMT_SSIZE_T "): %s\n",
+            code, doing);
+
     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."),
+    case NE_SOCK_CLOSED:
+       if (sess->use_proxy) {
+           ne_set_error(sess, _("%s: connection was closed by proxy server."),
                         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);
+           ne_set_error(sess, _("%s: connection was closed by server."),
+                        doing);
+       }
+       break;
+    case NE_SOCK_TIMEOUT:
+       ne_set_error(sess, _("%s: connection timed out."), doing);
+       ret = NE_TIMEOUT;
+       break;
+    case NE_SOCK_ERROR:
+    case NE_SOCK_RESET:
+    case NE_SOCK_TRUNC:
+        ne_set_error(sess, "%s: %s", doing, ne_sock_error(sess->socket));
+        break;
+    case 0:
+       ne_set_error(sess, "%s", doing);
+       break;
     }
-    
-    *pnt = '\0';
 
+    ne_close_connection(sess);
     return ret;
 }
 
@@ -167,42 +248,101 @@ void ne_handle_numeric_header(void *userdata, const char *value)
     *location = atoi(value);
 }
 
-void ne_add_hooks(ne_session *sess, 
-                   const ne_request_hooks *hooks, void *private,
-                   ne_free_hooks free_hooks)
+static void *get_private(const struct hook *hk, const char *id)
+{
+    for (; hk != NULL; hk = hk->next)
+       if (strcmp(hk->id, id) == 0)
+           return hk->userdata;
+    return NULL;
+}
+
+void *ne_get_request_private(ne_request *req, const char *id)
 {
-    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;
+    return get_private(req->private, id);
 }
 
-void *ne_request_hook_private(ne_request *req, const char *id)
+void *ne_get_session_private(ne_session *sess, const char *id)
 {
-    struct hook_request *hk;
+    return get_private(sess->private, id);
+}
 
-    for (hk = req->hook_store; hk != NULL; hk = hk->next) {
-       if (strcasecmp(hk->hook->hooks->id, id) == 0) {
-           return hk->cookie;
-       }
+typedef void (*void_fn)(void);
+
+#define ADD_HOOK(hooks, fn, ud) add_hook(&(hooks), NULL, (void_fn)(fn), (ud))
+
+static void add_hook(struct hook **hooks, const char *id, void_fn fn, void *ud)
+{
+    struct hook *hk = ne_malloc(sizeof (struct hook)), *pos;
+
+    if (*hooks != NULL) {
+       for (pos = *hooks; pos->next != NULL; pos = pos->next)
+           /* nullop */;
+       pos->next = hk;
+    } else {
+       *hooks = hk;
     }
 
-    return NULL;
+    hk->id = id;
+    hk->fn = fn;
+    hk->userdata = ud;
+    hk->next = NULL;
 }
 
-void *ne_session_hook_private(ne_session *sess, const char *id)
+void ne_hook_create_request(ne_session *sess, 
+                           ne_create_request_fn fn, void *userdata)
 {
-    struct hook *hk;
+    ADD_HOOK(sess->create_req_hooks, fn, userdata);
+}
 
-    for (hk = sess->hooks; hk != NULL; hk = hk->next) {
-       if (strcasecmp(hk->hooks->id, id) == 0) {
-           return hk->private;
-       }
+void ne_hook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata)
+{
+    ADD_HOOK(sess->pre_send_hooks, fn, userdata);
+}
+
+void ne_hook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata)
+{
+    ADD_HOOK(sess->post_send_hooks, fn, userdata);
+}
+
+void ne_hook_destroy_request(ne_session *sess,
+                            ne_destroy_req_fn fn, void *userdata)
+{
+    ADD_HOOK(sess->destroy_req_hooks, fn, userdata);    
+}
+
+void ne_hook_destroy_session(ne_session *sess,
+                            ne_destroy_sess_fn fn, void *userdata)
+{
+    ADD_HOOK(sess->destroy_sess_hooks, fn, userdata);
+}
+
+/* 0.24.x hack to fix ne_compress layer problems */
+void ne_kill_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata)
+{
+    struct hook **last, *hk;
+
+    last = &sess->pre_send_hooks;
+    hk = *last;
+
+    while (hk) {
+        if (hk->fn == (void_fn)fn && hk->userdata == userdata) {
+            *last = hk->next;
+            ne_free(hk);
+            return;
+        }
+        last = &hk->next;
+        hk = *last;
     }
+}
 
-    return NULL;
+void ne_set_session_private(ne_session *sess, const char *id, void *userdata)
+{
+    add_hook(&sess->private, id, NULL, userdata);
+}
+
+void ne_set_request_private(ne_request *req, const char *id, void *userdata)
+{
+    add_hook(&req->private, id, NULL, userdata);
 }
 
 static ssize_t body_string_send(void *userdata, char *buffer, size_t count)
@@ -210,16 +350,16 @@ 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;
+       req->body.buf.left = req->body_size;
+       req->body.buf.pnt = req->body.buf.buffer;
     } else {
        /* if body_left == 0 we fall through and return 0. */
-       if (req->body_left < count)
-           count = req->body_left;
+       if (req->body.buf.left < count)
+           count = req->body.buf.left;
 
-       memcpy(buffer, req->body_pnt, count);
-       req->body_pnt += count;
-       req->body_left -= count;
+       memcpy(buffer, req->body.buf.pnt, count);
+       req->body.buf.pnt += count;
+       req->body.buf.left -= count;
     }
 
     return count;
@@ -230,10 +370,10 @@ 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);
+       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);
+       return lseek(req->body.fd, SEEK_SET, 0);
     }
 }
 
@@ -243,15 +383,23 @@ int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud)
 {
     int ret = 0;
     char buffer[BUFSIZ];
-    size_t bytes;
+    ssize_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) {
+    /* TODO: should this attempt to pull exactly the number of bytes
+     * they specified were in the body? Currently it just pulls until
+     * they return zero. That makes it possible to extend this to do
+     * chunked request bodies (i.e. indefinitely long, no C-L), so
+     * this is probably a better long-term interface. */
+    while ((bytes = req->body_cb(req->body_ud, buffer, sizeof buffer)) > 0) {
        ret = fn(ud, buffer, bytes);
        if (ret < 0)
            break;
+       NE_DEBUG(NE_DBG_HTTPBODY, 
+                "Body block (%" NE_FMT_SSIZE_T " bytes):\n[%.*s]\n",
+                bytes, (int)bytes, buffer);
     }
 
     if (bytes < 0) {
@@ -262,19 +410,38 @@ int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud)
     return ret;
 }
 
+static int send_with_progress(void *userdata, const char *data, size_t n)
+{
+    ne_request *req = userdata;
+    int ret;
+    
+    ret = ne_sock_fullwrite(req->session->socket, data, n);
+    if (ret == 0) {
+       req->body_progress += n;
+       req->session->progress_cb(req->session->progress_ud,
+                                 req->body_progress, req->body_size);
+    }
+    
+    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, 
+    int ret; 
+    NE_DEBUG(NE_DBG_HTTP, "Sending request body...\n");
+    if (req->session->progress_cb) {
+       /* with progress callbacks. */
+       req->body_progress = 0;
+       ret = ne_pull_request_body(req, send_with_progress, req);
+    } else {
+       /* without progress callbacks. */
+       ret = ne_pull_request_body(req, (ne_push_fn)ne_sock_fullwrite,
                                   req->session->socket);
-    
-    if (ret < 0) {
-       /* transfer failed */
-       req->forced_close = 1;
-       ret = set_sockerr(req, _("Could not send request body"), ret);
     }
 
+    NE_DEBUG(NE_DBG_HTTP, "Request body sent: %s.\n", ret?"failed":"okay");
     return ret;
 }
 
@@ -283,67 +450,44 @@ static int send_request_body(ne_request *req)
 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);
+        ne_buffer_zappend(req->headers, req->session->user_agent);
     }
-    /* 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");
+
+    /* Send Connection: Keep-Alive to pre-1.1 origin servers to try
+     * harder to get a persistent connection, except if using a proxy
+     * as per 2068 sec 19.7.1.  Always add TE: trailers since those
+     * are understood. */
+    if (!req->session->is_http11 && !req->session->use_proxy) {
+       ne_buffer_zappend(req->headers, 
+                          "Keep-Alive: " EOL
+                          "Connection: TE, Keep-Alive" EOL
+                          "TE: trailers" EOL);
     } 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);
+       ne_buffer_zappend(req->headers, 
+                          "Connection: TE" EOL
+                          "TE: trailers" 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)
+int ne_accept_always(void *userdata, ne_request *req, const ne_status *st)
 {
     return 1;
 }                                 
 
-int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st)
+int ne_accept_2xx(void *userdata, ne_request *req, const 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 */
+/* Handler for the "Transfer-Encoding" response header: treat *any*
+ * such header as implying a chunked response, per the "Protocol
+ * Compliance" statement in the manual. */
 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;
-    }
+
+    resp->mode = R_CHUNKED;    
 }
 
 /* Handler for the "Connection" response header */
@@ -351,79 +495,63 @@ static void connection_hdr_handler(void *userdata, const char *value)
 {
     ne_request *req = userdata;
     if (strcasecmp(value, "close") == 0) {
-       req->forced_close = 1;
+       req->can_persist = 0;
     } 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. */
+static void clength_hdr_handler(void *userdata, const char *value)
+{
+    struct ne_response *resp = userdata;
+    size_t len = strtoul(value, NULL, 10);
+    if (len != ULONG_MAX && resp->mode == R_TILLEOF) {
+       resp->mode = R_CLENGTH;
+       resp->length = len;
+    }
+}
+
 ne_request *ne_request_create(ne_session *sess,
-                             const char *method, const char *uri
+                             const char *method, const char *path
 {
-    ne_request *req = ne_calloc(sizeof(ne_request));
+    ne_request *req = ne_calloc(sizeof *req);
 
     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;
-    }
+    req->method = ne_strdup(method);
+    req->method_is_head = (strcmp(method, "HEAD") == 0);
 
     /* Add in handlers for all the standard HTTP headers. */
-
     ne_add_response_header_handler(req, "Content-Length", 
-                                     ne_handle_numeric_header, &req->resp.length);
+                                  clength_hdr_handler, &req->resp);
     ne_add_response_header_handler(req, "Transfer-Encoding", 
-                                     te_hdr_handler, &req->resp);
+                                  te_hdr_handler, &req->resp);
     ne_add_response_header_handler(req, "Connection", 
-                                     connection_hdr_handler, req);
+                                  connection_hdr_handler, req);
 
-    if (uri) {
-       ne_set_request_uri(req, uri);
-    }
+    /* Only use an absoluteURI here when absolutely necessary: some
+     * servers can't parse them. */
+    if (req->session->use_proxy && !req->session->use_ssl && path[0] == '/')
+       req->uri = ne_concat(req->session->scheme, "://", 
+                            req->session->server.hostport, path, NULL);
+    else
+       req->uri = ne_strdup(path);
 
     {
        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;
-           }
+       for (hk = sess->create_req_hooks; hk != NULL; hk = hk->next) {
+           ne_create_request_fn fn = (ne_create_request_fn)hk->fn;
+           fn(req, hk->userdata, method, req->uri);
        }
     }
 
@@ -435,13 +563,13 @@ ne_request *ne_request_create(ne_session *sess,
 static void set_body_size(ne_request *req, size_t size)
 {
     req->body_size = size;
-    ne_print_request_header(req, "Content-Length", "%d", size);
+    ne_print_request_header(req, "Content-Length", "%" NE_FMT_SIZE_T, size);
 }
 
 void ne_set_request_body_buffer(ne_request *req, const char *buffer,
                                size_t size)
 {
-    req->body_buffer = buffer;
+    req->body.buf.buffer = buffer;
     req->body_cb = body_string_send;
     req->body_ud = req;
     set_body_size(req, size);
@@ -461,14 +589,14 @@ int ne_set_request_body_fd(ne_request *req, int fd)
 
     /* 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);
+       char err[200];
+       ne_strerror(errno, err, sizeof err);
+       ne_set_error(req->session, _("Could not determine file length: %s"),
+                    err);
        NE_DEBUG(NE_DBG_HTTP, "Stat failed: %s\n", err);
        return -1;
     }
-    req->body_fd = fd;
+    req->body.fd = fd;
     req->body_cb = body_fd_send;
     req->body_ud = req;
     set_body_size(req, bodyst.st_size);
@@ -476,19 +604,19 @@ int ne_set_request_body_fd(ne_request *req, int fd)
 }
 
 void ne_add_request_header(ne_request *req, const char *name, 
-                            const char *value)
+                          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, ...)
+                            const char *format, ...)
 {
     va_list params;
     char buf[BUFSIZ];
     
     va_start(params, format);
-    vsnprintf(buf, BUFSIZ, format, params);
+    ne_vsnprintf(buf, sizeof buf, format, params);
     va_end(params);
     
     ne_buffer_concat(req->headers, name, ": ", buf, EOL, NULL);
@@ -496,33 +624,32 @@ void ne_print_request_header(ne_request *req, const char *name,
 
 void
 ne_add_response_header_handler(ne_request *req, const char *name, 
-                                ne_header_handler hdl, void *userdata)
+                              ne_header_handler hdl, void *userdata)
 {
     struct header_handler *new = ne_calloc(sizeof *new);
-    int hash;
-    new->name = lower_string(name);
+    unsigned int hash;
+    new->name = ne_strdup(name);
     new->handler = hdl;
     new->userdata = userdata;
-    hash = hdr_hash(new->name);
+    hash = hash_and_lower(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)
+                                   ne_header_handler hdl, void *userdata)
 {
-    struct header_handler *new = ne_calloc(sizeof  *new);
+    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)
+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));
+    struct body_reader *new = ne_malloc(sizeof *new);
     new->accept_response = acpt;
     new->handler = rdr;
     new->userdata = userdata;
@@ -534,46 +661,49 @@ 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;
+    struct hook *hk, *next_hk;
     int n;
 
-    NE_FREE(req->uri);
-    NE_FREE(req->abs_path);
+    ne_free(req->uri);
+    ne_free(req->method);
 
     for (rdr = req->body_readers; rdr != NULL; rdr = next_rdr) {
        next_rdr = rdr->next;
-       free(rdr);
+       ne_free(rdr);
     }
 
     for (hdlr = req->header_catchers; hdlr != NULL; hdlr = next_hdlr) {
        next_hdlr = hdlr->next;
-       free(hdlr);
+       ne_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_free(hdlr->name);
+           ne_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);
+    for (hk = req->session->destroy_req_hooks; hk; hk = hk->next) {
+       ne_destroy_req_fn fn = (ne_destroy_req_fn)hk->fn;
+       fn(req, hk->userdata);
     }
 
+    for (hk = req->private; hk; hk = next_hk) {
+       next_hk = hk->next;
+       ne_free(hk);
+    }
+
+    if (req->status.reason_phrase)
+       ne_free(req->status.reason_phrase);
+
     NE_DEBUG(NE_DBG_HTTP, "Request ends.\n");
-    free(req);
+    ne_free(req);
 }
 
 
@@ -584,9 +714,11 @@ void ne_request_destroy(ne_request *req)
 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) {
+    size_t willread;
+    ssize_t readlen;
+    ne_socket *sock = req->session->socket;
+    switch (resp->mode) {
+    case R_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.
@@ -596,21 +728,21 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
         * bytes left to read in the current chunk.
         */
        if (resp->chunk_left == 0) {
-           long int chunk_len;
+           unsigned long int chunk_len;
+           char *ptr;
            /* 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);
-           }
+           SOCK_ERR(req, ne_sock_readline(sock, buffer, *buflen),
+                    _("Could not read chunk size"));
            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;
+           chunk_len = strtoul(buffer, &ptr, 16);
+           /* limit chunk size to <= UINT_MAX, so it will probably
+            * fit in a size_t. */
+           if (ptr == buffer || 
+               chunk_len == ULONG_MAX || chunk_len > UINT_MAX) {
+               return aborted(req, _("Could not parse chunk size"), 0);
            }
-           NE_DEBUG(NE_DBG_HTTP, "Got chunk size: %ld\n", chunk_len);
+           NE_DEBUG(NE_DBG_HTTP, "Got chunk size: %lu\n", chunk_len);
            if (chunk_len == 0) {
                /* Zero-size chunk == end of response. */
                NE_DEBUG(NE_DBG_HTTP, "Zero-size chunk.\n");
@@ -619,48 +751,59 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
            }
            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;
+       willread = resp->chunk_left;
+       break;
+    case R_CLENGTH:
+       willread = resp->left;
+       break;
+    case R_TILLEOF:
+       willread = *buflen;
+       break;
+    case R_NO_BODY:
+    default:
+       willread = 0;
+       break;
     }
-    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) {
+    if (willread > *buflen) willread = *buflen;
+    else if (willread == 0) {
+       *buflen = 0;
+       return 0;
+    }
+    NE_DEBUG(NE_DBG_HTTP,
+            "Reading %" NE_FMT_SIZE_T " bytes of response body.\n", willread);
+    readlen = ne_sock_read(sock, buffer, willread);
+
+    /* EOF is only valid when response body is delimited by it.
+     * Strictly, an SSL truncation should not be treated as an EOF in
+     * any case, but SSL servers are just too buggy.  */
+    if (resp->mode == R_TILLEOF && 
+       (readlen == NE_SOCK_CLOSED || readlen == NE_SOCK_TRUNC)) {
        NE_DEBUG(NE_DBG_HTTP, "Got EOF.\n");
+       req->can_persist = 0;
        readlen = 0;
     } else if (readlen < 0) {
-       return set_sockerr(req, _("Could not read response body"), readlen);
+       return aborted(req, _("Could not read response body"), readlen);
     } else {
-       NE_DEBUG(NE_DBG_HTTP, "Got %d bytes.\n", readlen);
+       NE_DEBUG(NE_DBG_HTTP, "Got %" NE_FMT_SSIZE_T " bytes.\n", readlen);
     }
-    buffer[readlen] = '\0';
-    *buflen = readlen;
-    NE_DEBUG(NE_DBG_HTTPBODY, "Read block:\n%s\n", buffer);
-    if (resp->is_chunked) {
+    /* safe to cast: readlen guaranteed to be >= 0 above */
+    *buflen = (size_t)readlen;
+    NE_DEBUG(NE_DBG_HTTPBODY,
+            "Read block (%" NE_FMT_SSIZE_T " bytes):\n[%.*s]\n",
+            readlen, (int)readlen, buffer);
+    if (resp->mode == R_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);
-           }
+           readlen = ne_sock_fullread(sock, crlfbuf, 2);
+            if (readlen < 0)
+                return aborted(req, _("Could not read chunk delimiter"),
+                               readlen);
+            else if (crlfbuf[0] != '\r' || crlfbuf[1] != '\n')
+                return aborted(req, _("Chunk delimiter was invalid"), 0);
        }
-    } else if (resp->length > 0) {
+    } else if (resp->mode == R_CLENGTH) {
        resp->left -= readlen;
     }
     return NE_OK;
@@ -671,272 +814,201 @@ 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;  
+    if (read_response_block(req, &req->resp, buffer, &readlen))
        return -1;
-    }
+
+    req->resp.total += readlen;
 
     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);
+                                 (req->resp.mode==R_CLENGTH)?req->resp.length:-1);
     }
 
     /* 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);
+       if (rdr->use) rdr->handler(rdr->userdata, buffer, readlen);
     }
     
     return readlen;
 }
 
-/* Build the request. */
-static const char *build_request(ne_request *req) 
+/* Build the request string, returning the buffer. */
+static ne_buffer *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);
+    struct hook *hk;
+    ne_buffer *buf = ne_buffer_create();
 
-    /* Add in the request and the user-supplied headers */
-    ne_buffer_concat(buf, req->method, " ", uri, " HTTP/1.1" EOL,
-                  req->headers->data, NULL);
+    /* Add Request-Line and Host header: */
+    ne_buffer_concat(buf, req->method, " ", req->uri, " HTTP/1.1" EOL,
+                    "Host: ", req->session->server.hostport, EOL, 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);
+    /* Add custom headers: */
+    ne_buffer_append(buf, req->headers->data, ne_buffer_size(req->headers));
 
-    
-    /* 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;
-    }
+#define E100 "Expect: 100-continue" EOL
+    if (req->use_expect100)
+       ne_buffer_append(buf, E100, strlen(E100));
 
     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);
-       }
-    }                                              
+    for (hk = req->session->pre_send_hooks; hk!=NULL; hk = hk->next) {
+       ne_pre_send_fn fn = (ne_pre_send_fn)hk->fn;
+       fn(req, hk->userdata, buf);
+    }
     
-    /* Final CRLF */
-    ne_buffer_zappend(buf, EOL);
-
-    return buf->data;
+    ne_buffer_append(buf, "\r\n", 2);
+    return buf;
 }
 
-/* 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 {
+#ifdef NE_DEBUGGING
+#define DEBUG_DUMP_REQUEST(x) dump_request(x)
 
-       /* 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);
+static void dump_request(const char *request)
+{ 
+    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);
+       ne_free(reqdebug);
+    }
+}
+
+#else
+#define DEBUG_DUMP_REQUEST(x)
 #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;
-           }
+/* remove trailing EOL from 'buf', where strlen(buf) == *len.  *len is
+ * adjusted in accordance with any changes made to the string to
+ * remain equal to strlen(buf). */
+static inline void strip_eol(char *buf, ssize_t *len)
+{
+    char *pnt = &buf[*len-1];
+    while (pnt >= buf && (*pnt == '\r' || *pnt == '\n')) {
+       *pnt-- = '\0';
+       (*len)--;
+    }
+}
 
-           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;
-           }
+/* For accurate persistent connection handling, for any write() or
+ * read() operation for a new request on an already-open connection,
+ * an EOF or RST error MUST be treated as a persistent connection
+ * timeout, and the request retried on a new connection.  Once a
+ * read() operation has succeeded, any subsequent error MUST be
+ * treated as fatal.  A 'retry' flag is used; retry=1 represents the
+ * first case, retry=0 the latter. */
+
+/* RETRY_RET() crafts a function return value given the 'retry' flag,
+ * the socket error 'code', and the return value 'acode' from the
+ * aborted() function. */
+#define RETRY_RET(retry, code, acode) \
+((((code) == NE_SOCK_CLOSED || (code) == NE_SOCK_RESET || \
+ (code) == NE_SOCK_TRUNC) && retry) ? NE_RETRY : (acode))
+
+/* Read and parse response status-line into 'status'.  'retry' is non-zero
+ * if an NE_RETRY should be returned if an EOF is received. */
+static int read_status_line(ne_request *req, ne_status *status, int retry)
+{
+    char *buffer = req->respbuf;
+    ssize_t ret;
 
-           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);
+    ret = ne_sock_readline(req->session->socket, buffer, sizeof req->respbuf);
+    if (ret <= 0) {
+       int aret = aborted(req, _("Could not read status line"), ret);
+       return RETRY_RET(retry, ret, aret);
+    }
+    
+    NE_DEBUG(NE_DBG_HTTP, "[status-line] < %s", buffer);
+    strip_eol(buffer, &ret);
+    
+    if (status->reason_phrase) ne_free(status->reason_phrase);
+    memset(status, 0, sizeof *status);
 
-       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);
-       }
+    if (ne_parse_statusline(buffer, status))
+       return aborted(req, _("Could not parse response status line."), 0);
 
-    } while (try_again == 1);
+    return 0;
+}
 
+/* Discard a set of message headers. */
+static int discard_headers(ne_request *req)
+{
+    do {
+       SOCK_ERR(req, ne_sock_readline(req->session->socket, req->respbuf, 
+                                      sizeof req->respbuf),
+                _("Could not read interim response headers"));
+       NE_DEBUG(NE_DBG_HTTP, "[discard] < %s", req->respbuf);
+    } while (strcmp(req->respbuf, EOL) != 0);
     return NE_OK;
 }
 
+/* Send the request, and read the response Status-Line. Returns:
+ *   NE_RETRY   connection closed by server; persistent connection
+ *             timeout
+ *   NE_OK     success
+ *   NE_*      error
+ * On NE_RETRY and NE_* responses, the connection will have been 
+ * closed already.
+ */
+static int send_request(ne_request *req, const ne_buffer *request)
+{
+    ne_session *sess = req->session;
+    ssize_t ret = NE_OK;
+    int sentbody = 0; /* zero until body has been sent. */
+    int retry; /* non-zero whilst the request should be retried */
+    ne_status *status = &req->status;
+
+    /* Send the Request-Line and headers */
+    NE_DEBUG(NE_DBG_HTTP, "Sending request-line and headers:\n");
+    /* Open the connection if necessary */
+    HTTP_ERR(open_connection(req));
+
+    /* Allow retry if a persistent connection has been used. */
+    retry = sess->persisted;
+    
+    ret = ne_sock_fullwrite(req->session->socket, request->data, 
+                           ne_buffer_size(request));
+    if (ret < 0) {
+       int aret = aborted(req, _("Could not send request"), ret);
+       return RETRY_RET(retry, ret, aret);
+    }
+    
+    if (!req->use_expect100 && req->body_size > 0) {
+       /* Send request body, if not using 100-continue. */
+       ret = send_request_body(req);
+       if (ret < 0) {
+           int aret = aborted(req, _("Could not send request body"), ret);
+           return RETRY_RET(sess, ret, aret);
+       }
+    }
+    
+    NE_DEBUG(NE_DBG_HTTP, "Request sent; retry is %d.\n", retry);
+
+    /* Loop eating interim 1xx responses (RFC2616 says these MAY be
+     * sent by the server, even if 100-continue is not used). */
+    while ((ret = read_status_line(req, status, retry)) == NE_OK 
+          && status->klass == 1) {
+       NE_DEBUG(NE_DBG_HTTP, "Interim %d response.\n", status->code);
+       retry = 0; /* successful read() => never retry now. */
+       /* Discard headers with the interim response. */
+       if ((ret = discard_headers(req)) != NE_OK) break;
+
+       if (req->use_expect100 && (status->code == 100) && !sentbody) {
+           /* Send the body after receiving the first 100 Continue */
+           if ((ret = send_request_body(req)) != NE_OK) break;     
+           sentbody = 1;
+       }
+    }
+
+    return ret;
+}
+
 /* Read a message header from sock into buf, which has size 'buflen'.
  *
  * Returns:
@@ -946,186 +1018,204 @@ static int send_request(ne_request *req)
  */
 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;
+    ssize_t n;
+    ne_socket *sock = req->session->socket;
 
-    n = sock_readline(sock, buf, buflen);
+    n = ne_sock_readline(sock, buf, buflen);
     if (n <= 0)
-       return set_sockerr(req, _("Error reading response headers"), n);
+       return aborted(req, _("Error reading response headers"), n);
     NE_DEBUG(NE_DBG_HTTP, "[hdr] %s", buf);
 
-    STRIP_EOL(buf);
+    strip_eol(buf, &n);
 
-    len = strlen(buf);
-
-    if (len == 0) {
+    if (n == 0) {
        NE_DEBUG(NE_DBG_HTTP, "End of headers.\n");
        return NE_OK;
     }
 
+    buf += n;
+    buflen -= n;
+
     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);
-       }
+       SOCK_ERR(req, ne_sock_peek(sock, &ch, 1),
+                _("Error reading response headers"));
+
        if (ch != ' ' && ch != '\t') {
-           /* No continuation of this header. NUL-terminate to be paranoid. */
+           /* No continuation of this header: stop reading. */
            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);
+       n = ne_sock_readline(sock, buf, buflen);
+       if (n <= 0) {
+           return aborted(req, _("Error reading response headers"), n);
        }
 
        NE_DEBUG(NE_DBG_HTTP, "[cont] %s", buf);
 
-       STRIP_EOL(buf);
-       len = strlen(buf);
+       strip_eol(buf, &n);
        
        /* 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] = ' ';
+       if (n) buf[0] = ' ';
+
+       /* ready for the next header. */
+       buf += n;
+       buflen -= n;
     }
 
     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. */
+/* Read response headers.  Returns NE_* code, sets session error. */
 static int read_response_headers(ne_request *req) 
 {
-    char hdr[MAX_HEADER_LENGTH] = {0};
+    char hdr[8192]; /* max header length */
     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 
+    while ((ret = read_message_header(req, hdr, sizeof hdr)) == 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);
-       }
+       char *pnt;
+       unsigned int hash = 0;
        
-       /* 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++) {
+       for (hdl = req->header_catchers; hdl != NULL; hdl = hdl->next)
+           hdl->handler(hdl->userdata, hdr);
+
+       /* Strip any trailing whitespace */
+       pnt = hdr + strlen(hdr) - 1;
+       while (pnt > hdr && (*pnt == ' ' || *pnt == '\t'))
+           *pnt-- = '\0';
+
+       /* Convert the header name to lower case and hash it. */
+       for (pnt = hdr; (*pnt != '\0' && *pnt != ':' && 
+                        *pnt != ' ' && *pnt != '\t'); 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;
+       /* Skip over any whitespace before the colon. */
+       while (*pnt == ' ' || *pnt == '\t')
+           *pnt++ = '\0';
+
+       /* ignore header lines which lack a ':'. */
+       if (*pnt != ':')
+           continue;
+       
+       /* NUL-terminate at the colon (when no whitespace before) */
+       *pnt++ = '\0';
+
+       /* Skip any whitespace after the colon... */
+       while (*pnt == ' ' || *pnt == '\t')
+           pnt++;
+
+       /* pnt now points to the header value. */
+       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);
        }
     }
 
-    if (count == MAX_HEADER_FIELDS) {
-       ne_set_error(req->session, 
-                      _("Response exceeded maximum number of header fields."));
-       ret = NE_ERROR;
-    }
+    if (count == MAX_HEADER_FIELDS)
+       ret = aborted(
+           req, _("Response exceeded maximum number of header fields."), 0);
 
     return ret;
 }
 
+static int lookup_host(ne_session *sess, struct host_info *info)
+{
+    NE_DEBUG(NE_DBG_HTTP, "Doing DNS lookup on %s...\n", info->hostname);
+    if (sess->notify_cb)
+       sess->notify_cb(sess->notify_ud, ne_conn_namelookup, info->hostname);
+    info->address = ne_addr_resolve(info->hostname, 0);
+    if (ne_addr_result(info->address)) {
+       char buf[256];
+       ne_set_error(sess, _("Could not resolve hostname `%s': %s"), 
+                    info->hostname,
+                    ne_addr_error(info->address, buf, sizeof buf));
+       ne_addr_destroy(info->address);
+       info->address = NULL;
+       return NE_LOOKUP;
+    } else {
+       return NE_OK;
+    }
+}
+
 int ne_begin_request(ne_request *req)
 {
     struct body_reader *rdr;
+    struct host_info *host;
+    ne_buffer *data;
+    const ne_status *const st = &req->status;
+    int ret;
 
-    NE_DEBUG(NE_DBG_HTTPBASIC, "Request: %s %s\n", req->method, req->uri);
+    /* Resolve hostname if necessary. */
+    host = req->session->use_proxy?&req->session->proxy:&req->session->server;
+    if (host->address == NULL)
+       HTTP_ERR(lookup_host(req->session, host));
 
-    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));
+    req->resp.mode = R_TILLEOF;
     
+    /* FIXME: Determine whether to use the Expect: 100-continue header. */
+    req->use_expect100 = (req->session->expect100_works > -1) &&
+       (req->body_size > HTTP_EXPECT_MINSIZE) && req->session->is_http11;
+
+    /* Build the request string, and send it */
+    data = build_request(req);
+    DEBUG_DUMP_REQUEST(data->data);
+    ret = send_request(req, data);
+    /* Retry this once after a persistent connection timeout. */
+    if (ret == NE_RETRY && !req->session->no_persist) {
+       NE_DEBUG(NE_DBG_HTTP, "Persistent connection timed out, retrying.\n");
+       ret = send_request(req, data);
+    }
+    ne_buffer_destroy(data);
+    if (ret != NE_OK) return ret;
+
+    /* Determine whether server claims HTTP/1.1 compliance. */
+    req->session->is_http11 = (st->major_version == 1 && 
+                               st->minor_version > 0) || st->major_version > 1;
+
+    /* Persistent connections supported implicitly in HTTP/1.1 */
+    if (req->session->is_http11) req->can_persist = 1;
+
+    ne_set_error(req->session, "%d %s", st->code, st->reason_phrase);
+
     /* Read the headers */
     HTTP_ERR(read_response_headers(req));
 
-    /* response message logic */
-    normalize_response_length(req);
+#ifdef NEON_SSL
+    /* Special case for CONNECT handling: the response has no body,
+     * and the connection can persist. */
+    if (req->session->in_connect && st->klass == 2) {
+       req->resp.mode = R_NO_BODY;
+       req->can_persist = 1;
+    }
+#endif
+
+    /* HEAD requests and 204, 205, 304 responses have no response body,
+     * regardless of what headers are present. */
+    if (req->method_is_head || st->code==204 || st->code==205 || st->code==304)
+       req->resp.mode = R_NO_BODY;
 
-    /* Prepare for reading the response entity-body.  call each of the
+    /* 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);
+       rdr->use = rdr->accept_response(rdr->userdata, req, st);
     }
     
     req->resp.left = req->resp.length;
@@ -1136,70 +1226,45 @@ int ne_begin_request(ne_request *req)
 
 int ne_end_request(ne_request *req)
 {
-    struct hook_request *st;
+    struct hook *hk;
     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) {
+    if (req->resp.mode == R_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);
-       }
+    for (hk = req->session->post_send_hooks; 
+        ret == NE_OK && hk != NULL; hk = hk->next) {
+       ne_post_send_fn fn = (ne_post_send_fn)hk->fn;
+       ret = fn(req, hk->userdata, &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))) {
+    /* Close the connection if persistent connections are disabled or
+     * not supported by the server. */
+    if (req->session->no_persist || !req->can_persist)
        ne_close_connection(req->session);
-    }
+    else
+       req->session->persisted = 1;
     
-    /* 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];
+       ssize_t len;
        
        HTTP_ERR(ne_begin_request(req));
        
        do {
-           len = ne_read_response_block(req, buffer, BUFSIZ);
+           len = ne_read_response_block(req, req->respbuf,
+                                        sizeof req->respbuf);
        } while (len > 0);
 
        if (len < 0) {
@@ -1217,124 +1282,129 @@ int ne_request_dispatch(ne_request *req)
     return ret;
 }
 
-const ne_status *ne_get_status(ne_request *req)
+const ne_status *ne_get_status(const ne_request *req)
+{
+    return &req->status;
+}
+
+ne_session *ne_get_session(const ne_request *req)
 {
-    return &(req->status);
+    return req->session;
 }
 
+#ifdef NEON_SSL
 /* 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);
+    ne_request *req;
     int ret = NE_OK;
+    char ruri[200];
 
-    /* Fudge the URI to be how we want it */
-    req->uri = ne_strdup(sess->server.hostport);
+    /* Can't use server.hostport here; Request-URI must include `:port' */
+    ne_snprintf(ruri, sizeof ruri, "%s:%u", sess->server.hostname,  
+               sess->server.port);
+    req = ne_request_create(sess, "CONNECT", ruri);
 
-    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"));
+    sess->persisted = 0; /* don't treat this is a persistent connection. */
+
+    if (ret != NE_OK || !sess->connected || req->status.klass != 2) {
+       ne_set_error
+           (sess, _("Could not create SSL connection through proxy server"));
        ret = NE_ERROR;
     }
 
     ne_request_destroy(req);
-    
     return ret;
 }
+#endif
 
-static int open_connection(ne_request *req) 
+/* Make new TCP connection to server at 'host' of type 'name'.  Note
+ * that once a connection to a particular network address has
+ * succeeded, that address will be used first for the next attempt to
+ * connect. */
+/* TODO: an alternate implementation could always cycle through the
+ * addresses: this could ease server load, but could hurt SSL session
+ * caching for SSL sessions, which would increase server load. */
+static int do_connect(ne_request *req, struct host_info *host, const char *err)
 {
-    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);
+    ne_session *const sess = req->session;
+    int ret;
 
-       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;
-       }
+    if ((sess->socket = ne_sock_create()) == NULL) {
+        ne_set_error(sess, _("Could not create socket"));
+        return NE_ERROR;
+    }
 
-       notify_status(sess, ne_conn_connected, sess->server.hostport);
+    if (host->current == NULL)
+       host->current = ne_addr_first(host->address);
 
-       if (sess->progress_cb) {
-           sock_register_progress(sess->socket, sess->progress_cb,
-                                  sess->progress_ud);
+    do {
+       notify_status(sess, ne_conn_connecting, host->hostport);
+#ifdef NE_DEBUGGING
+       if (ne_debug_mask & NE_DBG_HTTP) {
+           char buf[150];
+           NE_DEBUG(NE_DBG_HTTP, "Connecting to %s\n",
+                    ne_iaddr_print(host->current, buf, sizeof buf));
        }
+#endif
+       ret = ne_sock_connect(sess->socket, host->current, host->port);
+    } while (ret && /* try the next address... */
+            (host->current = ne_addr_next(host->address)) != NULL);
+
+    if (ret) {
+        ne_set_error(sess, "%s: %s", err, ne_sock_error(sess->socket));
+        ne_sock_close(sess->socket);
+       return NE_CONNECT;
+    }
 
-       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_connected, sess->proxy.hostport);
+    
+    if (sess->rdtimeout)
+       ne_sock_read_timeout(sess->socket, sess->rdtimeout);
 
-           notify_status(sess, ne_conn_secure, sock_get_version(sess->socket));
+    sess->connected = 1;
+    /* clear persistent connection flag. */
+    sess->persisted = 0;
+    return NE_OK;
+}
 
-       }
+static int open_connection(ne_request *req) 
+{
+    ne_session *sess = req->session;
+    int ret;
+    
+    if (sess->connected) return NE_OK;
 
-       sess->connected = 1;
+    if (!sess->use_proxy)
+       ret = do_connect(req, &sess->server, _("Could not connect to server"));
+    else
+       ret = do_connect(req, &sess->proxy,
+                        _("Could not connect to proxy server"));
+
+    if (ret != NE_OK) return ret;
+
+#ifdef NEON_SSL
+    /* Negotiate SSL layer if required. */
+    if (sess->use_ssl && !sess->in_connect) {
+        /* CONNECT tunnel */
+        if (req->session->use_proxy)
+            ret = proxy_tunnel(sess);
+        
+        if (ret == NE_OK)
+            ret = ne_negotiate_ssl(req);
+
+        /* This is probably only really needed for ne_negotiate_ssl
+         * failures as proxy_tunnel will fail via aborted(). */
+        if (ret != NE_OK)
+            ne_close_connection(sess);
     }
-    return NE_OK;
+#endif
+    
+    return ret;
 }
index 336355f..e59b59d 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP Request Handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2002, 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
 #define NE_REQUEST_H
 
 #include "ne_utils.h" /* For ne_status */
-#include "ne_string.h" /* For sbuffer */
+#include "ne_string.h" /* For ne_buffer */
 #include "ne_session.h"
 
 BEGIN_NEON_DECLS
 
-#define NE_OK (0)
+#define NE_OK (0) /* Success */
 #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 NE_LOOKUP (2) /* Server or proxy hostname lookup failed */
+#define NE_AUTH (3) /* User authentication failed on server */
+#define NE_PROXYAUTH (4) /* User authentication failed on proxy */
+#define NE_CONNECT (5) /* Could not connect to server */
+#define NE_TIMEOUT (6) /* Connection timed out */
+#define NE_FAILED (7) /* The precondition failed */
+#define NE_RETRY (8) /* Retry request (ne_end_request ONLY) */
+#define NE_REDIRECT (9) /* See ne_redirect.h */
+
+/* FIXME: remove this */
 #define EOL "\r\n"
 
+/* Opaque object representing a single HTTP request. */
 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);
+/* Create a request in session 'sess', with given method and path.
+ * 'path' must conform to the 'abs_path' grammar in RFC2396, with an
+ * optional "? query" part, and MUST be URI-escaped by the caller. */
+ne_request *ne_request_create(ne_session *sess,
+                             const char *method, const char *path);
 
 /* '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:
+/* Send the contents of a file as the request body; 'fd' must be a
+ * file descriptor of an open, seekable file.  Current file offset of
+ * fd is not retained (and ignored: the file is read from the the
+ * first byte). Returns:
  *  0 on okay.
- *  non-zero if could not determine length of file.
- */
+ *  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.
+/* "Pull"-based request body provider: a callback which is invoked to
+ * provide blocks of request body on demand.
  *
  * 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           : error, abort request.
  *         0           : ignore 'buffer' contents, end of body.
- *     0 < x <= buflen : buffer contains x bytes of body data.
- */
+ *     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. */
+/* Install a callback which is invoked as needed to provide request
+ * body blocks.  Total request body is 'size' bytes: the callback MUST
+ * ensure it returns in total exactly 'size' bytes each time the
+ * request body is provided. */
 void ne_set_request_body_provider(ne_request *req, size_t size,
                                  ne_provide_body provider, void *userdata);
 
@@ -93,19 +98,18 @@ void ne_set_request_body_provider(ne_request *req, size_t size,
 /* '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);
+    void *userdata, ne_request *req, const 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);
+int ne_accept_2xx(void *userdata, ne_request *req, const ne_status *st);
 
 /* An acceptance callback which accepts all responses.  Ignores
  * userdata. */
-int ne_accept_always(void *userdata, ne_request *req, ne_status *st);
+int ne_accept_always(void *userdata, ne_request *req, const ne_status *st);
 
-/* The 'reader' callback type */
-typedef void (*ne_block_reader)(
-    void *userdata, const char *buf, size_t len);
+/* Callback for reading a block of data. */
+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
@@ -117,10 +121,9 @@ typedef void (*ne_block_reader)(
  * 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.
- */
+ * zero.  */
 void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
-                                ne_block_reader rdr, void *userdata);
+                                ne_block_reader reader, void *userdata);
 
 /* Handle response headers. Each handler is associated with a specific
  * header field (indicated by name). The handler is then passed the
@@ -158,46 +161,29 @@ void ne_add_request_header(ne_request *req, const char *name,
 /* 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__ */
-;
+                            const char *format, ...) 
+    ne_attribute((format(printf, 3, 4)));
 
 /* 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_AUTH       user not authorised on server
+ *  NE_PROXYAUTH  user not authorised on proxy server
  *  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)
+/* Returns a pointer to the response status information for the given
+ * request; pointer is valid until request object is destroyed. */
+const ne_status *ne_get_status(const ne_request *req) ne_attribute((const));
 
-/* 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__ */
-    ;
+/* Returns pointer to session associated with request. */
+ne_session *ne_get_session(const ne_request *req);
 
 /* Destroy memory associated with request pointer */
 void ne_request_destroy(ne_request *req);
@@ -208,9 +194,12 @@ void ne_request_destroy(ne_request *req);
  * caller must call:
  *  1. ne_begin_request (fail if returns non-NE_OK)
  *  2. while(ne_read_response_block(...) > 0) ... loop ...;
+ *     (fail if ne_read_response_block returns <0)
  *  3. ne_end_request
- * If ne_end_request returns NE_RETRY, MUST go back to 1.
- */
+ *
+ * ne_end_request and ne_begin_request both return an NE_* code; if
+ * ne_end_request returns NE_RETRY, you must restart the loop from (1)
+ * above. */
 int ne_begin_request(ne_request *req);
 int ne_end_request(ne_request *req);
 
@@ -226,44 +215,48 @@ 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'.
+/* Hook called when a create is created; passed the request method,
+ * and the string used as the Request-URI (which may be an abs_path,
+ * or an absoluteURI, depending on whether an HTTP proxy is in
+ * use).  */
+typedef void (*ne_create_request_fn)(ne_request *req, void *userdata,
+                                    const char *method, const char *requri);
+void ne_hook_create_request(ne_session *sess, 
+                           ne_create_request_fn fn, void *userdata);
+
+/* Hook called before the request is sent.  'header' is the raw HTTP
+ * header before the trailing CRLF is added: add in more here. */
+typedef void (*ne_pre_send_fn)(ne_request *req, void *userdata, 
+                              ne_buffer *header);
+void ne_hook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);
+
+/* Hook called after the request is sent. May return:
+ *  NE_OK     everything is okay
+ *  NE_RETRY  try sending the request again.
+ * anything else signifies an error, and the request is failed. The return
+ * code is passed back the _dispatch caller, so the session error must
+ * also be set appropriately (ne_set_error).
  */
-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);
+typedef int (*ne_post_send_fn)(ne_request *req, void *userdata,
+                              const ne_status *status);
+void ne_hook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata);
+
+/* Hook called when the function is destroyed. */
+typedef void (*ne_destroy_req_fn)(ne_request *req, void *userdata);
+void ne_hook_destroy_request(ne_session *sess,
+                            ne_destroy_req_fn fn, void *userdata);
+
+typedef void (*ne_destroy_sess_fn)(void *userdata);
+/* Hook called when the session is destroyed. */
+void ne_hook_destroy_session(ne_session *sess,
+                            ne_destroy_sess_fn fn, void *userdata);
+
+/* Store an opaque context for the request, 'priv' is returned by a
+ * call to ne_request_get_private with the same ID. */
+void ne_set_request_private(ne_request *req, const char *id, void *priv);
+void *ne_get_request_private(ne_request *req, const char *id);
 
 END_NEON_DECLS
 
index 9f730d5..04b777e 100644 (file)
@@ -1,6 +1,8 @@
 /* 
    HTTP session handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+   Portions are:
+   Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
 
 #include "ne_session.h"
 #include "ne_alloc.h"
 #include "ne_utils.h"
+#include "ne_i18n.h"
+#include "ne_string.h"
 
 #include "ne_private.h"
 
-#define NEON_USERAGENT "neon/" NEON_VERSION;
-
-/* Initializes an HTTP session */
-ne_session *ne_session_create(void) 
+/* Destroy a a list of hooks. */
+static void destroy_hooks(struct hook *hooks)
 {
-    ne_session *sess = ne_calloc(sizeof *sess);
+    struct hook *nexthk;
 
-    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;
+    while (hooks) {
+       nexthk = hooks->next;
+       ne_free(hooks);
+       hooks = nexthk;
+    }
 }
 
-int ne_session_destroy(ne_session *sess) 
+void 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;
+    /* Run the destroy hooks. */
+    for (hk = sess->destroy_sess_hooks; hk != NULL; hk = hk->next) {
+       ne_destroy_sess_fn fn = (ne_destroy_sess_fn)hk->fn;
+       fn(hk->userdata);
     }
-
-    NE_FREE(sess->server.hostname);
-    NE_FREE(sess->server.hostport);
-    NE_FREE(sess->proxy.hostport);
-    NE_FREE(sess->user_agent);
+    
+    destroy_hooks(sess->create_req_hooks);
+    destroy_hooks(sess->pre_send_hooks);
+    destroy_hooks(sess->post_send_hooks);
+    destroy_hooks(sess->destroy_req_hooks);
+    destroy_hooks(sess->destroy_sess_hooks);
+    destroy_hooks(sess->private);
+
+    ne_free(sess->scheme);
+    ne_free(sess->server.hostname);
+    ne_free(sess->server.hostport);
+    if (sess->server.address) ne_addr_destroy(sess->server.address);
+    if (sess->proxy.address) ne_addr_destroy(sess->proxy.address);
+    if (sess->proxy.hostname) ne_free(sess->proxy.hostname);
+    if (sess->user_agent) ne_free(sess->user_agent);
 
     if (sess->connected) {
        ne_close_connection(sess);
     }
 
-    free(sess);
-    return NE_OK;
+#ifdef NEON_SSL
+    if (sess->ssl_context)
+        ne_ssl_context_destroy(sess->ssl_context);
+
+    if (sess->server_cert)
+        ne_ssl_cert_free(sess->server_cert);
+    
+    if (sess->client_cert)
+        ne_ssl_clicert_free(sess->client_cert);
+#endif
+
+    ne_free(sess);
 }
 
 int ne_version_pre_http11(ne_session *s)
 {
-    return (s->version_major<1 || (s->version_major==1 && s->version_minor<1));
+    return !s->is_http11;
 }
 
-static char *get_hostport(struct host_info *host) 
+/* Stores the "hostname[:port]" segment */
+static void set_hostport(struct host_info *host, unsigned int defaultport)
 {
     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;
+    host->hostport = ne_malloc(len + 10);
+    strcpy(host->hostport, host->hostname);
+    if (host->port != defaultport)
+       ne_snprintf(host->hostport + len, 9, ":%u", host->port);
 }
 
+/* Stores the hostname/port in *info, setting up the "hostport"
+ * segment correctly. */
 static void
-set_hostinfo(struct host_info *info, const char *hostname, int port)
+set_hostinfo(struct host_info *info, const char *hostname, unsigned int port)
 {
-    NE_FREE(info->hostport);
-    NE_FREE(info->hostname);
-    info->hostname= ne_strdup(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)
+ne_session *ne_session_create(const char *scheme,
+                             const char *hostname, unsigned int port)
 {
-    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;
-    }
-}
+    ne_session *sess = ne_calloc(sizeof *sess);
 
-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;
-    }
-}
+    NE_DEBUG(NE_DBG_HTTP, "HTTP session to %s://%s:%d begins.\n",
+            scheme, hostname, port);
 
-void ne_set_secure_context(ne_session *sess, nssl_context *ctx)
-{
-    sess->ssl_context = ctx;
-}
+    strcpy(sess->error, "Unknown error.");
 
-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
-}
+    /* use SSL if scheme is https */
+    sess->use_ssl = !strcmp(scheme, "https");
+    
+    /* set the hostname/port */
+    set_hostinfo(&sess->server, hostname, port);
+    set_hostport(&sess->server, sess->use_ssl?443:80);
 
-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;
+#ifdef NEON_SSL
+    if (sess->use_ssl) {
+        sess->ssl_context = ne_ssl_context_create();
+    }
 #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
-}
+    sess->scheme = ne_strdup(scheme);
 
-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;
+    /* Default expect-100 to OFF. */
+    sess->expect100_works = -1;
+    return sess;
 }
 
-int ne_session_proxy(ne_session *sess, const char *hostname, int port)
+void ne_session_proxy(ne_session *sess, const char *hostname,
+                     unsigned int port)
 {
-    if (sess->connected) {
-       /* Force reconnect */
-       ne_close_connection(sess);
-    }
-    sess->have_proxy = 1;
+    sess->use_proxy = 1;
+    if (sess->proxy.hostname) ne_free(sess->proxy.hostname);
     set_hostinfo(&sess->proxy, hostname, port);
-    return lookup_host(sess, &sess->proxy);
 }
 
-void ne_set_error(ne_session *sess, const char *errstring)
+void ne_set_error(ne_session *sess, const char *format, ...)
 {
-    strncpy(sess->error, errstring, BUFSIZ);
-    sess->error[BUFSIZ-1] = '\0';
-    STRIP_EOL(sess->error);
+    va_list params;
+
+    va_start(params, format);
+    ne_vsnprintf(sess->error, sizeof sess->error, format, params);
+    va_end(params);
 }
 
 
 void ne_set_progress(ne_session *sess, 
-                      sock_progress progress, void *userdata)
+                    ne_progress progress, void *userdata)
 {
     sess->progress_cb = progress;
     sess->progress_ud = userdata;
@@ -225,40 +198,80 @@ void ne_set_persist(ne_session *sess, int persist)
     sess->no_persist = !persist;
 }
 
+void ne_set_read_timeout(ne_session *sess, int timeout)
+{
+    sess->rdtimeout = timeout;
+}
+
+#define UAHDR "User-Agent: "
+#define AGENT " neon/" NEON_VERSION "\r\n"
+
 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);
+    if (sess->user_agent) ne_free(sess->user_agent);
+    sess->user_agent = ne_malloc(strlen(UAHDR) + strlen(AGENT) + 
+                                 strlen(token) + 1);
+#ifdef HAVE_STPCPY
+    strcpy(stpcpy(stpcpy(sess->user_agent, UAHDR), token), AGENT);
+#else
+    strcat(strcat(strcpy(sess->user_agent, UAHDR), token), AGENT);
+#endif
 }
 
-const char *ne_get_server_hostport(ne_session *sess) {
+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";
-    }
+    return sess->scheme;
 }
 
+void ne_fill_server_uri(ne_session *sess, ne_uri *uri)
+{
+    uri->host = ne_strdup(sess->server.hostname);
+    uri->port = sess->server.port;
+    uri->scheme = ne_strdup(sess->scheme);
+}
 
-const char *ne_get_error(ne_session *sess) {
-    return sess->error;
+const char *ne_get_error(ne_session *sess)
+{
+    return ne_strclean(sess->error);
 }
 
-int ne_close_connection(ne_session *sess)
+void ne_close_connection(ne_session *sess)
 {
-    NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
-    if (sess->connected > 0) {
-       sock_close(sess->socket);
+    if (sess->connected) {
+       NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
+       ne_sock_close(sess->socket);
        sess->socket = NULL;
+       NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
+    } else {
+       NE_DEBUG(NE_DBG_SOCKET, "(Not closing closed connection!).\n");
     }
     sess->connected = 0;
-    NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
-    return 0;
 }
 
+void ne_ssl_set_verify(ne_session *sess, ne_ssl_verify_fn fn, void *userdata)
+{
+    sess->ssl_verify_fn = fn;
+    sess->ssl_verify_ud = userdata;
+}
+
+void ne_ssl_provide_clicert(ne_session *sess, 
+                         ne_ssl_provide_fn fn, void *userdata)
+{
+    sess->ssl_provide_fn = fn;
+    sess->ssl_provide_ud = userdata;
+}
+
+void ne_ssl_trust_cert(ne_session *sess, const ne_ssl_certificate *cert)
+{
+#ifdef NEON_SSL
+    ne_ssl_ctx_trustcert(sess->ssl_context, cert);
+#endif
+}
+
+
+
index 7397349..69e8647 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP session handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
 #ifndef NE_SESSION_H
 #define NE_SESSION_H 1
 
-#include "ne_socket.h" /* for nssl_context */
+#include <sys/types.h>
+
+#include "ne_ssl.h"
+#include "ne_uri.h" /* for ne_uri */
 #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);
+/* Create a session to the given server, using the given scheme.  If
+ * "https" is passed as the scheme, SSL will be used to connect to the
+ * server. */
+ne_session *ne_session_create(const char *scheme,
+                             const char *hostname, unsigned int port);
 
 /* Finish an HTTP session */
-int ne_session_destroy(ne_session *sess);
+void ne_session_destroy(ne_session *sess);
 
 /* Prematurely force the connection to be closed for the given
- * session.  */
-int ne_close_connection(ne_session *sess);
+ * session. */
+void 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 the proxy server to be used for the session. */
+void ne_session_proxy(ne_session *sess,
+                     const char *hostname, unsigned int port);
 
 /* Set protocol options for session:
  *   expect100: Defaults to OFF
@@ -95,9 +62,17 @@ void ne_session_decide_proxy(ne_session *sess, ne_use_proxy use_proxy,
 void ne_set_expect100(ne_session *sess, int use_expect100);
 void ne_set_persist(ne_session *sess, int persist);
 
+/* Progress callback. */
+typedef void (*ne_progress)(void *userdata, off_t progress, off_t total);
+
 /* Set a progress callback for the session. */
 void ne_set_progress(ne_session *sess, 
-                    sock_progress progress, void *userdata);
+                    ne_progress progress, void *userdata);
+
+/* Store an opaque context for the session, 'priv' is returned by a
+ * call to ne_session_get_private with the same ID. */
+void ne_set_session_private(ne_session *sess, const char *id, void *priv);
+void *ne_get_session_private(ne_session *sess, const char *id);
 
 typedef enum {
     ne_conn_namelookup, /* lookup up hostname (info = hostname) */
@@ -110,49 +85,74 @@ 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);
+/* Certificate verification failures.
+ * The certificate is not yet valid: */
+#define NE_SSL_NOTYETVALID (0x01)
+/* The certificate has expired: */
+#define NE_SSL_EXPIRED (0x02)
+/* The hostname for which the certificate was issued does not
+ * match the hostname of the server; this could mean that the
+ * connection is being intercepted: */
+#define NE_SSL_IDMISMATCH (0x04)
+/* The certificate authority which signed the server certificate is
+ * not trusted: there is no indicatation the server is who they claim
+ * to be: */
+#define NE_SSL_UNTRUSTED (0x08)
+
+/* The bitmask of known failure bits: if (failures & ~NE_SSL_FAILMASK)
+ * is non-zero, an unrecognized failure is given, and the verification
+ * should be failed. */
+#define NE_SSL_FAILMASK (0x0f)
+
+/* A callback which is used when server certificate verification is
+ * needed.  The reasons for verification failure are given in the
+ * 'failures' parameter, which is a binary OR of one or more of the
+ * above NE_SSL_* values. failures is guaranteed to be non-zero.  The
+ * callback must return zero to accept the certificate: a non-zero
+ * return value will fail the SSL negotiation. */
+typedef int (*ne_ssl_verify_fn)(void *userdata, int failures,
+                               const ne_ssl_certificate *cert);
+
+/* Install a callback to handle server certificate verification.  This
+ * is required when the CA certificate is not known for the server
+ * certificate, or the server cert has other verification problems. */
+void ne_ssl_set_verify(ne_session *sess, ne_ssl_verify_fn fn, void *userdata);
+
+/* Use the given client certificate for the session.  The client cert
+ * MUST be in the decrypted state, otherwise behaviour is undefined. */
+void ne_ssl_set_clicert(ne_session *sess, const ne_ssl_client_cert *clicert);
+
+/* Indicate that the certificate 'cert' is trusted; 'cert' is
+ * duplicated internally and may be destroyed at will. */
+void ne_ssl_trust_cert(ne_session *sess, const ne_ssl_certificate *cert);
+
+/* If the SSL library provided a default set of CA certificates, trust
+ * this set of CAs. */
+void ne_ssl_trust_default_ca(ne_session *sess);
+
+/* Callback used to load a client certificate on demand.  If dncount
+ * is > 0, the 'dnames' array dnames[0] through dnames[dncount-1]
+ * gives the list of CA names which the server indicated were
+ * acceptable.  The callback should load an appropriate client
+ * certificate and then pass it to 'ne_ssl_set_clicert'. */
+typedef void (*ne_ssl_provide_fn)(void *userdata, ne_session *sess,
+                                 const ne_ssl_dname *const *dnames,
+                                  int dncount);
+
+/* Register a function to be called when the server requests a client
+ * certificate. */
+void ne_ssl_provide_clicert(ne_session *sess, 
+                            ne_ssl_provide_fn fn, void *userdata);
+
+/* Set the timeout (in seconds) used when reading from a socket.  The
+ * timeout value must be greater than zero. */
+void ne_set_read_timeout(ne_session *sess, int timeout);
 
 /* Sets the user-agent string. neon/VERSION will be appended, to make
  * the full header "User-Agent: product neon/VERSION".
@@ -160,32 +160,30 @@ int ne_set_accept_secure_upgrade(ne_session *sess, int acc_upgrade);
  * 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]*
- */
+ * 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.
- */
+/* Returns non-zero if next-hop server does not claim compliance to
+ * HTTP/1.1 or later. */
 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) 
- */
+ * "my.server.com:8080". */
 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".
- */
+/* Returns the URL scheme being used for the current session, omitting
+ * the trailing ':'; e.g. "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);
+/* Sets the host, scheme, and port fields (and no others) of the given
+ * URI structure; host and scheme are malloc-allocated. */
+void ne_fill_server_uri(ne_session *sess, ne_uri *uri);
+
+/* Set the error string for the session; takes printf-like format
+ * string. */
+void ne_set_error(ne_session *sess, const char *format, ...)
+    ne_attribute((format (printf, 2, 3)));
+
 /* Retrieve the error string for the session */
 const char *ne_get_error(ne_session *sess);
 
index 1ab4c1e..1bcf257 100644 (file)
@@ -1,12 +1,7 @@
 /* 
    socket handling routines
-   Copyright (C) 1998-2001, Joe Orton <joe@light.plus.com>, 
-   except where otherwise indicated.
-
-   Portions are:
+   Copyright (C) 1998-2004, Joe Orton <joe@manyfish.co.uk>, 
    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 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.
-
+/*
+  portions were originally under GPL in Mutt, http://www.mutt.org/
+  Relicensed under LGPL for neon, http://www.webdav.org/neon/
 */
 
 #include "config.h"
 
+#ifdef __hpux
+/* pick up hstrerror */
+#define _XOPEN_SOURCE_EXTENDED 1
+/* don't use the broken getaddrinfo shipped in HP-UX 11.11 */
+#ifdef USE_GETADDRINFO
+#undef USE_GETADDRINFO
+#endif
+#endif
+
 #include <sys/types.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-
-#ifdef WIN32
-#include <WinSock2.h>
-#else
+#include <sys/stat.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#endif /* WIN32 */
+#endif
 
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-
-#include <sys/stat.h>
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
 #endif
 
-#include <errno.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <stddef.h>
+#endif
 
-#include <fcntl.h>
+#if defined(NEON_SSL) && defined(HAVE_LIMITS_H)
+#include <limits.h> /* for INT_MAX */
+#endif
 #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 */
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
 
-/* SOCKS support. */
 #ifdef HAVE_SOCKS_H
 #include <socks.h>
 #endif
 
+#ifdef NEON_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h> /* for PKCS12_PBE_add */
+#include <openssl/rand.h>
+
+#include "ne_privssl.h"
+#endif
+
 #include "ne_i18n.h"
 #include "ne_utils.h"
 #include "ne_string.h"
+
+#define NE_INET_ADDR_DEFINED
+/* A slightly ugly hack: change the ne_inet_addr definition to be the
+ * real address type used.  The API only exposes ne_inet_addr as a
+ * pointer to an opaque object, so this should be well-defined
+ * behaviour.  It avoids the hassle of a real wrapper ne_inet_addr
+ * structure, or losing type-safety by using void *. */
+#ifdef USE_GETADDRINFO
+typedef struct addrinfo ne_inet_addr;
+
+/* To avoid doing AAAA queries unless absolutely necessary, either use
+ * AI_ADDRCONFIG where available, or a run-time check for working IPv6
+ * support; the latter is only known to work on Linux. */
+#if !defined(USE_GAI_ADDRCONFIG) && defined(__linux__)
+#define USE_CHECK_IPV6
+#endif
+
+#else
+typedef struct in_addr ne_inet_addr;
+#endif
+
 #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)
+#if defined(__BEOS__) && !defined(BONE_VERSION)
+/* pre-BONE */
+#define ne_write(a,b,c) send(a,b,c,0)
+#define ne_read(a,b,c) recv(a,b,c,0)
+#define ne_close(s) closesocket(s)
+#define ne_errno errno
 #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)
+#define ne_write(a,b,c) send(a,b,c,0)
+#define ne_read(a,b,c) recv(a,b,c,0)
+#define ne_close(s) closesocket(s)
+#define ne_errno WSAGetLastError()
 #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)
+#define ne_write(a,b,c) write(a,b,c)
+#define ne_read(a,b,c) read(a,b,c)
+#define ne_close(s) close(s)
+#define ne_errno errno
 #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()))
-
+#ifdef WIN32
+#define NE_ISRESET(e) ((e) == WSAECONNABORTED || (e) == WSAETIMEDOUT || \
+                       (e) == WSAECONNRESET || (e) == WSAENETRESET)
+#define NE_ISCLOSED(e) ((e) == WSAESHUTDOWN || (e) == WSAENOTCONN)
+#define NE_ISINTR(e) (0)
+#else /* Unix */
+#define NE_ISRESET(e) ((e) == ECONNRESET)
+#define NE_ISCLOSED(e) ((e) == EPIPE)
+#define NE_ISINTR(e) ((e) == EINTR)
 #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
+/* Socket read timeout */
+#define SOCKET_READ_TIMEOUT 120
+
+/* Critical I/O functions on a socket: useful abstraction for easily
+ * handling SSL I/O alongside raw socket I/O. */
+struct iofns {
+    /* Read up to 'len' bytes into 'buf' from socket.  Return <0 on
+     * error or EOF, or >0; number of bytes read. */
+    ssize_t (*read)(ne_socket *s, char *buf, size_t len);
+    /* Write exactly 'len' bytes from 'buf' to socket.  Return zero on
+     * success, <0 on error. */
+    ssize_t (*write)(ne_socket *s, const char *buf, size_t len);
+    /* Wait up to 'n' seconds for socket to become readable.  Returns
+     * 0 when readable, otherwise NE_SOCK_TIMEOUT or NE_SOCK_ERROR. */
+    int (*readable)(ne_socket *s, int n);
+};
 
-struct nsocket_s {
+struct ne_socket_s {
     int fd;
-    const char *error; /* Store error string here */
-    sock_progress progress_cb;
+    char error[200];
     void *progress_ud;
-#ifdef ENABLE_SSL
-    SSL *ssl;
-    SSL_CTX *default_ctx;
+    int rdtimeout; /* read timeout. */
+    const struct iofns *ops;
+#ifdef NEON_SSL
+    ne_ssl_socket ssl;
 #endif
-#ifdef BEOS_PRE_BONE
-#define MAX_PEEK_BUFFER 1024
-    char peeked_bytes[MAX_PEEK_BUFFER];
-    char *peeked_bytes_curpos;
-    int peeked_bytes_avail;
+    /* The read buffer: ->buffer stores byte which have been read; as
+     * these are consumed and passed back to the caller, bufpos
+     * advances through ->buffer.  ->bufavail gives the number of
+     * bytes which remain to be consumed in ->buffer (from ->bufpos),
+     * and is hence always <= RDBUFSIZ. */
+#define RDBUFSIZ 4096
+    char buffer[RDBUFSIZ];
+    char *bufpos;
+    size_t bufavail;
+};
+
+/* ne_sock_addr represents an Internet address. */
+struct ne_sock_addr_s {
+#ifdef USE_GETADDRINFO
+    struct addrinfo *result, *cursor;
+#else
+    struct in_addr *addrs;
+    size_t cursor, count;
 #endif
+    int errnum;
 };
 
-struct nssl_context_s {
-#ifdef ENABLE_SSL
-    SSL_CTX *ctx;
+/* set_error: set socket error string to 'str'. */
+#define set_error(s, str) ne_strnzcpy((s)->error, (str), sizeof (s)->error)
+
+/* set_strerror: set socket error to system error string for 'errnum' */
+#ifdef WIN32
+/* Print system error message to given buffer. */
+static void print_error(int errnum, char *buffer, size_t buflen)
+{
+    if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
+                       | FORMAT_MESSAGE_IGNORE_INSERTS,
+                       NULL, (DWORD) errnum, 0, 
+                       buffer, buflen, NULL) == 0)
+        ne_snprintf(buffer, buflen, "Socket error %d", errnum);
+}
+#define set_strerror(s, e) print_error((e), (s)->error, sizeof (s)->error)
+#else /* not WIN32 */
+#define set_strerror(s, e) ne_strerror((e), (s)->error, sizeof (s)->error)
 #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)
+#ifdef NEON_SSL
+
+/* Initialize SSL library. */
+static void init_ssl(void)
 {
-    sock->progress_cb = cb;
-    sock->progress_ud = userdata;
+    SSL_load_error_strings();
+    SSL_library_init();
+    PKCS12_PBE_add();  /* ### not sure why this is needed. */
 }
 
-void sock_call_progress(nsocket *sock, off_t progress, off_t total)
+/* Seed the SSL PRNG, if necessary; returns non-zero on failure. */
+static int seed_ssl_prng(void)
 {
-    if (sock->progress_cb) {
-       sock->progress_cb(sock->progress_ud, progress, total);
+    /* Check whether the PRNG has already been seeded. */
+    if (RAND_status() == 1)
+       return 0;
+
+#ifdef EGD_PATH
+    NE_DEBUG(NE_DBG_SOCKET, "Seeding PRNG from " EGD_PATH "...\n");
+    if (RAND_egd(EGD_PATH) != -1)
+       return 0;
+#elif defined(ENABLE_EGD)
+    {
+       static const char *paths[] = { "/var/run/egd-pool", "/dev/egd-pool",
+                                      "/etc/egd-pool", "/etc/entropy" };
+       size_t n;
+       for (n = 0; n < sizeof(paths) / sizeof(char *); n++) {
+           NE_DEBUG(NE_DBG_SOCKET, "Seeding PRNG from %s...\n", paths[n]);
+           if (RAND_egd(paths[n]) != -1)
+               return 0;
+       }
     }
+#endif /* EGD_PATH */
+
+    NE_DEBUG(NE_DBG_SOCKET, "No entropy source found; could not seed PRNG.\n");
+    return -1;
 }
+#endif /* NEON_SSL */
+
+#ifdef USE_CHECK_IPV6
+static int ipv6_disabled = 0;
 
-int sock_init(void)
+/* On Linux kernels, IPv6 is typically built as a loadable module, and
+ * socket(AF_INET6, ...) will fail if this module is not loaded, so
+ * the slow AAAA lookups can be avoided for this common case. */
+static void init_ipv6(void)
+{
+    int fd = socket(AF_INET6, SOCK_STREAM, 0);
+    
+    if (fd < 0)
+        ipv6_disabled = 1;
+    else
+        close(fd);
+}
+#else
+#define ipv6_disabled (0)
+#endif
+
+static int init_result = 0;
+
+int ne_sock_init(void)
 {
 #ifdef WIN32
     WORD wVersionRequested;
     WSADATA wsaData;
     int err;
-    
+#endif
+
+    if (init_result > 0) 
+       return 0;
+    else if (init_result < 0)
+       return -1;
+
+#ifdef WIN32    
     wVersionRequested = MAKEWORD(2, 2);
     
     err = WSAStartup(wVersionRequested, &wsaData);
-    if (err != 0)
+    if (err != 0) {
+       init_result = -1;
        return -1;
+    }
 
 #endif
 
-#ifdef ENABLE_SSL
-    SSL_load_error_strings();
-    SSL_library_init();
+#ifdef NEON_SOCKS
+    SOCKSinit("neon");
+#endif
 
-    NE_DEBUG(NE_DBG_SOCKET, "Initialized SSL.\n");
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE)
+    (void) signal(SIGPIPE, SIG_IGN);
 #endif
 
+#ifdef USE_CHECK_IPV6
+    init_ipv6();
+#endif
+
+#ifdef NEON_SSL
+    init_ssl();
+#endif
+
+    init_result = 1;
     return 0;
 }
 
-void sock_exit(void)
+void ne_sock_exit(void)
 {
 #ifdef WIN32
     WSACleanup();
-#endif 
+#endif
+    init_result = 0;
 }
 
-/* sock_read is read() with a timeout of SOCKET_READ_TIMEOUT. */
-int sock_read(nsocket *sock, char *buffer, size_t count) 
+int ne_sock_block(ne_socket *sock, int n)
 {
-    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... */
+    if (sock->bufavail)
        return 0;
-    }
+    return sock->ops->readable(sock, n);
+}
 
-    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
-           }
+/* Cast address object AD to type 'sockaddr_TY' */ 
+#define SACAST(ty, ad) ((struct sockaddr_##ty *)(ad))
+
+#define SOCK_ERR(x) do { ssize_t _sock_err = (x); \
+if (_sock_err < 0) return _sock_err; } while(0)
+
+ssize_t ne_sock_read(ne_socket *sock, char *buffer, size_t buflen)
+{
+    ssize_t bytes;
+
+#if 0
+    NE_DEBUG(NE_DBG_SOCKET, "buf: at %d, %d avail [%s]\n", 
+            sock->bufpos - sock->buffer, sock->bufavail, sock->bufpos);
 #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;
-       }
+
+    if (sock->bufavail > 0) {
+       /* Deliver buffered data. */
+       if (buflen > sock->bufavail)
+           buflen = sock->bufavail;
+       memcpy(buffer, sock->bufpos, buflen);
+       sock->bufpos += buflen;
+       sock->bufavail -= buflen;
+       return buflen;
+    } else if (buflen >= sizeof sock->buffer) {
+       /* No need for read buffer. */
+       return sock->ops->read(sock, buffer, buflen);
+    } else {
+       /* Fill read buffer. */
+       bytes = sock->ops->read(sock, sock->buffer, sizeof sock->buffer);
+       if (bytes <= 0)
+           return bytes;
+
+       if (buflen > (size_t)bytes)
+           buflen = bytes;
+       memcpy(buffer, sock->buffer, buflen);
+       sock->bufpos = sock->buffer + buflen;
+       sock->bufavail = bytes - buflen;
+       return buflen; 
     }
-    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) 
+ssize_t ne_sock_peek(ne_socket *sock, char *buffer, size_t buflen)
 {
-    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;
-           }
-       }
+    ssize_t bytes;
+    
+    if (sock->bufavail) {
+       /* just return buffered data. */
+       bytes = sock->bufavail;
     } 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
+       /* fill the buffer. */
+       bytes = sock->ops->read(sock, sock->buffer, sizeof sock->buffer);
+       if (bytes <= 0)
+           return bytes;
+
+       sock->bufpos = sock->buffer;
+       sock->bufavail = bytes;
     }
-#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;
+
+    if (buflen > (size_t)bytes)
+       buflen = bytes;
+
+    memcpy(buffer, sock->bufpos, buflen);
+
+    return buflen;
 }
 
-/* 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) 
+/* Await data on raw fd in socket. */
+static int readable_raw(ne_socket *sock, int secs)
 {
-    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
+    int fdno = sock->fd, ret;
+    fd_set rdfds;
+    struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
 
     /* Init the fd set */
-    FD_ZERO(&fds);
-    FD_SET(sock->fd, &fds);
-    /* Set the timeout */
-    tv.tv_sec = timeout;
-    tv.tv_usec = 0;
+    FD_ZERO(&rdfds);
     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;
+       FD_SET(fdno, &rdfds);
+       if (tvp) {
+           tvp->tv_sec = secs;
+           tvp->tv_usec = 0;
+       }
+       ret = select(fdno + 1, &rdfds, NULL, NULL, tvp);
+    } while (ret < 0 && NE_ISINTR(ne_errno));
+    if (ret < 0) {
+       set_strerror(sock, ne_errno);
+       return NE_SOCK_ERROR;
     }
+    return (ret == 0) ? NE_SOCK_TIMEOUT : 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) 
+static ssize_t read_raw(ne_socket *sock, char *buffer, size_t len)
 {
-    char *buffer;
-    int ret;
-    CONCAT2(buffer, line, "\r\n");
-    ret = sock_send_string(sock, buffer);
-    free(buffer);
+    ssize_t ret;
+    
+    ret = readable_raw(sock, sock->rdtimeout);
+    if (ret) return ret;
+
+    do {
+       ret = ne_read(sock->fd, buffer, len);
+    } while (ret == -1 && NE_ISINTR(ne_errno));
+
+    if (ret == 0) {
+       set_error(sock, _("Connection closed"));
+       ret = NE_SOCK_CLOSED;
+    } else if (ret < 0) {
+       int errnum = ne_errno;
+       ret = NE_ISRESET(errnum) ? NE_SOCK_RESET : NE_SOCK_ERROR;
+       set_strerror(sock, errnum);
+    }
+
     return ret;
 }
 
-int sock_readfile_blocked(nsocket *sock, off_t length,
-                         sock_block_reader reader, void *userdata) 
+#define MAP_ERR(e) (NE_ISCLOSED(e) ? NE_SOCK_CLOSED : \
+                    (NE_ISRESET(e) ? NE_SOCK_RESET : NE_SOCK_ERROR))
+
+static ssize_t write_raw(ne_socket *sock, const char *data, size_t length) 
 {
-    char buffer[BUFSIZ];
-    int ret;
-    off_t done = 0;
+    ssize_t wrote;
+    
     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));
+       wrote = ne_write(sock->fd, data, length);
+        if (wrote > 0) {
+            data += wrote;
+            length -= wrote;
+        }
+    } while ((wrote > 0 || NE_ISINTR(ne_errno)) && length > 0);
+
+    if (wrote < 0) {
+       int errnum = ne_errno;
+       set_strerror(sock, errnum);
+       return MAP_ERR(errnum);
+    }
+
     return 0;
 }
 
+static const struct iofns iofns_raw = { read_raw, write_raw, readable_raw };
 
-/* 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) 
+#ifdef NEON_SSL
+/* OpenSSL I/O function implementations. */
+static int readable_ossl(ne_socket *sock, int secs)
 {
-    ssize_t wrote;
+    /* If there is buffered SSL data, then don't block on the socket.
+     * FIXME: make sure that SSL_read *really* won't block if
+     * SSL_pending returns non-zero.  Possibly need to do
+     * SSL_read(ssl, buf, SSL_pending(ssl)) */
 
-#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;
-               }
+    if (SSL_pending(sock->ssl.ssl))
+       return 0;
+
+    return readable_raw(sock, secs);
+}
+
+/* SSL error handling, according to SSL_get_error(3). */
+static int error_ossl(ne_socket *sock, int sret)
+{
+    int err = SSL_get_error(sock->ssl.ssl, sret), ret = NE_SOCK_ERROR;
+    
+    switch (err) {
+    case SSL_ERROR_ZERO_RETURN:
+       ret = NE_SOCK_CLOSED;
+       set_error(sock, _("Connection closed"));
+       break;
+    case SSL_ERROR_SYSCALL:
+       err = ERR_get_error();
+       if (err == 0) {
+           if (sret == 0) {
+               /* EOF without close_notify, possible truncation */
+               set_error(sock, _("Secure connection truncated"));
+               ret = NE_SOCK_TRUNC;
+           } else {
+               /* Other socket error. */
+               err = ne_errno;
+               set_strerror(sock, err);
+               ret = MAP_ERR(err);
            }
-           sent += wrote;
-           pnt += wrote;
-#ifdef ENABLE_SSL
+       } else {
+           ne_snprintf(sock->error, sizeof sock->error, 
+                       _("SSL error: %s"), ERR_reason_error_string(err));
        }
-#endif
+       break;
+    default:
+       ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"), 
+                   ERR_reason_error_string(ERR_get_error()));
+       break;
     }
-    return 0;
+    return ret;
 }
 
-/* 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) 
+/* Work around OpenSSL's use of 'int' rather than 'size_t', to prevent
+ * accidentally passing a negative number, etc. */
+#define CAST2INT(n) (((n) > INT_MAX) ? INT_MAX : (n))
+
+static ssize_t read_ossl(ne_socket *sock, char *buffer, size_t len)
 {
-    return sock_fullwrite(sock, data, strlen(data));
+    int ret;
+
+    ret = readable_ossl(sock, sock->rdtimeout);
+    if (ret) return ret;
+    
+    ret = SSL_read(sock->ssl.ssl, buffer, CAST2INT(len));
+    if (ret <= 0)
+       ret = error_ossl(sock, ret);
+
+    return ret;
 }
 
-/* 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)
+static ssize_t write_ossl(ne_socket *sock, const char *data, size_t 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;
+    int ret, ilen = CAST2INT(len);
+    ret = SSL_write(sock->ssl.ssl, data, ilen);
+    /* ssl.h says SSL_MODE_ENABLE_PARTIAL_WRITE must be enabled to
+     * have SSL_write return < length...  so, SSL_write should never
+     * return < length. */
+    if (ret != ilen)
+       return error_ossl(sock, ret);
+    return 0;
 }
 
-/*** End of ESR-copyrighted section ***/
+static const struct iofns iofns_ossl = {
+    read_ossl,
+    write_ossl,
+    readable_ossl
+};
+
+#endif /* NEON_SSL */
 
-/* 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) 
+int ne_sock_fullwrite(ne_socket *sock, const char *data, size_t len)
 {
-    char buffer[BUFSIZ];
-    size_t curlen; /* total bytes yet to read from srcfd */
-    off_t sumwrlen; /* total bytes written to destfd */
+    return sock->ops->write(sock, data, len);
+}
 
-    if (readlen == -1) {
-       curlen = BUFSIZ; /* so the buffer size test works */
-    } else {
-       curlen = readlen; /* everything to do */
+ssize_t ne_sock_readline(ne_socket *sock, char *buf, size_t buflen)
+{
+    char *lf;
+    size_t len;
+    
+    if ((lf = memchr(sock->bufpos, '\n', sock->bufavail)) == NULL
+       && sock->bufavail < RDBUFSIZ) {
+       /* The buffered data does not contain a complete line: move it
+        * to the beginning of the buffer. */
+       if (sock->bufavail)
+           memmove(sock->buffer, sock->bufpos, sock->bufavail);
+       sock->bufpos = sock->buffer;
+       
+       /* Loop filling the buffer whilst no newline is found in the data
+        * buffered so far, and there is still buffer space available */ 
+       do {
+           /* Read more data onto end of buffer. */
+           ssize_t ret = sock->ops->read(sock, sock->buffer + sock->bufavail,
+                                         RDBUFSIZ - sock->bufavail);
+           if (ret < 0) return ret;
+           sock->bufavail += ret;
+       } while ((lf = memchr(sock->buffer, '\n', sock->bufavail)) == NULL
+                && sock->bufavail < RDBUFSIZ);
     }
-    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;
-       }
+    if (lf)
+       len = lf - sock->bufpos + 1;
+    else
+       len = buflen; /* fall into "line too long" error... */
 
-       sumwrlen += rdlen;
+    if ((len + 1) > buflen) {
+       set_error(sock, _("Line too long"));
+       return NE_SOCK_ERROR;
     }
-    sock_call_progress(sock, sumwrlen, readlen);
-    return sumwrlen;
+
+    memcpy(buf, sock->bufpos, len);
+    buf[len] = '\0';
+    /* consume the line from buffer: */
+    sock->bufavail -= len;
+    sock->bufpos += len;
+    return len;
 }
 
-/* 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) 
+ssize_t ne_sock_fullread(ne_socket *sock, char *buffer, size_t buflen) 
 {
-    char *pnt; /* current position within buffer */
-    int len;
-    pnt = buffer;
+    ssize_t len;
+
     while (buflen > 0) {
-       len = sock_read(sock, pnt, buflen);
+       len = ne_sock_read(sock, buffer, buflen);
        if (len < 0) return len;
        buflen -= len;
-       pnt += len;
+       buffer += 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) 
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long) -1)
+#endif
+
+#if !defined(USE_GETADDRINFO) && !defined(HAVE_DECL_H_ERRNO) && !defined(WIN32)
+/* Ancient versions of netdb.h don't export h_errno. */
+extern int h_errno;
+#endif
+
+/* This implemementation does not attempt to support IPv6 using
+ * gethostbyname2 et al.  */
+ne_sock_addr *ne_addr_resolve(const char *hostname, int flags)
 {
-    struct hostent *hp;
+    ne_sock_addr *addr = ne_calloc(sizeof *addr);
+#ifdef USE_GETADDRINFO
+    struct addrinfo hints = {0};
+    char *pnt;
+    hints.ai_socktype = SOCK_STREAM;
+    if (hostname[0] == '[' && ((pnt = strchr(hostname, ']')) != NULL)) {
+       char *hn = ne_strdup(hostname + 1);
+       hn[pnt - hostname - 1] = '\0';
+#ifdef AI_NUMERICHOST /* added in the RFC2553 API */
+       hints.ai_flags = AI_NUMERICHOST;
+#endif
+        hints.ai_family = AF_INET6;
+       addr->errnum = getaddrinfo(hn, NULL, &hints, &addr->result);
+       ne_free(hn);
+    } else {
+#ifdef USE_GAI_ADDRCONFIG /* added in the RFC3493 API */
+        hints.ai_flags = AI_ADDRCONFIG;
+        hints.ai_family = AF_UNSPEC;
+        addr->errnum = getaddrinfo(hostname, NULL, &hints, &addr->result);
+#else
+        hints.ai_family = ipv6_disabled ? AF_INET : AF_UNSPEC;
+       addr->errnum = getaddrinfo(hostname, NULL, &hints, &addr->result);
+#endif
+    }
+#else /* Use gethostbyname() */
     unsigned long laddr;
+    struct hostent *hp;
     
-    /* 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. */
+    laddr = inet_addr(hostname);
+    if (laddr == INADDR_NONE) {
        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;
-           }
+#ifdef WIN32
+           addr->errnum = WSAGetLastError();
+#else
+            addr->errnum = h_errno;
 #endif
-           return SOCK_ERROR;
+       } else if (hp->h_length != sizeof(struct in_addr)) {
+           /* fail gracefully if somebody set RES_USE_INET6 */
+           addr->errnum = NO_RECOVERY;
+       } else {
+           size_t n;
+           /* count addresses */
+           for (n = 0; hp->h_addr_list[n] != NULL; n++)
+               /* noop */;
+
+           addr->count = n;
+           addr->addrs = ne_malloc(n * sizeof *addr->addrs);
+
+           for (n = 0; n < addr->count; n++)
+               memcpy(&addr->addrs[n], hp->h_addr_list[n], hp->h_length);
        }
-       memcpy(addr, hp->h_addr, hp->h_length);
     } else {
-       addr->s_addr = laddr;
+       addr->addrs = ne_malloc(sizeof *addr->addrs);
+       addr->count = 1;
+       memcpy(addr->addrs, &laddr, sizeof *addr->addrs);
     }
-    return 0;
+#endif
+    return addr;
+}
+
+int ne_addr_result(const ne_sock_addr *addr)
+{
+    return addr->errnum;
 }
 
-static nsocket *create_sock(int fd)
+const ne_inet_addr *ne_addr_first(ne_sock_addr *addr)
 {
-    nsocket *sock = ne_calloc(sizeof *sock);
-#ifdef ENABLE_SSL
-    sock->default_ctx = SSL_CTX_new(SSLv23_client_method());
+#ifdef USE_GETADDRINFO
+    addr->cursor = addr->result->ai_next;
+    return addr->result;
+#else
+    addr->cursor = 0;
+    return &addr->addrs[0];
 #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)
+const ne_inet_addr *ne_addr_next(ne_sock_addr *addr)
 {
-    struct sockaddr_in sa;
-    int fd;
+#ifdef USE_GETADDRINFO
+    struct addrinfo *ret = addr->cursor;
+    if (addr->cursor) addr->cursor = addr->cursor->ai_next;
+#else
+    struct in_addr *ret;
+    if (++addr->cursor < addr->count)
+       ret = &addr->addrs[addr->cursor];
+    else
+       ret = NULL;
+#endif
+    return ret;
+}
 
-    /* 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);
+char *ne_addr_error(const ne_sock_addr *addr, char *buf, size_t bufsiz)
+{
+#ifdef WIN32
+    print_error(addr->errnum, buf, bufsiz);
+#else
+    const char *err;
+#ifdef USE_GETADDRINFO
+    /* override horrible generic "Name or service not known" error. */
+    if (addr->errnum == EAI_NONAME)
+       err = _("Host not found");
+    else
+       err = gai_strerror(addr->errnum);
+#elif defined(HAVE_HSTRERROR)
+    err = hstrerror(addr->errnum);
+#else
+    err = _("Host not found");
+#endif
+    ne_strnzcpy(buf, err, bufsiz);
+#endif /* WIN32 */
+    return buf;
 }
 
-nsocket *sock_accept(int listener) 
+char *ne_iaddr_print(const ne_inet_addr *ia, char *buf, size_t bufsiz)
 {
-    int fd = accept(listener, NULL, NULL);
-    if (fd > 0) {
-       return create_sock(fd);
+#ifdef USE_GETADDRINFO /* implies inet_ntop */
+    const char *ret;
+#ifdef AF_INET6
+    if (ia->ai_family == AF_INET6) {
+       struct sockaddr_in6 *in6 = SACAST(in6, ia->ai_addr);
+       ret = inet_ntop(AF_INET6, &in6->sin6_addr, buf, bufsiz);
+    } else
+#endif
+    if (ia->ai_family == AF_INET) {
+       struct sockaddr_in *in = SACAST(in, ia->ai_addr);
+       ret = inet_ntop(AF_INET, &in->sin_addr, buf, bufsiz);
+    } else
+       ret = NULL;
+    if (ret == NULL)
+       ne_strnzcpy(buf, "[IP address]", bufsiz);
+#else
+    ne_strnzcpy(buf, inet_ntoa(*ia), bufsiz);
+#endif
+    return buf;
+}
+
+void ne_addr_destroy(ne_sock_addr *addr)
+{
+#ifdef USE_GETADDRINFO
+    if (addr->result)
+       freeaddrinfo(addr->result);
+#else
+    if (addr->addrs)
+       ne_free(addr->addrs);
+#endif
+    ne_free(addr);
+}
+
+/* Connect socket 'fd' to address 'addr' on given 'port': */
+static int raw_connect(int fd, const ne_inet_addr *addr, unsigned int port)
+{
+#ifdef USE_GETADDRINFO
+#ifdef AF_INET6
+    /* fill in the _family field for AIX 4.3, which forgets to do so. */
+    if (addr->ai_family == AF_INET6) {
+       struct sockaddr_in6 in6;
+       memcpy(&in6, addr->ai_addr, sizeof in6);
+       in6.sin6_port = port;
+        in6.sin6_family = AF_INET6;
+       return connect(fd, (struct sockaddr *)&in6, sizeof in6);
+    } else
+#endif
+    if (addr->ai_family == AF_INET) {
+       struct sockaddr_in in;
+       memcpy(&in, addr->ai_addr, sizeof in);
+       in.sin_port = port;
+        in.sin_family = AF_INET;
+       return connect(fd, (struct sockaddr *)&in, sizeof in);
     } else {
-       return NULL;
+       errno = EINVAL;
+       return -1;
     }
+#else
+    struct sockaddr_in sa = {0};
+    sa.sin_family = AF_INET;
+    sa.sin_port = port;
+    sa.sin_addr = *addr;
+    return connect(fd, (struct sockaddr *)&sa, sizeof sa);
+#endif
 }
 
-int sock_get_fd(nsocket *sock)
+ne_socket *ne_sock_create(void)
 {
-    return sock->fd;
+    ne_socket *sock = ne_calloc(sizeof *sock);
+    sock->rdtimeout = SOCKET_READ_TIMEOUT;
+    sock->bufpos = sock->buffer;
+    sock->ops = &iofns_raw;
+    sock->fd = -1;
+    return sock;
 }
 
-nssl_context *sock_create_ssl_context(void)
+int ne_sock_connect(ne_socket *sock,
+                    const ne_inet_addr *addr, unsigned int port)
 {
-    nssl_context *ctx = ne_calloc(sizeof *ctx);
-#ifdef ENABLE_SSL
-    ctx->ctx = SSL_CTX_new(SSLv23_client_method());
+    int fd;
+
+#ifdef USE_GETADDRINFO
+    /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo
+     * implementations do not set ai_socktype, e.g. RHL6.2. */
+    fd = socket(addr->ai_family, SOCK_STREAM, addr->ai_protocol);
+#else
+    fd = socket(AF_INET, SOCK_STREAM, 0);
 #endif
-    return ctx;
+    if (fd < 0) {
+        set_strerror(sock, ne_errno);
+       return -1;
+    }
+    
+#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP)
+    { /* Disable the Nagle algorithm; better to add write buffering
+       * instead of doing this. */
+        int flag = 1;
+        setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof flag);
+    }
+#endif
+
+    if (raw_connect(fd, addr, ntohs(port))) {
+        set_strerror(sock, ne_errno);
+       ne_close(fd);
+       return -1;
+    }
+
+    sock->fd = fd;
+    return 0;
 }
 
-void sock_destroy_ssl_context(nssl_context *ctx)
+ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw)
 {
-#ifdef ENABLE_SSL
-    SSL_CTX_free(ctx->ctx);
+    ne_inet_addr *ia;
+#if !defined(AF_INET6) || !defined(USE_GETADDRINFO)
+    /* fail if IPv6 address is given if IPv6 is not supported. */
+    if (type == ne_iaddr_ipv6)
+       return NULL;
 #endif
-    free(ctx);
+    ia = ne_calloc(sizeof *ia);
+#ifdef USE_GETADDRINFO
+    /* ai_protocol and ai_socktype aren't used by raw_connect so
+     * ignore them here. (for now) */
+    if (type == ne_iaddr_ipv4) {
+       struct sockaddr_in *in4 = ne_calloc(sizeof *in4);
+       ia->ai_family = AF_INET;
+       ia->ai_addr = (struct sockaddr *)in4;
+       ia->ai_addrlen = sizeof *in4;
+       in4->sin_family = AF_INET;
+       memcpy(&in4->sin_addr.s_addr, raw, sizeof in4->sin_addr.s_addr);
+    }
+#ifdef AF_INET6
+    else {
+       struct sockaddr_in6 *in6 = ne_calloc(sizeof *in6);
+       ia->ai_family = AF_INET6;
+       ia->ai_addr = (struct sockaddr *)in6;
+       ia->ai_addrlen = sizeof *in6;
+       in6->sin6_family = AF_INET6;
+       memcpy(&in6->sin6_addr, raw, sizeof in6->sin6_addr.s6_addr);
+    }
+#endif
+#else /* !USE_GETADDRINFO */
+    memcpy(&ia->s_addr, raw, sizeof ia->s_addr);
+#endif    
+    return ia;
 }
 
-#ifdef ENABLE_SSL
-void sock_disable_tlsv1(nssl_context *c)
+int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2)
 {
-    SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1);
+#ifdef USE_GETADDRINFO
+    if (i1->ai_family != i2->ai_family)
+       return i2->ai_family - i1->ai_family;
+    if (i1->ai_family == AF_INET) {
+       struct sockaddr_in *in1 = SACAST(in, i1->ai_addr), 
+           *in2 = SACAST(in, i2->ai_addr);
+       return memcmp(&in1->sin_addr.s_addr, &in2->sin_addr.s_addr, 
+                     sizeof in1->sin_addr.s_addr);
+    } else if (i1->ai_family == AF_INET6) {
+       struct sockaddr_in6 *in1 = SACAST(in6, i1->ai_addr), 
+           *in2 = SACAST(in6, i2->ai_addr);
+       return memcmp(in1->sin6_addr.s6_addr, in2->sin6_addr.s6_addr,
+                     sizeof in1->sin6_addr.s6_addr);
+    } else
+       return -1;
+#else
+    return memcmp(&i1->s_addr, &i2->s_addr, sizeof i1->s_addr);
+#endif
 }
-void sock_disable_sslv2(nssl_context *c)
-{
-    SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv2);
 
+void ne_iaddr_free(ne_inet_addr *addr)
+{
+#ifdef USE_GETADDRINFO
+    ne_free(addr->ai_addr);
+#endif
+    ne_free(addr);
 }
-void sock_disable_sslv3(nssl_context *c)
+
+int ne_sock_accept(ne_socket *sock, int listener) 
 {
-    SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv3);
+    int fd = accept(listener, NULL, NULL);
+
+    if (fd < 0)
+        return -1;
+
+    sock->fd = fd;
+    return 0;
 }
 
-/* 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)
+int ne_sock_fd(const ne_socket *sock)
 {
-    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);
+    return sock->fd;
 }
 
-void sock_set_key_prompt(nssl_context *ctx, 
-                        nssl_key_prompt prompt, void *userdata)
+void ne_sock_read_timeout(ne_socket *sock, int timeout)
 {
-    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;
+    sock->rdtimeout = timeout;
 }
 
-#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
+#ifdef NEON_SSL
+
+void ne_sock_switch_ssl(ne_socket *sock, void *ssl)
+{
+    sock->ssl.ssl = ssl;
+    sock->ops = &iofns_ossl;
+}
 
-int sock_make_secure(nsocket *sock, nssl_context *ctx)
+int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx)
 {
-#ifdef ENABLE_SSL
+    SSL *ssl;
     int ret;
-    SSL_CTX *ssl_ctx;
 
-    if (ctx) {
-       ssl_ctx = ctx->ctx;
-    } else {
-       ssl_ctx = sock->default_ctx;
+    if (seed_ssl_prng()) {
+       set_error(sock, _("SSL disabled due to lack of entropy"));
+       return NE_SOCK_ERROR;
     }
 
-    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;
+    sock->ssl.ssl = ssl = SSL_new(ctx->ctx);
+    if (!ssl) {
+       set_error(sock, _("Could not create SSL structure"));
+       return NE_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);
+    SSL_set_app_data(ssl, ctx);
+    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
+    SSL_set_fd(ssl, sock->fd);
+    sock->ops = &iofns_ossl;
     
-    ret = SSL_connect(sock->ssl);
-    if (ret == -1) {
-       sock->error = ERROR_SSL_STRING;
-       SSL_free(sock->ssl);
-       sock->ssl = NULL;
-       return SOCK_ERROR;
+    if (ctx->sess)
+       SSL_set_session(ssl, ctx->sess);
+
+    ret = SSL_connect(ssl);
+    if (ret != 1) {
+       error_ossl(sock, ret);
+       SSL_free(ssl);
+       sock->ssl.ssl = NULL;
+       return NE_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)
+ne_ssl_socket *ne_sock_sslsock(ne_socket *sock)
 {
-#ifdef ENABLE_SSL
-    return SSL_get_version(sock->ssl);
-#else
-    return NULL;
-#endif
+    return &sock->ssl;
 }
 
-const char *sock_get_error(nsocket *sock)
+#endif
+
+const char *ne_sock_error(const ne_socket *sock)
 {
     return sock->error;
 }
 
-/* Closes given nsocket */
-int sock_close(nsocket *sock) {
+/* Closes given ne_socket */
+int ne_sock_close(ne_socket *sock)
+{
     int ret;
-#ifdef ENABLE_SSL
-    if (sock->ssl) {
-       SSL_shutdown(sock->ssl);
-       SSL_free(sock->ssl);
+#ifdef NEON_SSL
+    if (sock->ssl.ssl) {
+       SSL_shutdown(sock->ssl.ssl);
+       SSL_free(sock->ssl.ssl);
     }
-    SSL_CTX_free(sock->default_ctx);
 #endif
-    ret = NEON_CLOSE(sock->fd);
-    free(sock);
+    if (sock->fd < 0)
+        ret = 0;
+    else
+        ret = ne_close(sock->fd);
+    ne_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) {
+int ne_service_lookup(const char *name)
+{
     struct servent *ent;
     ent = getservbyname(name, "tcp");
-    if (ent == NULL) {
-       return 0;
-    } else {
+    if (ent)
        return ntohs(ent->s_port);
-    }
+    return 0;
 }
index d0e0cf9..4da0c0c 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    socket handling interface
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
 #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"
+#include "ne_ssl.h" /* for ne_ssl_context */
 
 BEGIN_NEON_DECLS
 
-#define SOCK_ERROR -1
+#define NE_SOCK_ERROR (-1)
 /* Read/Write timed out */
-#define SOCK_TIMEOUT -2
-/* Passed buffer was full */
-#define SOCK_FULL -3
+#define NE_SOCK_TIMEOUT (-2)
 /* Socket was closed */
-#define SOCK_CLOSED -4
+#define NE_SOCK_CLOSED (-3)
+/* Connection was reset (e.g. server crashed) */
+#define NE_SOCK_RESET (-4)
+/* Secure connection was subject to possible truncation attack. */
+#define NE_SOCK_TRUNC (-5)
+
+/* ne_socket represents a TCP socket. */
+typedef struct ne_socket_s ne_socket;
+
+/* ne_sock_addr represents an address object. */
+typedef struct ne_sock_addr_s ne_sock_addr;
+
+#ifndef NE_INET_ADDR_DEFINED
+typedef struct ne_inet_addr_s ne_inet_addr;
+#endif
+
+/* While neon itself doesn't require per-process global
+ * initialization, some platforms do, and so does the OpenSSL
+ * library. */
+int ne_sock_init(void);
+
+/* Shutdown any underlying libraries. */
+void ne_sock_exit(void);
+
+/* Resolve the given hostname.  'flags' are currently ignored.  Hex
+ * string IPv6 addresses (e.g. `::1') may be enclosed in brackets
+ * (e.g. `[::1]'). */
+ne_sock_addr *ne_addr_resolve(const char *hostname, int flags);
+
+/* Returns zero if name resolution was successful, non-zero on
+ * error. */
+int ne_addr_result(const ne_sock_addr *addr);
+
+/* Returns the first network address associated with the 'addr'
+ * object.  Undefined behaviour if ne_addr_result returns non-zero for
+ * 'addr'; otherwise, never returns NULL.  */
+const ne_inet_addr *ne_addr_first(ne_sock_addr *addr);
+
+/* Returns the next network address associated with the 'addr' object,
+ * or NULL if there are no more. */
+const ne_inet_addr *ne_addr_next(ne_sock_addr *addr);
+
+/* NB: the pointers returned by ne_addr_first and ne_addr_next are
+ * valid until ne_addr_destroy is called for the corresponding
+ * ne_sock_addr object.  They must not be passed to ne_iaddr_free. */
 
-/* Socket read timeout */
-#define SOCKET_READ_TIMEOUT 120
+/* If name resolution fails, copies the error string into 'buffer',
+ * which is of size 'bufsiz'.  'buffer' is returned. */
+char *ne_addr_error(const ne_sock_addr *addr, char *buffer, size_t bufsiz);
 
-struct nsocket_s;
-typedef struct nsocket_s nsocket;
+/* Destroys an address object created by ne_addr_resolve. */
+void ne_addr_destroy(ne_sock_addr *addr);
 
-typedef void (*sock_block_reader) (
-    void *userdata, const char *buf, size_t len);
+/* Network address type; IPv4 or IPv6 */
+typedef enum {
+    ne_iaddr_ipv4 = 0,
+    ne_iaddr_ipv6
+} ne_iaddr_type;
 
-typedef void (*sock_progress)(void *userdata, off_t progress, off_t total);
+/* Create a network address from raw byte representation (in network
+ * byte order) of given type.  'raw' must be four bytes for an IPv4
+ * address, 16 bytes for an IPv6 address.  May return NULL if address
+ * type is not supported. */
+ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw);
 
-void sock_register_progress(nsocket *sock, sock_progress cb, void *userdata);
+/* Compare two network addresses i1 and i2; return non-zero if they
+ * are not equal. */
+int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2);
 
-void sock_call_progress(nsocket *sock, off_t progress, off_t total);
+/* Prints the string representation of network address 'ia' into the
+ * 'buffer', which is of size 'bufsiz'.  Returns 'buffer'. */
+char *ne_iaddr_print(const ne_inet_addr *ia, char *buffer, size_t bufsiz);
 
-/* 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);
+/* Free a network address created using ne_iaddr_make. */
+void ne_iaddr_free(ne_inet_addr *addr);
 
-/* Shutdown the socket library. */
-void sock_exit(void);
+/* Create a TCP socket; returns NULL on error. */
+ne_socket *ne_sock_create(void);
 
-/* sock_read is read() with a timeout of SOCKET_TIMEOUT.
+/* Connect the socket to server at address 'addr' on port 'port'.
+ * Returns non-zero if a connection could not be established. */
+int ne_sock_connect(ne_socket *sock, const ne_inet_addr *addr, 
+                    unsigned int port);
+
+/* ne_sock_read reads up to 'count' bytes into 'buffer'.
  * Returns:
- *   SOCK_* on error,
- *    0 on no data to read (due to EOF),
+ *   NE_SOCK_* on error,
  *   >0 length of data read into buffer.
  */
-int sock_read(nsocket *sock, char *buffer, size_t count);
+ssize_t ne_sock_read(ne_socket *sock, char *buffer, size_t count);
 
-/* sock_peek is recv() with a timeout of SOCKET_TIMEOUT.
+/* ne_sock_peek reads up to 'count' bytes into 'buffer', but the data
+ * will still be returned on a subsequent call to ne_sock_read or 
+ * ne_sock_peek.
  * Returns:
- *   SOCK_* on error,
- *    0 on no data to read (due to EOF),
+ *   NE_SOCK_* on error,
  *   >0 length of data read into buffer.
  */
-int sock_peek(nsocket *sock, char *buffer, size_t count);
+ssize_t ne_sock_peek(ne_socket *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,
+/* Block for up to 'n' seconds until data becomes available for reading
+ * on the socket. Returns:
+ *  NE_SOCK_* on error,
+ *  NE_SOCK_TIMEOUT if no data arrives in 'n' seconds.
  *  0 if data arrived on the socket.
  */
-int sock_block(nsocket *sock, int timeout);
+int ne_sock_block(ne_socket *sock, int n);
 
-/* 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.
+/* Writes 'count' bytes of 'data' to the socket.
+ * Returns 0 on success, NE_SOCK_* on error. */
+int ne_sock_fullwrite(ne_socket *sock, const char *data, size_t count); 
+
+/* Reads an LF-terminated line into 'buffer', and NUL-terminate it.
+ * At most 'len' bytes are read (including the NUL terminator).
+ * Returns:
+ * NE_SOCK_* on error,
+ * >0 number of bytes read (including NUL terminator)
  */
-nsocket *sock_accept(int listener);
+ssize_t ne_sock_readline(ne_socket *sock, char *buffer, size_t len);
 
-/* Returns the file descriptor used for the socket */
-int sock_get_fd(nsocket *sock);
+/* Read exactly 'len' bytes into buffer; returns 0 on success, SOCK_*
+ * on error. */
+ssize_t ne_sock_fullread(ne_socket *sock, char *buffer, size_t len);
 
-/* Closes the socket and frees the nsocket object. */
-int sock_close(nsocket *sock);
+/* Accept a connection on listening socket 'fd'. */
+int ne_sock_accept(ne_socket *sock, int fd);
 
-const char *sock_get_version(nsocket *sock);
+/* Returns the file descriptor used for socket 'sock'. */
+int ne_sock_fd(const ne_socket *sock);
 
-const char *sock_get_error(nsocket *sock);
+/* Close the socket, and destroy the socket object. Returns non-zero
+ * on error. */
+int ne_sock_close(ne_socket *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);
+/* Return current error string for socket. */
+const char *ne_sock_error(const ne_socket *sock);
 
-/* Returns the standard TCP port for the given service */
-int sock_service_lookup(const char *name);
+/* Set read timeout for socket. */
+void ne_sock_read_timeout(ne_socket *sock, int timeout);
 
-/* 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);
+/* Returns the standard TCP port for the given service, or zero if
+ * none is known. */
+int ne_service_lookup(const char *name);
+
+/* Enable SSL with an already-negotiated SSL socket. */
+void ne_sock_switch_ssl(ne_socket *sock, void *ssl);
+
+/* Perform an SSL negotiation on 'sock', using given context. */
+int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx);
+
+/* Return SSL socket object in use for 'sock'. */
+typedef struct ne_ssl_socket_s ne_ssl_socket;
+ne_ssl_socket *ne_sock_sslsock(ne_socket *sock);
 
 END_NEON_DECLS
 
diff --git a/neon/src/ne_ssl.h b/neon/src/ne_ssl.h
new file mode 100644 (file)
index 0000000..bda65a3
--- /dev/null
@@ -0,0 +1,150 @@
+/* 
+   SSL/TLS abstraction layer for neon
+   Copyright (C) 2003-2004, 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
+
+*/
+
+/* ne_ssl.h defines an interface for loading and accessing the
+ * properties of SSL certificates. */
+
+#ifndef NE_SSL_H
+#define NE_SSL_H 1
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* A "distinguished name"; a unique name for some entity. */
+typedef struct ne_ssl_dname_s ne_ssl_dname;
+
+/* Returns a single-line string representation of a distinguished
+ * name, intended to be human-readable (e.g. "Acme Ltd., Norfolk,
+ * GB").  Return value is a UTF-8-encoded malloc-allocated string and
+ * must be free'd by the caller. */
+char *ne_ssl_readable_dname(const ne_ssl_dname *dn);
+
+/* Returns zero if 'dn1' and 'dn2' refer to same name, or non-zero if
+ * they are different. */
+int ne_ssl_dname_cmp(const ne_ssl_dname *dn1, const ne_ssl_dname *dn2);
+
+/* An SSL certificate. */
+typedef struct ne_ssl_certificate_s ne_ssl_certificate;
+
+/* Read a certificate from a file in PEM format; returns NULL if the
+ * certificate could not be parsed. */
+ne_ssl_certificate *ne_ssl_cert_read(const char *filename);
+
+/* Write a certificate to a file in PEM format; returns non-zero if
+ * the certificate could not be written. */
+int ne_ssl_cert_write(const ne_ssl_certificate *cert, const char *filename);
+
+/* Export a certificate to a base64-encoded, NUL-terminated string.
+ * The returned string is malloc-allocated and must be free()d by the
+ * caller. */
+char *ne_ssl_cert_export(const ne_ssl_certificate *cert);
+
+/* Import a certificate from a base64-encoded string as returned by
+ * ne_ssl_cert_export(). Returns a certificate object or NULL if
+ * 'data' was not valid. */
+ne_ssl_certificate *ne_ssl_cert_import(const char *data);
+
+/* Returns the identity of the certificate, or NULL if none is given.
+ * For a server certificate this will be the hostname of the server to
+ * whom the cert was issued. */
+const char *ne_ssl_cert_identity(const ne_ssl_certificate *cert);
+
+/* Return the certificate of the entity which signed certificate
+ * 'cert'.  Returns NULL if 'cert' is self-signed or the issuer
+ * certificate is not available. */
+const ne_ssl_certificate *ne_ssl_cert_signedby(const ne_ssl_certificate *cert);
+
+/* Returns the distinguished name of the certificate issuer. */
+const ne_ssl_dname *ne_ssl_cert_issuer(const ne_ssl_certificate *cert);
+
+/* Returns the distinguished name of the certificate subject. */
+const ne_ssl_dname *ne_ssl_cert_subject(const ne_ssl_certificate *cert);
+
+#define NE_SSL_DIGESTLEN (60)
+
+/* Calculate the certificate digest ("fingerprint") and format it as a
+ * NUL-terminated hex string in 'digest', of the form "aa:bb:...:ff".
+ * Returns zero on success or non-zero if there was an internal error
+ * whilst calculating the digest.  'digest' must be at least 
+ * NE_SSL_DIGESTLEN bytes in length. */
+int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest);
+
+#define NE_SSL_VDATELEN (30)
+
+/* Copy the validity dates into buffers 'from' and 'until' as
+ * NUL-terminated human-readable strings.  The buffers must be at
+ * least NE_SSL_VDATELEN bytes in length. */
+void ne_ssl_cert_validity(const ne_ssl_certificate *cert,
+                          char *from, char *until);
+
+/* Returns zero if 'c1' and 'c2' refer to the same certificate, or
+ * non-zero otherwise. */
+int ne_ssl_cert_cmp(const ne_ssl_certificate *c1,
+                    const ne_ssl_certificate *c2);
+
+/* Deallocate memory associated with certificate. */
+void ne_ssl_cert_free(ne_ssl_certificate *cert);
+
+/* A client certificate (and private key). */
+typedef struct ne_ssl_client_cert_s ne_ssl_client_cert;
+
+/* Read a client certificate and private key from a PKCS12 file;
+ * returns NULL if the file could not be parsed.  If the client cert
+ * is encrypted, it must be decrypted before use. */
+ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename);
+
+/* Returns the "friendly name" given for the client cert, or NULL if
+ * none given.  This can be called before or after the client cert has
+ * been decrypted.  Returns a NUL-terminated string. */
+const char *ne_ssl_clicert_name(ne_ssl_client_cert *ccert);
+
+/* Returns non-zero if client cert is encrypted. */
+int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *ccert);
+
+/* Decrypt the encrypted client cert using given password.  Returns
+ * non-zero on failure, in which case, the function can be called
+ * again with a different password.  For a ccert on which _encrypted()
+ * returns 0, calling _decrypt results in undefined behaviour. */
+int ne_ssl_clicert_decrypt(ne_ssl_client_cert *ccert, const char *password);
+
+/* Return the actual certificate part of the client certificate (never
+ * returns NULL). */
+const ne_ssl_certificate *ne_ssl_clicert_owner(const ne_ssl_client_cert *ccert);
+
+/* Deallocate memory associated with a client certificate. */
+void ne_ssl_clicert_free(ne_ssl_client_cert *ccert);
+
+/* An SSL context; only necessary when interfacing with ne_socket.h. */
+typedef struct ne_ssl_context_s ne_ssl_context;
+
+/* Create an SSL context. */
+ne_ssl_context *ne_ssl_context_create(void);
+
+/* Trust the given certificate 'cert' in context 'ctx'. */
+void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert);
+
+/* Destroy an SSL context. */
+void ne_ssl_context_destroy(ne_ssl_context *ctx);
+
+END_NEON_DECLS
+
+#endif
index b205eb3..3cddad9 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    String utility functions
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
@@ -19,9 +19,7 @@
 
 */
 
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #include <unistd.h>
 #endif
 
+#include <ctype.h> /* for isprint() etc in ne_strclean() */
+
 #include "ne_alloc.h"
 #include "ne_string.h"
 
-char *ne_token(char **str, char separator, const char *quotes)
+char *ne_token(char **str, char separator)
 {
-    char *pnt, *ret = NULL;
+    char *ret = *str, *pnt = strchr(*str, separator);
 
-    if (quotes) {
+    if (pnt) {
+       *pnt = '\0';
+       *str = pnt + 1;
+    } else {
+       /* no separator found: return end of string. */
+       *str = NULL;
+    }
+    
+    return ret;
+}
+
+char *ne_qtoken(char **str, char separator, const char *quotes)
+{
+    char *pnt, *ret = NULL;
 
-       for (pnt = *str; *pnt != '\0'; pnt++) {
-           char *quot = strchr(quotes, *pnt);
+    for (pnt = *str; *pnt != '\0'; pnt++) {
+       char *quot = strchr(quotes, *pnt);
+       
+       if (quot) {
+           char *qclose = strchr(pnt+1, *quot);
            
-           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;
+           if (!qclose) {
+               /* no closing quote: invalid string. */
+               return NULL;
            }
-       }
-
-    } else {
-       pnt = strchr(*str, separator);
-       if (pnt != NULL) {
+           
+           pnt = qclose;
+       } else if (*pnt == separator) {
+           /* found end of token. */
            *pnt = '\0';
            ret = *str;
            *str = pnt + 1;
+           return ret;
        }
     }
 
-    if (ret == NULL) {
-       /* no separator found: return end of string. */
-       ret = *str;
-       *str = NULL;
-    }
-
+    /* no separator found: return end of string. */
+    ret = *str;
+    *str = NULL;
     return ret;
 }
 
@@ -85,7 +86,7 @@ char *ne_shave(char *str, const char *whitespace)
 {
     char *pnt, *ret = str;
 
-    while (strchr(whitespace, *ret) != NULL) {
+    while (*ret != '\0' && strchr(whitespace, *ret) != NULL) {
        ret++;
     }
 
@@ -242,7 +243,7 @@ char **pair_string(const char *str, const char compsep, const char kvsep,
        pairs[2*n][length] = '\0';
        pairs[2*n+1] = split?(split + 1):NULL;
     }
-    free(comps);
+    ne_free(comps);
     pairs[2*count] = pairs[2*count+1] = NULL;    
     return pairs;
 }
@@ -251,149 +252,131 @@ void split_string_free(char **components)
 {
     char **pnt = components;
     while (*pnt != NULL) {
-       free(*pnt);
+       ne_free(*pnt);
        pnt++;
     }
-    free(components);
+    ne_free(components);
 }
 
 void pair_string_free(char **pairs) 
 {
     int n;
     for (n = 0; pairs[n] != NULL; n+=2) {
-       free(pairs[n]);
+       ne_free(pairs[n]);
     }
-    free(pairs);
+    ne_free(pairs);
 }
 
-char *shave_string(const char *str, const char ch
+void ne_buffer_clear(ne_buffer *buf
 {
-    size_t len = strlen(str);
-    char *ret;
-    if (str[len-1] == ch) {
-       len--;
-    }
-    if (str[0] == ch) {
-       len--;
-       str++;
+    memset(buf->data, 0, buf->length);
+    buf->used = 1;
+}  
+
+/* Grows for given size, returns 0 on success, -1 on error. */
+void ne_buffer_grow(ne_buffer *buf, size_t newsize) 
+{
+#define NE_BUFFER_GROWTH 512
+    if (newsize > buf->length) {
+       /* If it's not big enough already... */
+       buf->length = ((newsize / NE_BUFFER_GROWTH) + 1) * NE_BUFFER_GROWTH;
+       
+       /* Reallocate bigger buffer */
+       buf->data = ne_realloc(buf->data, buf->length);
     }
-    ret = ne_malloc(len + 1);
-    memcpy(ret, str, len);
-    ret[len] = '\0';
-    return ret;
 }
 
-char *ne_concat(const char *str, ...)
+static size_t count_concat(va_list *ap)
 {
-    va_list ap;
-    ne_buffer *tmp = ne_buffer_create();
+    size_t total = 0;
+    char *next;
 
-    ne_buffer_zappend(tmp, str);
+    while ((next = va_arg(*ap, char *)) != NULL)
+       total += strlen(next);
 
-    va_start(ap, str);
-    ne_buffer_concat(tmp, ap);
-    va_end(ap);
-    
-    return ne_buffer_finish(tmp);
+    return total;
 }
 
-void ne_buffer_clear(ne_buffer *buf
+static void do_concat(char *str, va_list *ap
 {
-    memset(buf->data, 0, buf->length);
-    buf->used = 1;
-}  
+    char *next;
 
-/* Grows for given size, returns 0 on success, -1 on error. */
-int ne_buffer_grow(ne_buffer *buf, size_t newsize) 
+    while ((next = va_arg(*ap, char *)) != NULL) {
+#ifdef HAVE_STPCPY
+        str = stpcpy(str, next);
+#else
+       size_t len = strlen(next);
+       memcpy(str, next, len);
+       str += len;
+#endif
+    }
+}
+
+void ne_buffer_concat(ne_buffer *buf, ...)
 {
-    size_t newlen, oldbuflen;
+    va_list ap;
+    ssize_t total;
 
-#define NE_BUFFER_GROWTH 512
+    va_start(ap, buf);
+    total = buf->used + count_concat(&ap);
+    va_end(ap);    
 
-    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;
+    /* Grow the buffer */
+    ne_buffer_grow(buf, total);
     
-    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;
+    va_start(ap, buf);    
+    do_concat(buf->data + buf->used - 1, &ap);
+    va_end(ap);    
+
+    buf->used = total;
+    buf->data[total - 1] = '\0';
 }
 
-int ne_buffer_concat(ne_buffer *buf, ...) 
+char *ne_concat(const char *str, ...)
 {
     va_list ap;
-    char *next;
-    size_t totallen = buf->used; 
+    size_t total, slen = strlen(str);
+    char *ret;
 
-    /* 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_start(ap, str);
+    total = slen + count_concat(&ap);
     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);
+
+    ret = memcpy(ne_malloc(total + 1), str, slen);
+
+    va_start(ap, str);
+    do_concat(ret + slen, &ap);
     va_end(ap);
-    
-    buf->used = totallen;
-    return 0;
+
+    ret[total] = '\0';
+    return ret;    
 }
 
 /* Append zero-terminated string... returns 0 on success or -1 on
  * realloc failure. */
-int ne_buffer_zappend(ne_buffer *buf, const char *str) 
+void 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;
+    ne_buffer_append(buf, str, strlen(str));
 }
 
-int ne_buffer_append(ne_buffer *buf, const char *data, size_t len) 
+void ne_buffer_append(ne_buffer *buf, const char *data, size_t len) 
 {
-    if (ne_buffer_grow(buf, buf->used + len)) {
-       return -1;
-    }
+    ne_buffer_grow(buf, buf->used + len);
     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);
+    return ne_buffer_ncreate(512);
 }
 
-ne_buffer *ne_buffer_create_sized(size_t s) 
+ne_buffer *ne_buffer_ncreate(size_t s) 
 {
-    ne_buffer *buf = ne_malloc(sizeof(struct ne_buffer_s));
-    buf->data = ne_calloc(s);
+    ne_buffer *buf = ne_malloc(sizeof(*buf));
+    buf->data = ne_malloc(s);
+    buf->data[0] = '\0';
     buf->length = s;
     buf->used = 1;
     return buf;
@@ -401,16 +384,14 @@ ne_buffer *ne_buffer_create_sized(size_t s)
 
 void ne_buffer_destroy(ne_buffer *buf) 
 {
-    if (buf->data) {
-       free(buf->data);
-    }
-    free(buf);
+    ne_free(buf->data);
+    ne_free(buf);
 }
 
 char *ne_buffer_finish(ne_buffer *buf)
 {
     char *ret = buf->data;
-    free(buf);
+    ne_free(buf);
     return ret;
 }
 
@@ -419,51 +400,120 @@ void ne_buffer_altered(ne_buffer *buf)
     buf->used = strlen(buf->data) + 1;
 }
 
-char *ne_utf8_encode(const char *str)
+static const char *b64_alphabet =  
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "0123456789+/=";
+    
+char *ne_base64(const unsigned char *text, size_t inlen)
 {
-    char *buffer = ne_malloc(strlen(str) * 2 + 1);
-    int in, len = strlen(str);
-    char *out;
+    /* 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;
+    size_t outlen;
+    
+    /* Use 'buffer' to store the output. Work out how big it should be...
+     * This must be a multiple of 4 bytes */
 
-    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;
-       }
+    outlen = (inlen*4)/3;
+    if ((inlen % 3) > 0) /* got to pad */
+       outlen += 4 - (inlen % 3);
+    
+    buffer = ne_malloc(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 > 0) {
+       /* 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++) = '=';
     }
 
-    /* nul-terminate */
-    *out = '\0';
+    /* Null-terminate */
+    *point = '\0';
+
     return buffer;
 }
 
-/* Single byte range 0x00 -> 0x7F */
-#define SINGLEBYTE_UTF8(ch) (((unsigned char) (ch)) < 0x80)
+/* VALID_B64: fail if 'ch' is not a valid base64 character */
+#define VALID_B64(ch) (((ch) >= 'A' && (ch) <= 'Z') || \
+                       ((ch) >= 'a' && (ch) <= 'z') || \
+                       ((ch) >= '0' && (ch) <= '9') || \
+                       (ch) == '/' || (ch) == '+' || (ch) == '=')
+
+/* DECODE_B64: decodes a valid base64 character. */
+#define DECODE_B64(ch) ((ch) >= 'a' ? ((ch) + 26 - 'a') : \
+                        ((ch) >= 'A' ? ((ch) - 'A') : \
+                         ((ch) >= '0' ? ((ch) + 52 - '0') : \
+                          ((ch) == '+' ? 62 : 63))))
 
-char *ne_utf8_decode(const char *str)
+size_t ne_unbase64(const char *data, unsigned char **out)
 {
-    int n, m, len = strlen(str);
-    char *dest = ne_malloc(len + 1);
+    size_t inlen = strlen(data);
+    unsigned char *outp;
+    const unsigned char *in;
+
+    if (inlen == 0 || (inlen % 4) != 0) return 0;
     
-    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++;
-           }
-       }
+    outp = *out = ne_malloc(inlen * 3 / 4);
+
+    for (in = (const unsigned char *)data; *in; in += 4) {
+        unsigned int tmp;
+        if (!VALID_B64(in[0]) || !VALID_B64(in[1]) || !VALID_B64(in[2]) ||
+            !VALID_B64(in[3]) || in[0] == '=' || in[1] == '=' ||
+            (in[2] == '=' && in[3] != '=')) {
+            ne_free(*out);
+            return 0;
+        }
+        tmp = (DECODE_B64(in[0]) & 0x3f) << 18 |
+            (DECODE_B64(in[1]) & 0x3f) << 12;
+        *outp++ = (tmp >> 16) & 0xff;
+        if (in[2] != '=') {
+            tmp |= (DECODE_B64(in[2]) & 0x3f) << 6;
+            *outp++ = (tmp >> 8) & 0xff;
+            if (in[3] != '=') {
+                tmp |= DECODE_B64(in[3]) & 0x3f;
+                *outp++ = tmp & 0xff;
+            }
+        }
     }
-    
-    dest[m] = '\0';
-    return dest;
+
+    return outp - *out;
+}
+
+char *ne_strclean(char *str)
+{
+    char *pnt;
+    for (pnt = str; *pnt; pnt++)
+        if (iscntrl(*pnt) || !isprint(*pnt)) *pnt = ' ';
+    return str;
+}
+
+char *ne_strerror(int errnum, char *buf, size_t buflen)
+{
+#ifdef HAVE_STRERROR_R
+#ifdef STRERROR_R_CHAR_P
+    /* glibc-style strerror_r which may-or-may-not use provided buffer. */
+    char *ret = strerror_r(errnum, buf, buflen);
+    if (ret != buf)
+       ne_strnzcpy(buf, ret, buflen);
+#else /* POSIX-style strerror_r: */
+    strerror_r(errnum, buf, buflen);
+#endif
+#else /* no strerror_r: */
+    ne_strnzcpy(buf, strerror(errnum), buflen);
+#endif
+    return buf;
 }
index 517bcfe..81ede09 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    String utility functions
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2002, 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
 
 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);
+/* ne_token and ne_qtoken return the next token in *str between *str
+ * and separator character 'sep' or the NUL terminator. ne_qtoken
+ * skips over any parts quoted using a pair of any one of the
+ * characters given in 'quotes'.  After returning, *str will point to
+ * the next character after the separator, or NULL if no more
+ * separator characters were found.
+ * 
+ * ne_qtoken may return NULL if unterminated quotes are found. */
+char *ne_token(char **str, char sep);
+char *ne_qtoken(char **str, char sep, 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.
- */
+/* Cleanse 'str' of non-printable characters.  'str' is modified
+ * in-place, and returned. */
+char *ne_strclean(char *str);
+
+/* A base64 encoder: converts 'len' bytes of 'text' to base64.
+ * Returns malloc-allocated buffer; caller must free(). */
+char *ne_base64(const unsigned char *text, size_t len);
+
+/* Base64 decoder; decodes NUL-terminated base64-encoded string
+ * 'data', places malloc-allocated raw data in '*out', returns length,
+ * or zero on decode error (in which case *out is undefined). */
+size_t ne_unbase64(const char *data, unsigned char **out);
+
+/*** OBSOLETE INTERFACES ***/
 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);
+/*** END OF OBSOLETE INTERFACES */
 
-/* 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)
+/* String buffer handling. (Strings are zero-terminated still).  A
+ * string buffer ne_buffer * which grows dynamically with the
+ * string. */
 
-/* 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 {
+typedef struct {
     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;
+} 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, ...);
+/* Concatenate all given strings onto the end of the buffer.  The
+ * strings must all be NUL-terminated, and MUST be followed by a NULL
+ * argument marking the end of the list.  */
+void ne_buffer_concat(ne_buffer *buf, ...);
 
-/* Create a new ne_buffer. Returns NULL on error */
+/* Create a new ne_buffer. */
 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);
+/* Create a new ne_buffer of given minimum size. */
+ne_buffer *ne_buffer_ncreate(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 a NUL-terminated string 'str' to buf. */
+void 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);
+ * NUL-terminated. The resultant string will have a NUL-terminator,
+ * either way.  */
+void 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);
+/* Grows the ne_buffer to a minimum size. */
+void ne_buffer_grow(ne_buffer *buf, size_t size);
 
 void ne_buffer_altered(ne_buffer *buf);
 
@@ -171,41 +115,22 @@ void ne_buffer_altered(ne_buffer *buf);
  * 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)
+/* Thread-safe strerror() wrapper; place system error for errno value
+ * 'errnum' in 'buffer', which is of length 'buflen'.  Returns
+ * 'buffer'. */
+char *ne_strerror(int errnum, char *buffer, size_t buflen);
+
+/* ne_strnzcpy copies at most 'n'-1 bytes of 'src' to 'dest', and
+ * ensures that 'dest' is subsequently NUL-terminated. */
+#define ne_strnzcpy(dest, src, n) do { \
+strncpy(dest, src, n-1); dest[n-1] = '\0'; } while (0)
+
+/* Return malloc-allocated concatenation of all NUL-terminated string
+ * arguments, up to a terminating NULL. */
+char *ne_concat(const char *str, ...);
+
+#define NE_ASC2HEX(x) (((x) <= '9') ? ((x) - '0') : (tolower((x)) + 10 - 'a'))
+#define NE_HEX2ASC(x) ((char) ((x) > 9 ? ((x) - 10 + 'a') : ((x) + '0')))
 
 END_NEON_DECLS
 
diff --git a/neon/src/ne_stubssl.c b/neon/src/ne_stubssl.c
new file mode 100644 (file)
index 0000000..ff71e1e
--- /dev/null
@@ -0,0 +1,135 @@
+/* 
+   Stubs for SSL support when no SSL library has been configured
+   Copyright (C) 2002-2003, 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 <stdlib.h> /* for NULL */
+
+#include "ne_ssl.h"
+#include "ne_session.h"
+
+char *ne_ssl_readable_dname(const ne_ssl_dname *dn)
+{
+    return NULL;
+}
+
+ne_ssl_certificate *ne_ssl_cert_read(const char *filename)
+{
+    return NULL;
+}
+
+int ne_ssl_cert_cmp(const ne_ssl_certificate *c1, const ne_ssl_certificate *c2)
+{
+    return 1;
+}
+
+const ne_ssl_certificate *ne_ssl_cert_signedby(const ne_ssl_certificate *cert)
+{ 
+    return NULL;
+}
+
+const ne_ssl_dname *ne_ssl_cert_issuer(const ne_ssl_certificate *cert)
+{
+    return NULL;
+}
+
+const ne_ssl_dname *ne_ssl_cert_subject(const ne_ssl_certificate *cert)
+{
+    return NULL;
+}
+
+void ne_ssl_cert_free(ne_ssl_certificate *cert) {}
+
+ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename)
+{
+    return NULL;
+}
+
+const ne_ssl_certificate *ne_ssl_clicert_owner(const ne_ssl_client_cert *ccert)
+{
+    return NULL;
+}
+
+int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *ccert)
+{
+    return -1;
+}
+
+int ne_ssl_clicert_decrypt(ne_ssl_client_cert *ccert, const char *password)
+{
+    return -1;
+}
+
+void ne_ssl_clicert_free(ne_ssl_client_cert *ccert) {}
+
+void ne_ssl_trust_default_ca(ne_session *sess) {}
+
+ne_ssl_context *ne_ssl_context_create(void)
+{
+    return NULL;
+}
+
+void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert)
+{}
+
+void ne_ssl_context_destroy(ne_ssl_context *ctx) {}
+
+int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char digest[60])
+{
+    return -1;
+}
+
+void ne_ssl_cert_validity(const ne_ssl_certificate *cert, char *from, char *until)
+{}
+
+const char *ne_ssl_cert_identity(const ne_ssl_certificate *cert)
+{
+    return NULL;
+}
+
+
+const char *ne_ssl_clicert_name(ne_ssl_client_cert *ccert)
+{
+    return NULL;
+}
+
+int ne_ssl_dname_cmp(const ne_ssl_dname *dn1, const ne_ssl_dname *dn2)
+{
+    return -1;
+}
+
+int ne_ssl_cert_write(const ne_ssl_certificate *cert, const char *filename)
+{
+    return -1;
+}
+
+char *ne_ssl_cert_export(const ne_ssl_certificate *cert)
+{
+    return NULL;
+}
+
+ne_ssl_certificate *ne_ssl_cert_import(const char *data)
+{
+    return NULL;
+}
+
+void ne_ssl_set_clicert(ne_session *sess, const ne_ssl_client_cert *cc) 
+{}
index fbc2a8d..51107d1 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP URI handling
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2003, 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
@@ -24,6 +24,9 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #include <ctype.h>
 
 #include "ne_utils.h" /* for 'min' */
-#include "ne_string.h" /* for CONCAT3 */
+#include "ne_string.h" /* for ne_buffer */
 #include "ne_alloc.h"
 #include "ne_uri.h"
 
-char *uri_parent(const char *uri) 
+char *ne_path_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 */
+    size_t len = strlen(uri);
+    const char *pnt = uri + len - 1;
+    /* skip trailing slash (parent of "/foo/" is "/") */
+    if (pnt >= uri && *pnt == '/')
+       pnt--;
+    /* find previous slash */
+    while (pnt > uri && *pnt != '/')
+       pnt--;
+    if (pnt < uri || (pnt == uri && *pnt != '/'))
        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;
+    return ne_strndup(uri, pnt - uri + 1);
 }
 
-int uri_has_trailing_slash(const char *uri) 
+int ne_path_has_trailing_slash(const char *uri) 
 {
     size_t len = strlen(uri);
     return ((len > 0) &&
            (uri[len-1] == '/'));
 }
 
-const char *uri_abspath(const char *uri) 
+unsigned int ne_uri_defaultport(const char *scheme)
 {
-    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;
+    /* RFC2616/3.2.3 says use case-insensitive comparisons here. */
+    if (strcasecmp(scheme, "http") == 0)
+       return 80;
+    else if (strcasecmp(scheme, "https") == 0)
+       return 443;
+    else
+       return 0;
 }
 
+/* TODO: Also, maybe stop malloc'ing here, take a "char *" uri, modify
+ * it in-place, and have fields point inside passed uri.  More work
+ * for the caller then though. */
 /* TODO: not a proper URI parser */
-int uri_parse(const char *uri, struct uri *parsed, 
-             const struct uri *defaults)
+int ne_uri_parse(const char *uri, ne_uri *parsed)
 {
-    const char *pnt, *slash, *colon, *atsign;
+    const char *pnt, *slash, *colon, *atsign, *openbk;
 
-    parsed->port = -1;
+    parsed->port = 0;
     parsed->host = NULL;
     parsed->path = NULL;
     parsed->scheme = NULL;
     parsed->authinfo = NULL;
 
-    if (strlen(uri) == 0) {
+    if (uri[0] == '\0') {
        return -1;
     }
 
@@ -108,26 +98,30 @@ int uri_parse(const char *uri, struct uri *parsed,
        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, '/');
+    openbk = 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 (openbk && (!slash || openbk < slash)) {
+       const char *closebk = strchr(openbk, ']');
+       if (closebk == NULL)
+           return -1;
+       colon = strchr(closebk + 1, ':');
+    } else {
+       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);
@@ -136,7 +130,6 @@ int uri_parse(const char *uri, struct uri *parsed,
     } 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 {
@@ -153,32 +146,16 @@ int uri_parse(const char *uri, struct uri *parsed,
     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) 
+void ne_uri_free(ne_uri *u)
 {
-    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;
+    if (u->host) ne_free(u->host);
+    if (u->path) ne_free(u->path);
+    if (u->scheme) ne_free(u->scheme);
+    if (u->authinfo) ne_free(u->authinfo);
+    memset(u, 0, sizeof *u);
 }
 
-/* Un-escapes a URI. Returns ne_malloc-allocated URI */
-char *uri_unescape(const char *uri) 
+char *ne_path_unescape(const char *uri) 
 {
     const char *pnt;
     char *ret, *retpos, buf[5] = { "0x00\0" };
@@ -188,6 +165,7 @@ char *uri_unescape(const char *uri)
            if (!isxdigit((unsigned char) pnt[1]) || 
                !isxdigit((unsigned char) pnt[2])) {
                /* Invalid URI */
+                ne_free(ret);
                return NULL;
            }
            buf[2] = *++pnt; buf[3] = *++pnt; /* bit faster than memcpy */
@@ -238,10 +216,7 @@ static const char uri_chars[128] = {
 #undef AN
 #undef RE
 
-/* Escapes the abspath segment of a URI.
- * Returns ne_malloc-allocated string.
- */
-char *uri_abspath_escape(const char *abs_path) 
+char *ne_path_escape(const char *abs_path) 
 {
     const char *pnt;
     char *ret, *retpos;
@@ -273,16 +248,45 @@ char *uri_abspath_escape(const char *abs_path)
 
 #undef ESCAPE
 
+#define CASECMP(field) do { \
+n = strcasecmp(u1->field, u2->field); if (n) return n; } while(0)
+
+#define CMP(field) do { \
+n = strcmp(u1->field, u2->field); if (n) return n; } while(0)
+
+/* As specified by RFC 2616, section 3.2.3. */
+int ne_uri_cmp(const ne_uri *u1, const ne_uri *u2)
+{
+    int n;
+    
+    if (u1->path[0] == '\0' && strcmp(u2->path, "/") == 0)
+       return 0;
+    if (u2->path[0] == '\0' && strcmp(u1->path, "/") == 0)
+       return 0;
+
+    CMP(path);
+    CASECMP(host);
+    CASECMP(scheme);
+    if (u1->port > u2->port)
+       return 1;
+    else if (u1->port < u2->port)
+       return -1;
+    return 0;
+}
+
+#undef CMP
+#undef CASECMP
+
 /* TODO: implement properly */
-int uri_compare(const char *a, const char *b) 
+int ne_path_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),
+       int traila = ne_path_has_trailing_slash(a),
+           trailb = ne_path_has_trailing_slash(b),
            lena = strlen(a), lenb = strlen(b);
        if (traila != trailb && abs(lena - lenb) == 1 &&
            ((traila && lena > lenb) || (trailb && lenb > lena))) {
@@ -295,9 +299,26 @@ int uri_compare(const char *a, const char *b)
     return ret;
 }
 
+char *ne_uri_unparse(const ne_uri *uri)
+{
+    ne_buffer *buf = ne_buffer_create();
+
+    ne_buffer_concat(buf, uri->scheme, "://", uri->host, NULL);
+
+    if (uri->port > 0 && ne_uri_defaultport(uri->scheme) != uri->port) {
+       char str[20];
+       ne_snprintf(str, 20, ":%d", uri->port);
+       ne_buffer_zappend(buf, str);
+    }
+
+    ne_buffer_zappend(buf, uri->path);
+
+    return ne_buffer_finish(buf);
+}
+
 /* 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) 
+int ne_path_childof(const char *parent, const char *child) 
 {
     char *root = ne_strdup(child);
     int ret;
@@ -306,8 +327,8 @@ int uri_childof(const char *parent, const char *child)
     } else {
        /* root is the first of child, equal to length of parent */
        root[strlen(parent)] = '\0';
-       ret = (uri_compare(parent, root) == 0);
+       ret = (ne_path_compare(parent, root) == 0);
     }
-    free(root);
+    ne_free(root);
     return ret;
 }
index fe6afa8..76929b0 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP utility functions
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
 #include <string.h>
 #endif
 
+#include <stdio.h>
 #include <ctype.h> /* isdigit() for ne_parse_statusline */
 
-#include "ne_utils.h"
-
-#include "ne_dates.h"
-
-#ifdef ENABLE_SSL
+#ifdef NEON_SSL
 #include <openssl/opensslv.h>
 #endif
 
-#ifdef HAVE_XMLVERSION_H
 /* libxml2: pick up the version string. */
-#include <xmlversion.h>
+#if defined(HAVE_LIBXML)
+#include <libxml/xmlversion.h>
+#elif defined(HAVE_EXPAT) && !defined(HAVE_XMLPARSE_H)
+#include <expat.h>
 #endif
 
+#ifdef NEON_ZLIB
+#include <zlib.h>
+#endif
+
+#include "ne_utils.h"
+#include "ne_string.h" /* for ne_strdup */
+#include "ne_dates.h"
+
 int ne_debug_mask = 0;
 FILE *ne_debug_stream = NULL;
 
@@ -49,21 +56,28 @@ void ne_debug_init(FILE *stream, int mask)
 {
     ne_debug_stream = stream;
     ne_debug_mask = mask;
+#if defined(HAVE_SETVBUF) && defined(_IONBF)
+    /* If possible, turn off buffering on the debug log.  this is very
+     * helpful if debugging segfaults. */
+    if (stream) setvbuf(stream, NULL, _IONBF, 0);
+#endif        
 }
 
 void ne_debug(int ch, const char *template, ...) 
 {
     va_list params;
-    if ((ch&ne_debug_mask) != ch) return;
+    if ((ch & ne_debug_mask) == 0) return;
     fflush(stdout);
     va_start(params, template);
     vfprintf(ne_debug_stream, template, params);
     va_end(params);
-    if ((ch&NE_DBG_FLUSH) == NE_DBG_FLUSH) {
+    if ((ch & NE_DBG_FLUSH) == NE_DBG_FLUSH)
        fflush(ne_debug_stream);
-    }
 }
 
+#define NE_STRINGIFY(x) # x
+#define NE_EXPAT_VER(x,y,z) NE_STRINGIFY(x) "." NE_STRINGIFY(y) "." NE_STRINGIFY(z)
+
 static const char *version_string = "neon " NEON_VERSION ": " 
 #ifdef NEON_IS_LIBRARY
   "Library build"
@@ -72,24 +86,28 @@ static const char *version_string = "neon " NEON_VERSION ": "
 #endif
 #ifdef HAVE_EXPAT
   ", Expat"
-#else
-#ifdef HAVE_LIBXML
-  ", libxml"
-#ifdef LIBXML_DOTTED_VERSION
-  " " LIBXML_DOTTED_VERSION
-#endif
+/* expat >=1.95.2 exported the version */
+#ifdef XML_MAJOR_VERSION
+" " NE_EXPAT_VER(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION)
 #endif
+#else /* !HAVE_EXPAT */
+#ifdef HAVE_LIBXML
+  ", libxml " LIBXML_DOTTED_VERSION
+#endif /* HAVE_LIBXML */
+#endif /* !HAVE_EXPAT */
+#if defined(NEON_ZLIB) && defined(ZLIB_VERSION)
+  ", zlib " ZLIB_VERSION
+#endif /* NEON_ZLIB && ... */
+#ifdef NEON_SOCKS
+   ", SOCKSv5"
 #endif
-#ifdef ENABLE_SSL
-   ", SSL support using "
+#ifdef NEON_SSL
 #ifdef OPENSSL_VERSION_TEXT
-    OPENSSL_VERSION_TEXT
+    ", " OPENSSL_VERSION_TEXT
 #else
-   "unknown SSL library"
+   "OpenSSL (unknown version)"
 #endif /* OPENSSL_VERSION_TEXT */
-#else /* !ENABLE_SSL */
-   ", no OpenSSL support"
-#endif /* ENABLE_SSL */
+#endif
    "."
 ;
 
@@ -98,11 +116,18 @@ const char *ne_version_string(void)
     return version_string;
 }
 
-int ne_version_minimum(int major, int minor)
+int ne_version_match(int major, int minor)
 {
-    return 
-       (NEON_VERSION_MAJOR < major) || 
-       (NEON_VERSION_MINOR < minor);
+    return (NEON_VERSION_MAJOR != major) || (NEON_VERSION_MINOR < minor);
+}
+
+int ne_supports_ssl(void)
+{
+#ifdef NEON_SSL
+    return 1;
+#else
+    return 0;
+#endif
 }
 
 int ne_parse_statusline(const char *status_line, ne_status *st)
@@ -110,50 +135,42 @@ 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++) {
+    /* skip leading garbage if any. */
+    part = strstr(status_line, "HTTP/");
+    if (part == NULL) return -1;
+
+    minor = major = 0;
+
+    /* Parse version string, skipping leading zeroes. */
+    for (part += 5; *part != '\0' && isdigit(*part); part++)
        major = major*10 + (*part-'0');
-    }
-    if (*part != '.') { 
-       return -1;
-    }
-    for (part++ ; *part != '\0' && isdigit(*part); part++) {
+
+    if (*part++ != '.') return -1;
+
+    for (;*part != '\0' && isdigit(*part); part++)
        minor = minor*10 + (*part-'0');
-    }
-    if (*part != ' ') {
-       return -1;
-    }
+
+    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. */
+    for (; *part == ' '; part++) /* noop */;
+
+    /* Parse the Status-Code; part now points at the first Y in
+     * "HTTP/x.x YYY". */
     if (!isdigit(part[0]) || !isdigit(part[1]) || !isdigit(part[2]) ||
-       part[3] != ' ') {
-       return -1;
-    }
+       (part[3] != '\0' && 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;
-    }
+
+    /* part now may be pointing to \0 if reason phrase is blank */
+
     /* Fill in the results */
     st->major_version = major;
     st->minor_version = minor;
-    st->reason_phrase = part;
+    st->reason_phrase = ne_strclean(ne_strdup(part));
     st->code = status_code;
     st->klass = klass;
     return 0;
index f269a68..87e6593 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP utility functions
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2002, 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
 
 #include "ne_defs.h"
 
+#ifdef NEON_TRIO
+/* no HAVE_TRIO_H check so this works from outside neon build tree. */
+#include <trio.h>
+#endif
+
 BEGIN_NEON_DECLS
 
 /* Returns a human-readable version string like:
@@ -36,56 +41,37 @@ BEGIN_NEON_DECLS
  */
 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);
+/* Returns non-zero if library version is not of major version
+ * 'major', or if minor version is not greater than or equal to
+ * 'minor'. */
+int ne_version_match(int major, int minor);
+
+/* Returns non-zero if neon has support for SSL. */
+int ne_supports_ssl(void);
 
-#define HTTP_QUOTES "\"'"
-#define HTTP_WHITESPACE " \r\n\t"
+/* Use replacement snprintf's if trio is being used. */
+#ifdef NEON_TRIO
+#define ne_snprintf trio_snprintf
+#define ne_vsnprintf trio_vsnprintf
+#else
+#define ne_snprintf snprintf
+#define ne_vsnprintf vsnprintf
+#endif
 
 #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)
@@ -94,34 +80,38 @@ int ne_version_minimum(int major, int minor);
 #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_SSL (1<<8)
 #define NE_DBG_FLUSH (1<<30)
 
+/* Send debugging output to 'stream', for all of the given debug
+ * channels.  To disable debugging, pass 'stream' as NULL and 'mask'
+ * as 0. */
 void ne_debug_init(FILE *stream, int mask);
+
+/* The current debug mask and stream set by the last call to
+ * ne_debug_init. */
 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__ */
-;
+/* Produce debug output if any of channels 'ch' is enabled for
+ * debugging. */
+void ne_debug(int ch, const char *, ...) ne_attribute((format(printf, 2, 3)));
 
 /* 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;
+    char *reason_phrase;
 } ne_status;
 
+/* NB: couldn't use 'class' in ne_status because it would clash with
+ * the C++ reserved word. */
+
 /* Parser for strings which follow the Status-Line grammar from 
- * RFC2616.
+ * RFC2616.  s->reason_phrase is malloc-allocated if non-NULL, and
+ * must be free'd by the caller.
  *  Returns:
  *    0 on success, *s will be filled in.
  *   -1 on parse error.
index 0920c1f..d405fa2 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Higher Level Interface to XML Parsers.
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   Copyright (C) 1999-2004, 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
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
-
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
 
 #include "ne_i18n.h"
 
 #include "ne_utils.h"
 #include "ne_string.h"
 
-#ifdef HAVE_EXPAT
-/******** Expat ***********/
-#ifdef HAVE_OLD_EXPAT
-/* jclark expat */
+#if defined(HAVE_EXPAT)
+/* expat support: */
+#ifdef HAVE_XMLPARSE_H
 #include "xmlparse.h"
-#else /* not HAVE_OLD_EXPAT */
-/* new-style expat */
-#include "expat.h"
+#else
+#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>
+#elif defined(HAVE_LIBXML)
+/* libxml2 support: */
+#include <libxml/xmlversion.h>
+#include <libxml/parser.h>
 typedef xmlChar ne_xml_char;
 
-# else /* not HAVE_LIBXML */
+#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;
+struct handler {
+    ne_xml_startelm_cb *startelm_cb; /* start-element callback */
+    ne_xml_endelm_cb *endelm_cb; /* end-element callback */
+    ne_xml_cdata_cb *cdata_cb; /* character-data callback. */
+    void *userdata; /* userdata for the above. */
+    struct handler *next; /* next handler in stack. */
 };
 
 #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;
+struct element {
+    const ne_xml_char *nspace;
+    ne_xml_char *name;
 
-    /* Storage for an unknown element */
-    struct ne_xml_elm elm_real;
-    char *real_name;
+    int state; /* opaque state integer */
     
     /* Namespaces declared in this element */
     ne_xml_char *default_ns; /* A default namespace */
-    struct ne_xml_nspace *nspaces; /* List of other namespace scopes */
+    struct namespace *nspaces; /* List of other namespace scopes */
 
-    unsigned int mixed:1; /* are we in MIXED mode? */
+    struct handler *handler; /* Handler for this element */
 
-    /* Extras */
-    struct ne_xml_handler *handler; /* Where the element was declared */
-    struct ne_xml_state *parent; /* The parent in the tree */
+    struct element *parent; /* parent element, or NULL */    
 };
 
-  
-/* 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 
+    struct element *root; /* the root of the document */
+    struct element *current; /* current element in the branch */
+    struct handler *top_handlers; /* always points at the 
                                           * handler on top of the stack. */
+    int valid; /* non-zero whilst parse should continue */
+    int prune; /* if non-zero, depth within a dead branch */
+
 #ifdef HAVE_EXPAT
     XML_Parser parser;
+    char *encoding;
 #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
+static const char *resolve_nspace(const struct element *elm, 
+                                  const char *prefix, size_t pfxlen);
 
 /* Linked list of namespace scopes */
-struct ne_xml_nspace {
+struct namespace {
     ne_xml_char *name;
     ne_xml_char *uri;
-    struct ne_xml_nspace *next;
+    struct namespace *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 */
@@ -209,48 +149,31 @@ static xmlSAXHandler sax_handler = {
     NULL, /* comment */
     NULL, /* xmlParserWarning */
     sax_error, /* xmlParserError */
-    NULL, /* xmlParserError */
+    sax_error, /* fatal error (never called by libxml2?) */
     NULL, /* getParameterEntity */
     char_data /* cdataBlock */
 };
 
-#endif /* HAVE_LIBXML */
+/* empty attributes array to mimic expat behaviour */
+static const char *empty_atts[] = {NULL, NULL};
+
+/* macro for determining the attributes array to pass */
+#define PASS_ATTS(atts) (atts ? (const char **)(atts) : empty_atts)
 
-#ifdef NE_XML_DECODE_UTF8
+#else
 
-static inline int 
-decode_utf8_double(char *dest, const char *src) 
+#define PASS_ATTS(atts) ((const char **)(atts))
+
+/* XML declaration callback for expat. */
+static void decl_handler(void *userdata,
+                        const XML_Char *version, const XML_Char *encoding, 
+                        int standalone)
 {
-    /* 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;
-    }
+    ne_xml_parser *p = userdata;
+    if (encoding) p->encoding = ne_strdup(encoding);    
 }
 
-#endif
+#endif /* HAVE_LIBXML */
 
 int ne_xml_currentline(ne_xml_parser *p) 
 {
@@ -261,386 +184,214 @@ int ne_xml_currentline(ne_xml_parser *p)
 #endif
 }
 
-static int find_handler(ne_xml_parser *p, struct ne_xml_state *state) 
+const char *ne_xml_doc_encoding(const ne_xml_parser *p)
 {
-    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;
-    }
+#ifdef HAVE_LIBXML
+    return p->parser->encoding;
+#else
+    return p->encoding;
+#endif
 }
 
-/* Called with the start of a new element. */
-static void 
-start_element(void *userdata, const ne_xml_char *name, const ne_xml_char **atts) 
+/* Extract the namespace prefix declarations from 'atts'. */
+static int declare_nspaces(ne_xml_parser *p, struct element *elm,
+                           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;
+    int n;
+    
+    for (n = 0; atts && atts[n]; n += 2) {
+        if (strcasecmp(atts[n], "xmlns") == 0) {
+            /* New default namespace */
+            elm->default_ns = ne_strdup(atts[n+1]);
+        } else if (strncasecmp(atts[n], "xmlns:", 6) == 0) {
+            struct namespace *ns;
+            
+            if (atts[n][6] == '\0' || atts[n+1][0] == '\0') {
+                ne_snprintf(p->error, ERR_SIZE, 
+                            ("XML parse error at line %d: invalid namespace "
+                             "declaration"), ne_xml_currentline(p));
+                return -1;
+            }
+
+            /* New namespace scope */
+            ns = ne_calloc(sizeof(*ns));
+            ns->next = elm->nspaces;
+            elm->nspaces = ns;
+            ns->name = ne_strdup(atts[n]+6); /* skip the xmlns= */
+            ns->uri = ne_strdup(atts[n+1]);
+        }
     }
+    
+    return 0;
+}
 
-    /* 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);
+/* Expand an XML qualified name, which may include a namespace prefix
+ * as well as the local part. */
+static int expand_qname(ne_xml_parser *p, struct element *elm,
+                        const ne_xml_char *qname)
+{
+    const ne_xml_char *pfx;
+
+    pfx = strchr(qname, ':');
+    if (pfx == NULL) {
+        struct element *e = elm;
+
+        /* Find default namespace; guaranteed to terminate as the root
+         * element always has default_ns="". */
+        while (e->default_ns == NULL)
+            e = e->parent;
+        
+        elm->name = ne_strdup(qname);
+        elm->nspace = e->default_ns;
+    } else {
+        const char *uri = resolve_nspace(elm, qname, pfx-qname);
+
+       if (uri) {
+           /* The name is everything after the ':' */
+           if (pfx[1] == '\0') {
+               ne_snprintf(p->error, ERR_SIZE, 
+                           ("XML parse error at line %d: element name missing"
+                             "after namespace prefix"), ne_xml_currentline(p));
+               return -1;
            }
+           elm->name = ne_strdup(pfx+1);
+            elm->nspace = uri;
+       } else {
+           ne_snprintf(p->error, ERR_SIZE, 
+                        ("XML parse error at line %d: undeclared namespace"),
+                        ne_xml_currentline(p));
+           return -1;
        }
-       ne_buffer_zappend(p->buffer, ">");
-       /* One deeper */
-       p->collect++;
-       return;
     }
+    return 0;
+}
 
-    /* Set the new state */
-    s = ne_calloc(sizeof(struct ne_xml_state));
-    s->parent = p->current;
-    p->current = s;
+/* 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 element *elm;
+    struct handler *hand;
+    int state = NE_XML_DECLINE;
 
-    /* We need to handle namespaces ourselves */
-    if (parse_element(p, s, name, atts)) {
-       /* it bombed. */
-       p->valid = 0;
-       return;
+    if (!p->valid) return;
+    
+    if (p->prune) {
+        p->prune++;
+        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);
+    /* Create a new element */
+    elm = ne_calloc(sizeof *elm);
+    elm->parent = p->current;
+    p->current = elm;
 
-    if (find_handler(p, s)) {
-       p->valid = 0;
-       return;
+    if (declare_nspaces(p, elm, atts) || expand_qname(p, elm, name)) {
+        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");
+    /* Find a handler which will accept this element (or abort the parse) */
+    for (hand = elm->parent->handler; hand && state == NE_XML_DECLINE;
+         hand = hand->next) {
+        elm->handler = hand;
+        state = hand->startelm_cb(hand->userdata, elm->parent->state,
+                                  elm->nspace, elm->name, PASS_ATTS(atts));
     }
 
+    NE_DEBUG(NE_DBG_XMLPARSE, "XML: start-element (%d, {%s, %s}) => %d\n", 
+             elm->parent->state, elm->nspace, elm->name, state);             
+    
+    if (state > 0)
+        elm->state = state;
+    else if (state == NE_XML_DECLINE)
+        /* prune this branch. */
+        p->prune++;
+    else /* state == NE_XML_ABORT */
+        p->valid = 0;
 }
 
-/* Destroys given state */
-static void destroy_state(struct ne_xml_state *s
+/* Destroys an element structure. */
+static void destroy_element(struct element *elm
 {
-    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);
+    struct namespace *this_ns, *next_ns;
+    ne_free(elm->name);
     /* Free the namespaces */
-    this_ns = s->nspaces;
+    this_ns = elm->nspaces;
     while (this_ns != NULL) {
        next_ns = this_ns->next;
-       free(this_ns->name);
-       free(this_ns->uri);
-       free(this_ns);
+       ne_free(this_ns->name);
+       ne_free(this_ns->uri);
+       ne_free(this_ns);
        this_ns = next_ns;
     };
-    NE_DEBUG(NE_DBG_XMLPARSE, "Finished freeing namespaces.\n");
-    free(s);
+    if (elm->default_ns)
+        ne_free(elm->default_ns);
+    ne_free(elm);
 }
 
+/* cdata SAX callback */
 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
+    struct element *elm = p->current;
 
+    if (!p->valid || p->prune) return;
+    
+    if (elm->handler->cdata_cb && 
+        elm->handler->cdata_cb(elm->handler->userdata, elm->state, data, len)) {
+        NE_DEBUG(NE_DBG_XML, "Cdata callback failed.\n");
+        p->valid = 0;
+    }        
 }
 
 /* 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;
-       }
-    }
+    struct element *elm = p->current;
+
+    if (!p->valid) 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;
-       }
+    if (p->prune) {
+        if (p->prune-- > 1) return;
+    } else if (elm->handler->endelm_cb && 
+               elm->handler->endelm_cb(elm->handler->userdata, elm->state,
+                                       elm->nspace, elm->name)) {
+        NE_DEBUG(NE_DBG_XML, "XML: end-element for %d failed.\n", elm->state);
+        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);
+    
+    NE_DEBUG(NE_DBG_XMLPARSE, "XML: end-element (%d, {%s, %s})\n",
+             elm->state, elm->nspace, elm->name);
+
+    /* move back up the tree */
+    p->current = elm->parent;
+    p->prune = 0;
+
+    destroy_element(elm);
 }
 
-/* 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)
+/* Find a namespace definition for 'prefix' in given element, where
+ * length of prefix is 'pfxlen'.  Returns the URI or NULL. */
+static const char *resolve_nspace(const struct element *elm, 
+                                  const char *prefix, size_t pfxlen)
 {
-    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);
-           }
+    const struct element *s;
+
+    /* Search up the tree. */
+    for (s = elm; s != NULL; s = s->parent) {
+       const struct namespace *ns;
+       /* Iterate over defined spaces on this node. */
+       for (ns = s->nspaces; ns != NULL; ns = ns->next) {
+           if (strlen(ns->name) == pfxlen && 
+               memcmp(ns->name, prefix, pfxlen) == 0)
+               return 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;
+
+    return NULL;
 }
 
 ne_xml_parser *ne_xml_create(void) 
@@ -649,10 +400,10 @@ ne_xml_parser *ne_xml_create(void)
     /* 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();
+    p->current = p->root = ne_calloc(sizeof *p->root);
+    p->root->default_ns = "";
+    p->root->state = 0;
+    strcpy(p->error, _("Unknown error"));
 #ifdef HAVE_EXPAT
     p->parser = XML_ParserCreate(NULL);
     if (p->parser == NULL) {
@@ -661,67 +412,40 @@ ne_xml_parser *ne_xml_create(void)
     XML_SetElementHandler(p->parser, start_element, end_element);
     XML_SetCharacterDataHandler(p->parser, char_data);
     XML_SetUserData(p->parser, (void *) p);
+    XML_SetXmlDeclHandler(p->parser, decl_handler);
 #else
     p->parser = xmlCreatePushParserCtxt(&sax_handler, 
                                        (void *)p, NULL, 0, NULL);
     if (p->parser == NULL) {
        abort();
     }
+    p->parser->replaceEntities = 1;
 #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)
+                        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(struct ne_xml_handler));
+    struct handler *hand = ne_calloc(sizeof(struct 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);
+
+    /* If this is the first handler registered, update the
+     * base pointer too. */
+    if (p->top_handlers == NULL) {
+       p->root->handler = hand;
+       p->top_handlers = hand;
+    } else {
+       p->top_handlers->next = hand;
+       p->top_handlers = hand;
+    }
 }
 
 void ne_xml_parse_v(void *userdata, const char *block, size_t len) 
@@ -739,7 +463,8 @@ 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);
+       NE_DEBUG(NE_DBG_XML, "Not parsing %" NE_FMT_SIZE_T " bytes.\n", 
+                len);
        return;
     }
     if (len == 0) {
@@ -747,7 +472,8 @@ void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len)
        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);
+       NE_DEBUG(NE_DBG_XML, "Parsing %" NE_FMT_SIZE_T " length buffer.\n",
+                len);
        flag = 0;
     }
     /* Note, don't write a parser error if !p->valid, since an error
@@ -756,19 +482,20 @@ void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len)
     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)));
+       ne_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);
+    /* Parse errors are normally caught by the sax_error() callback,
+     * which clears p->valid. */
     if (p->parser->errNo && p->valid) {
-       /* FIXME: error handling */
-       snprintf(p->error, ERR_SIZE, "XML parse error at line %d.", 
-                ne_xml_currentline(p));
+       ne_snprintf(p->error, ERR_SIZE, "XML parse error at line %d.", 
+                   ne_xml_currentline(p));
        p->valid = 0;
     }
 #endif
@@ -781,37 +508,38 @@ int ne_xml_valid(ne_xml_parser *p)
 
 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);
+    struct element *elm, *parent;
+    struct handler *hand, *next;
 
     /* 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);
+       ne_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);
+    /* Clean up remaining elements */
+    for (elm = p->current; elm != p->root; elm = parent) {
+       parent = elm->parent;
+       destroy_element(elm);
     }
-        
+
+    /* free root element */
+    ne_free(p->root);
+
 #ifdef HAVE_EXPAT
     XML_ParserFree(p->parser);
+    if (p->encoding) ne_free(p->encoding);
 #else
     xmlFreeParserCtxt(p->parser);
 #endif
 
-    free(p);
+    ne_free(p);
 }
 
 void ne_xml_set_error(ne_xml_parser *p, const char *msg)
 {
-    snprintf(p->error, ERR_SIZE, msg);
+    ne_snprintf(p->error, ERR_SIZE, "%s", msg);
 }
 
 #ifdef HAVE_LIBXML
@@ -822,12 +550,12 @@ static void sax_error(void *ctx, const char *msg, ...)
     char buf[1024];
 
     va_start(ap, msg);
-    vsnprintf(buf, 1024, msg, ap);
+    ne_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);
+    ne_snprintf(p->error, ERR_SIZE, 
+               _("XML parse error at line %d: %s."),
+               p->parser->input->line, buf);
 
     p->valid = 0;
 }
@@ -838,25 +566,41 @@ 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)
+const char *
+ne_xml_get_attr(ne_xml_parser *p, const char **attrs, 
+               const char *nspace, 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) {
+       char *pnt = strchr(attrs[n], ':');
+
+       if (!nspace && !pnt && strcmp(attrs[n], name) == 0) {
            return attrs[n+1];
+       } else if (nspace && pnt) {
+           /* If a namespace is given, and the local part matches,
+            * then resolve the namespace and compare that too. */
+           if (strcmp(pnt + 1, name) == 0) {
+               const char *uri = resolve_nspace(p->current, 
+                                                attrs[n], pnt - attrs[n]);
+               if (uri && strcmp(uri, nspace) == 0)
+                   return attrs[n+1];
+           }
        }
     }
 
     return NULL;
 }
+
+int ne_xml_mapid(const struct ne_xml_idmap map[], size_t maplen,
+                 const char *nspace, const char *name)
+{
+    size_t n;
+    
+    for (n = 0; n < maplen; n++)
+        if (strcmp(name, map[n].name) == 0 &&
+            strcmp(nspace, map[n].nspace) == 0)
+            return map[n].id;
+    
+    return 0;
+}
index 2d2c365..58cde9a 100644 (file)
@@ -1,6 +1,6 @@
 /* 
-   Higher Level Interface to XML Parsers.
-   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+   neon XML parser interface
+   Copyright (C) 1999-2003, 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
 #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 }
-};
+#include <sys/types.h> /* for size_t */
 
-   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)
+#include "ne_defs.h"
 
-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);
+BEGIN_NEON_DECLS
 
-typedef void (*ne_xml_cdata_cb)
-    (void *userdata, const struct ne_xml_elm *s, 
-     const char *cdata, int len);
+/* The neon XML interface filters a streamed XML tree through a stack
+ * of SAX "handlers".  A handler is made up of three callbacks
+ * (start-element, char-data, end-element).  Each start-element event
+ * is passed to each handler in the stack in turn until one until one
+ * accepts the element.  This handler then receives subsequent
+ * char-data and end-element events for the element.
+ *
+ * For each new start-element event, the search up the handler stack
+ * begins with the handler for the parent element (for the root
+ * element, at the base of the stack).
+ *
+ * For each accepted element, a "state" integer is stored, which is
+ * passed to the corresponding char-data and end-element callbacks for
+ * the element.  This integer is also passed to the start-element
+ * callback of child elements so they can determine context.
+ *
+ * If no handler in the stack accepts a particular element, it (and
+ * its children, if any) is ignored. */
+
+#define NE_XML_DECLINE (0)
+#define NE_XML_ABORT (-1)
+
+/* A start-element callback for element with given namespace/name.
+ * The callback may return:
+ *   <0  =>  abort the parse (NE_XML_ABORT)
+ *    0  =>  decline this element (NE_XML_DECLINE)
+ *   >0  =>  accept this element; value is state for this element.
+ *
+ * The 'parent' integer is the state returned by the handler of the 
+ * parent element.   The attributes array gives name/value pairs
+ * in atts[n] and atts[n+1] from n=0 up to atts[n]==NULL. */
+typedef int ne_xml_startelm_cb(void *userdata, int parent,
+                               const char *nspace, const char *name,
+                               const char **atts);
+
+/* state for the root element */
+#define NE_XML_STATEROOT (0)
+
+/* Character data callback; may return non-zero to abort the parse. */
+typedef int ne_xml_cdata_cb(void *userdata, int state,
+                            const char *cdata, size_t len);
+/* End element callback; may return non-zero to abort the parse. */
+typedef int ne_xml_endelm_cb(void *userdata, int state, 
+                             const char *nspace, const char *name);
 
-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 */
+/* Create an XML 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.
- */
+/* Push a new handler on the stack of parser 'p'. 'cdata' and/or
+ * 'endelm' may be NULL; startelm must be non-NULL. */
 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);
+                         ne_xml_startelm_cb *startelm, 
+                         ne_xml_cdata_cb *cdata,
+                         ne_xml_endelm_cb *endelm,
+                         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. */
+ * any of the callbacks failed, 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. */
+/* Destroy the parser object. */
 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. */
+/* Parse the given block of input of length len. Block does not need
+ * to be NUL-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) */
+ * (This function can be passed to ne_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 */
+/* Set error string for parser. */
 void ne_xml_set_error(ne_xml_parser *p, const char *msg);
 
+/* Return the error string for the parser and never NULL. */
 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);
+/* From a start_element callback which was passed 'attrs' using given
+ * parser, return attribute of given name and namespace.  If nspace is
+ * NULL, no namespace resolution is performed. */
+const char *ne_xml_get_attr(ne_xml_parser *parser,
+                           const char **attrs, const char *nspace, 
+                           const char *name);
+
+/* Return the encoding of the document being parsed.  May return NULL
+ * if no encoding is defined or if the XML declaration has not yet
+ * been parsed. */
+const char *ne_xml_doc_encoding(const ne_xml_parser *p);
+
+/* A utility interface for mapping {nspace, name} onto an integer. */
+struct ne_xml_idmap {
+    const char *nspace, *name;
+    int id;
+};
+
+/* Return the size of an idmap array */
+#define NE_XML_MAPLEN(map) (sizeof(map) / sizeof(struct ne_xml_idmap))
+
+/* Return the 'id' corresponding to {nspace, name}, or zero. */
+int ne_xml_mapid(const struct ne_xml_idmap map[], size_t maplen,
+                 const char *nspace, const char *name);
+
+/* media type, appropriate for adding to a Content-Type header */
+#define NE_XML_MEDIA_TYPE "application/xml"
 
 END_NEON_DECLS
 
diff --git a/neon/src/sslcerts.c b/neon/src/sslcerts.c
deleted file mode 100644 (file)
index b0c972b..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * 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);
-}
index 9cb7ba7..2304dc8 100644 (file)
@@ -1,3 +1,46 @@
 tests
 *-tests
 Makefile
+request
+*.log*
+server
+regress
+compress
+*.gz
+*.lo
+*.tmp
+.libs
+acl
+auth
+lock
+basic
+ssl
+xml
+stubs
+ca
+ca-stamp
+ssigned.pem
+wildcard.*
+*.cert
+*.csr
+fqdn.pem
+wrongcn.pem
+socket
+redirect
+session
+*.out
+core*
+props
+socket-ssl
+resolve
+cookies
+*.bb
+*.da
+*.bbg
+ca*.pem
+chain.pem
+*.p12
+client.*
+output.pem
+libtest.*
+clog
index 0ab69b8..080a87f 100644 (file)
+Mon Jul  5 10:59:17 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       Add regression tests for trio of ne_compress.c bugs:
+       
+       * compress.c (reader): Validate that a size=0 call comes only
+       after the expected response data, and use struct string.
+       (do_fetch): Pass a struct string to reader and adapt for failure
+       logic.
+       (auth_cb, retry_compress_helper, retry_compress,
+       retry_notcompress): New tests.
+
+Sun Jul  4 21:55:00 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (serve_sstring_slowly, serve_sstring): Moved from
+       sockets.c, renamed from serve_string_slowly, serve_sstring.
+
+       * sockets.c: All callers updated.
+
+Tue May 18 21:00:07 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fail_ssl_request):
+       * basic.c (do_range): Fix false negatives from gcc
+       -Wformat-security.
+
+Sun May  2 21:16:45 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (regress_dates): Add regression tests.
+
+Wed Apr 14 10:45:43 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * props.c (regress, patch_regress): Add regression tests for
+       CAN-2004-0179 issues.
+
+Sun Feb 15 12:34:13 2004  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh, openssl.conf: Create new utf8subj.cert,
+       bmpsubj.cert, t61subj.cert certificates.
+
+       * ssl.c (dname_readable): Test that ne_ssl_readable_dname always
+       gives back UTF-8.
+
+Thu Nov 13 20:33:44 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (no_body_205): New test.
+
+Sat Oct 25 00:11:29 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fail_truncated_eof): Remove test.
+
+Tue Oct 21 20:29:46 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fail_tunnel, proxy_tunnel): Fix non-C89 code,
+       Radu Greab.
+
+Thu Oct  9 19:42:13 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (ssl_closure): Fix occassional spurious errors.
+       
+Tue Oct  7 20:23:35 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (ssl_server): Handle several requests if required.
+       (tunnel_header): New function.
+       (serve_tunnel): Fail with a 500 if the request included
+       an Authenticate header; take an ssl_server_args pointer
+       as userdata.
+       (fail_tunnel, proxy_tunnel): Adjust accordingly.
+       (apt_post_send): Adjust for 401 check.
+       (auth_tunnel_creds): New test.  
+
+Wed Oct  1 00:30:25 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (simple_sslv2): Regression test.
+       (ssl_server): Create SSL_CTX locally; optionally create an
+       SSLv2-only server.
+       (init_ssl): Do not create the SSL_CTX.
+
+Wed Sep 17 19:57:22 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c: Refactor around single SSL server function.
+       (ssl_server): Combination of do_ssl_response and old serve_*
+       functions.  All callers updated.
+
+Sun Sep 14 12:27:22 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (write_reset, read_reset): Skip if no RESET was
+       returned.
+
+Sun Sep 14 11:01:08 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Fix building the 'resolve' tool.
+
+Sat Sep  6 12:29:53 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh, openssl.conf: Generate altname5.cert with an IPv4
+       address in the subjectAltName attribute.
+
+       * ssl.c (ipaddr_altname): Test for IPv4 address in subjectAltName.
+
+Sat Sep  6 12:28:55 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Clear the SUFFIXES list; use standard make syntax;
+       fix build of 'basic' on some platforms.
+
+Sat Aug 30 18:59:24 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Rewrite to use libtool to build object files and
+       libtest.
+
+       * run.sh: Don't set LD_LIBRARY_PATH.
+
+Wed Jul 23 23:25:39 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * compress.c (do_fetch): Check for response truncation
+       for success case.
+       (fail_corrupt1, fail_corrupt2): New tests.
+
+Sat Jun 21 12:59:49 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (versions): Fix and enable test.
+
+Wed Jun 18 20:09:59 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (is_alive): Adapt for new socket API.
+
+       * socket.c (do_connect, addr_connect): Likewise.
+
+Tue May 20 20:14:03 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (cert_fingerprint): Fix for VPATH builds.
+
+Sat May 10 17:13:05 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * xml.c (matches): Add regression test for prefix matching bug
+       fixed in 0.18.0.
+
+Sat Apr 26 19:22:29 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (any_te_header): New function.
+
+Wed Apr 23 18:24:19 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * stubs.c (stub_ssl): Test ne_ssl_cert_import, ne_ssl_cert_export,
+       ne_ssl_cert_write stubs.
+
+Wed Apr 23 14:05:38 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (read_write): New function.
+
+Wed Apr 23 00:34:44 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (cache_cert, verify_cache): New functions.
+
+Wed Apr 23 00:14:14 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (any_ssl_request): Free the cert after passing it to
+       ne_ssl_trust_cert.
+
+Tue Apr 22 23:24:33 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (unbase64): Improve coverage.
+
+Tue Apr 22 20:25:15 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (import_export, flatten_pem, cert_compare): New functions.
+
+Tue Apr 22 18:32:43 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (b64_check, unbase64): New functions.
+       (base64): Use b64_check.
+
+Tue Apr 22 15:54:04 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (base64): Test decoding binary data which
+       contains bytes with the high bit set.
+
+Tue Apr 22 14:18:03 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (base64): Moved here...
+
+       * util-tests.c (base64): ...from here.
+
+Tue Apr 22 13:17:48 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (just_serve_string, fail_not_ssl): New functions.
+
+Tue Apr 22 13:09:13 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * stubs.c (stub_ssl): Test ne_ssl_cert_validity stub.
+
+Tue Apr 22 11:35:10 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (versions): Run test as XFAIL.
+
+Tue Apr 22 11:33:43 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (version_string): New function.
+
+Tue Apr 22 09:23:27 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (check_validity, cert_validity): New functions.
+
+Mon Apr 21 19:45:39 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (digest_md5): Replace ne_md5_buffer.
+       (md5): Use digest_md5; test 500-byte string.
+
+Mon Apr 21 18:38:02 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * xml.c (fail_parse): Call ne_xml_parse with length=0 finish
+       parse.
+
+Mon Apr 21 17:18:45 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * props.c: Add tests for ne_207.h interface and ne_simple_propfind
+       from ne_props.h.
+
+       * xml.c: Add tests for new XML interface.
+
+       * Makefile.in: Run props tests before lock since the latter is
+       implemented using the former.
+
+Mon Apr  7 22:27:18 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * stubs.c (stub_ssl): Test for ne_ssl_cert_identity stub.
+
+Mon Apr  7 22:17:56 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (cert_fingerprint): Renamed from fingerprint.
+       (check_identity, cert_identities): New functions.       
+
+Sun Apr  6 20:18:30 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * stubs.c (stub_ssl): Adjust for new clicert API.
+
+Sun Apr  6 20:12:48 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (dname_compare): Renamed from comparisons.
+       (dname_readable): New function.
+
+       * makekeys.sh: Create justmail.cert.
+
+Sun Apr  6 20:00:18 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (keypw_prompt): Removed function.
+       (init, load_client_cert, client_cert_provided): Adapt for new
+       clicert API.
+       (ccert_unencrypted): New function.
+
+Fri Apr  4 22:34:12 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_request_with_error): Refactored from
+       fail_request; check for a particular error string.
+       (fail_request): Use fail_request_with_error.
+       (invalid_response_gives_error): New function.
+       (fail_long_header): Use it.
+       (fail_corrupt_chunks): New function.
+
+Sat Mar 29 14:39:20 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (comparisons): New function.
+
+       * stubs.c (stub_ssl): Test ne_ssl_dname_cmp.
+
+Sat Mar 29 13:58:37 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh: Generate noclient.p12.
+
+       * ssl.c (load_client_cert): Test ne_ssl_clicert_name.
+
+       * stubs.c (stub_ssl): Check for ne_ssl_clicert_name stub.
+
+Sat Mar 29 13:31:35 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (load_client_cert): Test ne_ssl_clicert_owner.
+
+Fri Mar 28 22:13:55 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fingerprint): New function.
+
+       * stubs.c (stub_ssl): Check for ne_ssl_cert_digest stub.
+
+Wed Mar 26 22:52:15 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fail_missing_CN): New function.
+
+       * makekeys.sh: Generate missingcn.cert.
+
+       * openssl.conf: Allow commonName to be omitted from CSR.
+
+Wed Mar 26 22:41:48 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (load_server_certs): Renamed from load_ca; test loading
+       non-existent file.
+
+Wed Mar 26 20:38:08 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * stubs.c (stub_ssl): Updated for new SSL interface.
+
+Tue Mar 25 20:32:07 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Update tests for changes to SSL interface:
+       
+       * socket.c (init_ssl): Use ne_ssl_context_create,
+       ne_ssl_cert_read, ne_ssl_ctx_trustcert.
+       (begin): Use ne_sock_connect_ssl.
+
+       * ssl.c (serve_ssl_chained, trust_default_ca, load_client_cert,
+       check_dname, check_cert_dnames, check_cert, check_chain,
+       parse_chain, cc_check_dnames, cc_provided_dnames): New functions.
+       (serve_ccert): Always trust SERVER_CERT; optionally call
+       SSL_CTX_set_client_CA_list.
+       (any_ssl_request, load_ca, fail_truncated_eof): Use
+       ne_ssl_cert_read and ne_ssl_trust_cert.
+       (keypw_prompt): Fail if userdata is NULL.
+       (fail_load_ccerts, load_pkcs12_ccert, load_pem_ccert, check_DNs):
+       Removed functions.
+       (parse_cert): Use check_cert.
+       (client_cert_provided, client_cert_pkcs12): Rewritten for new API.
+
+       * makekeys.sh: Generate calist.pem, unclient.p12.
+
+Wed Mar 12 22:36:27 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * redirect.c (simple): Fold in tests for 30[237] redirects for
+       better coverage.
+       (no_redirect): Another test for _location returning NULL.
+
+Wed Mar 12 22:29:45 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * redirect.c (process_redir): Factored out from check_redir.
+       (no_redirect): New function.
+
+Sun Mar  9 17:46:37 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (fail_discover): New function.
+
+Sat Mar  1 10:53:58 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (authinfo): Removed.
+       (escapes): Test nothing-to-escape and invalid URI cases.
+       (compares): Gain 100% branch coverage in ne_path_compare.
+       (default_port): Test unknown scheme case.
+       (parse): Test authinfo here, and some edge cases.
+       (unparse): Fill in port if default.     
+
+Sat Mar  1 09:20:42 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (multi_init): New function.
+
+Sat Mar  1 08:04:09 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (cleaner): New function.
+
+Wed Feb 26 22:13:14 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_eof_chunk, fail_eof_badclen): New tests.
+
+Wed Feb 26 21:54:39 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (support): New function.
+       (bad_sl, accept_sl): More status-lines.
+
+Tue Feb 25 21:06:18 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (do_ssl_response): Fail if response contains
+       "Proxy-Authorization" header.
+       (apt_post_send, apt_creds, auth_proxy_tunnel): New functions.
+
+Thu Nov 28 21:25:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (te_over_clength2): New test.
+
+Sun Nov 17 18:59:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (addr_make_v4, addr_make_v6, addr_compare): New
+       functions.
+
+Fri Oct 11 00:49:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * props.c (regress): Moved from regress.c:propfind_segv; add
+       regression test for ne_props.c segfault fixed in rev 1.83.
+
+       * regress.c: Removed.
+       
+Tue Oct  8 20:06:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * xml.c (matches): Add tests that entities in attribute values are
+       dereferenced by the XML parser.
+
+Fri Oct  4 17:10:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (no_body_bad_clength, no_body_empty_clength): New
+       tests.
+       (expect_no_body): Use better paths in the requests.
+
+Tue Sep 24 21:27:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_long_header, versions, hook_create_req): New
+       functions.      
+
+Tue Sep 17 21:08:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * openssl.conf (neonca): Make 'countryName' optional in CA policy.
+       (reqDN.CNfirst): New section.
+
+       * makekeys.sh: Generate 'cnfirst.cert', which has commonName as
+       first attribute in subject DN.
+
+       * ssl.c (commonName_first): New function.
+
+Tue Sep 10 21:11:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_double_lookup): New function.
+
+Sun Aug 25 23:16:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (do_ssl_response): Add 'unclean' argument.
+       (all callers changed).
+       (serve_response_unclean, empty_truncated_eof, fail_truncated_eof):
+       New functions.
+
+Sun Aug 25 19:16:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (resolve_numeric): Test ne_addr_print too.
+
+Sun Aug 25 13:39:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * resolve.c: New file.
+
+Sun Aug 25 11:25:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (is_alive): Update for new ne_addr_* interface.
+
+Sun Aug 25 08:31:16 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (serve_truncate, ssl_truncate): New functions.
+
+Sun Aug 25 08:28:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (do_connect): New function; use new
+       ne_sock_connect/ne_addr interface.
+       (begin) [SOCKET_SSL, !SOCKET_SSL]: Use do_connect.
+       (resolve_numeric): Adjust for new ne_addr interface.
+       (resolve_ipv6): Disable test.
+
+Sat Aug 24 08:50:06 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_statusline): New function.
+
+Fri Aug 23 22:52:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (init): FAILHARD if initialization fails.
+
+Wed Aug 21 13:29:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (null_uri): Removed test.
+       (parse): More tests including IPv6 address tests; use ONCMP macro.
+       (failparse): New function.
+       (unparse): Add URI with IPv6 address.   
+
+Wed Aug 21 13:28:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (resolve_ipv6): New function.
+
+Mon Aug 19 16:59:46 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (resolve): Adapt for new ne_addr_resolve interface.
+       (resolve_numeric): New test.
+
+       * request.c (is_alive): Use new ne_addr_resolve interface.
+
+Mon Aug 19 16:57:53 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (begin): Fix handling of connect failure.
+       (TO_BEGIN): Handle errors from to_begin properly.
+
+Sun Aug 18 23:37:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (str_errors): Check return value and behaviour
+       when error string is truncated, an
+
+Sun Aug 18 23:31:51 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (str_errors): Moved to...
+
+       * string-tests.c (str_errors): here.
+
+Sun Aug 18 23:11:28 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (strnzcpy): New function.
+
+Sun Aug 18 08:18:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (caseless_match): New function.
+
+       * makekeys.sh: Create caseless.cert.
+
+Sun Aug 18 08:12:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (notdns_altname): New function.
+
+       * makekeys.sh: Create altname4.cert.
+
+       * openssl.conf (altExt4): New section.
+
+Sun Aug 18 07:42:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (multi_commonName): New function.
+       
+       * openssl.conf (req): Use distinguished_name section as
+       specificied by $REQDN.
+       (reqDN.doubleCN): New section.
+
+       * makekeys.sh: Set $REQDN; create twocn.cert.
+
+Sun Aug 18 00:47:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (accept_signed_cert): New function, factored out from
+       simple.
+       (simple): Use accept_signed_cert.
+       (subject_altname, two_subject_altname, two_subject_altname2):
+       New function.
+
+       * openssl.conf: Add extension sections altExt, altExt2, altExt3.
+
+       * makekeys.sh: Generate altname.cert, altname2.cert,
+       altname3.cert.
+
+Sat Aug 17 18:41:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh (csr_fields): New function; generate output for
+       `openssl req'.
+
+Sat Aug 17 18:27:36 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh: Add CA and REQ variables to simplify OpenSSL
+       invocation.  Pass -config to req rather than relying on installed
+       default configuration.
+
+       * openssl.conf: Add `req' and `reqDN' sections to allow use with
+       `openssl req' command.  Add CA basic constraint extention to
+       certificates used.
+
+Sat Aug 10 10:42:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh: Use openssl binary as ${OPENSSL}.
+
+       * Makefile.in: Pick up OPENSSL from configure, and pass it through
+       to makekeys.sh.
+
+Sat Aug 10 10:18:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (begin): Don't use run-time initialization.
+
+       * request.c (s_progress): Fix warnings on FreeBSD.
+
+Mon Aug  5 21:08:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (ccert_provider, client_cert_provided): New functions.
+       (fail_load_ccerts): Enable function.
+
+Sun Aug  4 22:32:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (serve_abort, retry_after_abort): New functions.
+
+Sun Aug  4 13:28:47 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (continued_header): New function.
+
+Sun Aug  4 12:54:52 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c [SOCKET_SSL] (ssl_closure): New function; use instead
+       of read_reset, write_reset for SOCKET_SSL build.
+
+Sun Aug  4 12:27:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       Build socket.c twice, once for testing over SSL connections:
+
+       * Makefile.in (socket-ssl.o, socket-ssl): New targets.
+       (SSL_TESTS): Include socket-ssl target.
+
+       * socket.c [SOCKET_SSL] (init_ssl, wrap_serve): New functions.
+       [SOCKET_SSL] (begin): Alternate implementation.
+
+Sat Aug  3 22:20:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * session.c (privates): New function.
+
+Sat Aug  3 22:20:14 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * auth.c (fail_auth_cb, tunnel_regress): New function.
+
+Sat Aug  3 22:12:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * auth.c (forget_regress): New function.
+
+Sun Jul 28 12:24:02 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (lock_timeout, submit_test, lock_shared): Use ne_concat,
+       not CONCAT? macros.
+
+       * ssl.c (init, fail_expired, fail_notvalid): Likewise.
+
+Thu Jul 25 00:04:47 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (buf_concat, buf_concat2, buf_concat3): Renamed
+       from concat, concat1, concat3).
+       (concat): New function.
+
+Sun Jul 14 11:42:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (versioning): New function.
+
+Thu Jul 11 17:24:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (no_headers): New function.
+
+Wed Jul 10 22:58:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (any_2xx_request_body): New function.
+
+Wed Jul 10 22:44:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (ptimeout_eof, ptimeout_eof2, close_not_retried,
+       serve_close2): New functions.
+       (abort_respbody): Rejoin child earlier for reliable results.
+
+Sun Jul  7 12:17:11 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (expect_eof): Better error reporting.
+       (good_close): Split from finish().
+       (finish): Use good_close.
+       (expect_write_closed, write_reset, read_reset): Add tests that
+       an ECONNRESET is treated as a SOCK_CLOSED failure.
+
+Sun Jul  7 08:38:12 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (serve_response): Use discard_body().
+
+Sun Jul  7 08:28:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (serve_expect, full_write, small_writes, large_writes,
+       echo_server, echo_expect, echo_lines): New functions.
+
+Sat Jul  6 13:11:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (serve_eof, fail_early_eof, fail_eof_continued,
+       fail_eof_headers): New functions.
+
+Sat Jul  6 08:58:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (serve_100_once, expect_100_once): New functions.
+
+Fri Jul  5 21:43:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * auth.c (username): Use the correct spelling of Aladdin.
+       (auth_hdr): Simplify debug messages.
+       (auth_serve): Fail if no Authorization header is given.
+       (basic): Check for response status.
+
+Fri Jul  5 21:41:02 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (any_2xx_request): New function.
+
+Sun Jun 30 17:10:59 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_noserver): Factor out from host_not_found.
+       (fail_lookup): Equivalent to old host_not_found.
+       (fail_connect, abort_respbody): New function.
+
+Sun Jun 30 14:32:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_chunksize): New function.
+
+Sun Jun 30 10:39:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (test_persist): Factor out from persist; take
+       response and response body as arguments.
+       (persist_http11): New function, equivalent to old persist.
+       (persist_chunked, persist_http10): New functions.
+
+Sun Jun 30 10:25:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (serve_response): Factor out from single_serve_string,
+       many_serve_string.
+       (single_serve_string, many_serve_string): Use serve_response.
+
+Sun Jun 30 09:13:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (expect_response, persist, persist_timeout,
+       multi_header): Rely on the fact that the test framework
+       will reap the server.
+       (expect_no_body, no_body_304, no_body_204, no_body_HEAD,
+       no_body_chunks): New functions.
+
+Tue Jun 25 23:05:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (trailing_header): New function.
+
+Sun Jun 23 23:00:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (no_verify): Fix sixth argument to any_ssl_request.
+
+Sun Jun 23 15:21:06 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (grind): New target.
+
+       * run.sh: Respect $HARNESS.
+
+Sun Jun 23 15:20:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * props.c: New file.
+
+Sun Jun 23 09:37:10 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh: Ignore failure from `hostname -[sdf]' commands, as
+       appropriate tests are skipped on failure.
+
+Sun Jun 23 08:33:50 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (host_not_found): Use any_request(); simplify.
+       (proxy_no_resolve): New function.
+
+Sun Jun 16 11:40:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (do_ssl_response): Succeed if connection is closed
+       by client after negotiation.
+       (serve_tunnel, fail_tunnel, proxy_tunnel): New functions.
+
+Mon Jun 10 21:18:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * redirect.c (check_redir): Await server child before returning.
+
+Sun Jun  9 13:05:25 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (DECL): Don't use run-time initialization.
+       (single_read, single_peek, small_reads, read_and_peek, line_closure,
+       larger_read, line_toolong): Use DECL, as last declaration.
+
+Sun Jun  9 13:03:36 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * compress.c (reader, do_fetch): Check that inflated data is of
+       expected length.
+
+Sun Jun  9 11:40:54 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * redirect.c (struct redir_args): Add 'path' field.
+       (any_request): Use path in Request-URI.
+       (simple, redir_303, non_absolute): Fill in path.
+       (relative_1, relative_2): New functions.
+
+Tue Jun  4 16:56:08 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (parents): Improve ne_path_parent tests.
+
+Mon Jun  3 18:22:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * cookies.c: New file.
+
+Sun Jun  2 10:06:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * basic.c (dav_capabilities): New function.
+
+Sat Jun  1 10:39:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (to_begin, to_end, peek_timeout, read_timeout,
+       readline_timeout, fullread_timeout): New functions.
+
+Sat Jun  1 10:38:13 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (read_timeout): Use sleepy_server.
+       (hung_server): Removed.
+
+Sat Jun  1 10:32:45 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (sleepy_server): New function.
+
+Thu May 30 20:00:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c (finish): New function, factored out from common code.
+       (small_reads, read_and_peek, larger_read): Use it.
+       (line_simple, line_closure, line_empty, line_toolong, line_mingle,
+       line_chunked): New functions.
+
+Sun May 26 14:54:52 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fill_uri, match_hostport, hostports): Moved functions
+       to session.c.
+
+       * session.c: New file.
+
+Fri May 24 08:14:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (match_hostport, hostports): New functions.
+
+Tue May 21 21:29:25 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * redirect.c: New file.
+
+Sun May 19 18:25:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * auth.c, lock.c, regress.c, socket.c, ssl.c, utils.c, utils.h:
+       Update for socket API change; s/sock_/ne_sock_/,
+       s/SOCK_/NE_SOCK_/.
+
+Wed May  8 19:41:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (do_ssl_response): Take response body as parameter; all
+       callers changed.
+       (serve_eof, simple_eof): New functions. 
+
+Wed May  8 17:17:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * socket.c: New file.
+
+       * sock-tests.c: Removed file.
+
+       * Makefile.in: Updated accordingly.
+
+Wed May  8 11:53:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (host_not_found): New function.
+
+Wed May  1 21:41:02 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (parse): New function.
+       (simple, simple_ssl): Adjust for ne_uri_parse port default.
+
+Tue Apr 23 21:39:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (read_timeout): Better diagnostic for test failure
+       cases.
+
+Sun Apr 14 12:00:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * basic.c (content_type): Updated to reflect default charset
+       ISO-8859-1 for text/ media types.
+
+Sun Apr  7 17:35:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * run.sh: Set MALLOC_CHECK_ so glibc's heap corruption detection
+       is enabled.
+
+Sun Apr  7 17:30:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * compress.c (do_fetch): Reset 'failed' flag to zero each time.
+
+Wed Apr  3 20:16:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (NO_BODY): Renamed from NOBODY (all callers changed).
+       (empty_header, ignore_header_ws, ignore_header_ws2): New tests.
+       (ignore_header_ws3): Renamed from ignore_header_spaces. 
+
+Tue Apr  2 21:09:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (expect_header_value): New function.
+        (ignore_header_case, ignore_header_spaces,
+       ignore_header_tabs): New tests.
+
+Mon Mar 25 21:51:24 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (multi_lock_response, lock_shared): New function.
+       (lock_compare): Factored out from discover_results.
+       (discover, lock_timeout, submit_test): Adjust for lock API
+       changes.
+
+Mon Mar 25 21:36:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fail_ssl_request): Check failure bits against
+       NE_SSL_FAILMASK.
+
+Sun Mar 10 22:07:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * stubs.c (stub_decompress, sd_reader): New function.
+
+Sun Mar 10 21:39:29 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (activelock): New function, factored out from
+       lock_response.
+       (lock_response): Use activelock; adjust argument types.
+       (make_lock): Adjusted for lock_response arg changes.
+       (discard_response, serve_discovery, discover_result, discover):
+       New functions.
+
+Wed Mar  6 22:22:04 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (submit_test): Handle failures gracefully.
+
+Wed Mar  6 21:23:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (submit_test): Update to expect an absoluteURI in If:
+       headers.
+
+Wed Mar  6 21:17:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (unparse): New function.
+
+Tue Mar  5 22:59:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (cmp): Checks for case-insensitive comparison, and
+       empty path, "/" equivalence.
+
+Mon Mar  4 01:07:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (blank_response): Add test for potential segfault
+       in strip_eol (would fail if run under Electric Fence).
+
+Sun Mar  3 20:50:01 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (make_lock, store_single, store_several, got_if_header,
+       serve_verify_if, do_request, submit_test, if_simple,
+       if_under_infinite, if_infinite_over, if_child, if_covered_child):
+       New tests.
+
+       (lock_timeout): Adjusted for API changes.
+
+Sun Mar  3 15:29:05 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (cmp_differ, cmp): New functions.
+
+Sun Mar  3 11:08:36 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fill_uri): New function.
+
+Sun Feb 17 21:31:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fqdn_match): Removed test.
+
+Sun Feb 17 20:32:16 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh: Create keypair for client cert.
+
+       * ssl.c (do_ssl_response, any_ssl_request, all callers thereof):
+       Better error handling.
+       (serve_ccert, load_pem_ccert, keypw_prompt, load_pkcs12_ccert,
+       fail_load_ccerts, client_cert_pem, client_cert_pkcs12): New
+       functions.
+
+Sun Feb 17 11:54:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * basic.c (do_range): Factored out from
+       get_range/fail_range_length.
+       (get_range, fail_range_length): Use do_range.
+       (fail_range_units, fail_range_notrange, fail_range_unsatify): New
+       tests.
+
+Sun Feb 17 11:36:00 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * basic.c (get_range, fail_range_length): New functions.
+
+Sat Feb 16 23:29:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * xml.c: New file.
+
+       * Makefile.in (DAV_TESTS): Add xml tests.
+
+Sat Feb 16 15:26:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * compress.c (do_fetch): Rename from fetch(); add 'expect_fail'
+       paramater.  (fetch): Re-implement using do_fetch.
+       (fail_trailing, fail_bad_csum, fail_truncate): New functions.
+
+       * Makefile.in (trailing.gz, truncated.gz, badcsum.gz): New helper
+       files.
+
+Thu Feb 14 19:09:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (everywhere): Simplify use of expect_response.
+
+Thu Feb 14 19:05:48 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (ignore_bad_headers): New function.
+
+Mon Feb 11 22:06:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * makekeys.sh: If the hostname command is clever enough to give
+       FQDN, hostname, domainname, then create wildcard.cert; cert with a
+       wildcard commonName.
+
+       * ssl.c (wildcard_match): New function
+
+Mon Feb 11 21:55:52 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (any_ssl_request): Take session pointer, don't
+       initialize here.  (DEFSESS): New macro.
+       (everywhere): Use DEFSESS rather than passing pointer-to-
+       session pointer.        
+
+Mon Feb 11 20:44:44 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fqdn_match): Test for FQDN matching against non-qualified
+       FQDN.
+       (makekeys.sh): Create server cert with FQDN.
+
+Sun Feb 10 12:36:55 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (chunk_oversize): New function.
+
+Sat Feb  9 21:12:47 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (reason_phrase): New function.
+
+Sat Feb  9 16:50:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (read_timeout, hung_server): New functions.
+
+Thu Feb  7 22:58:31 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (cache_verify, no_verify, count_vfy): New functions.
+
+Thu Feb  7 19:39:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (any_ssl_request): Take server function as argument: all
+       callers changed.
+       (fail_ssl_request): Renamed from failreq; uses any_ssl_request.
+
+Wed Feb  6 20:43:32 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (load_ca): New function.
+
+Wed Feb  6 20:36:15 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (any_ssl_request): Make ca_cert and verify_fn arguments
+       optional.
+       (trustall): Removed function.
+       (simple): Use the CA cert; no need for a verify function.
+       (parse_cert): Don't give a CA cert, force use of verify function.
+       (failreq): Bug fix, don't trust server cert as CA.
+       (fail_wrongCN, fail_notvalid, fail_expired): Pass server cert
+       as CA cert server cert is self-signed.
+
+Tue Feb  5 20:33:42 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (fail_untrusted_ca, fail_self_signed): New tests.
+       (fail_serve): New function.
+       (failreq, any_ssl_request): Take ca cert argument.
+       (check_DNs, trustall, get_failures): Adjust for new verify
+       callback interface.
+
+Sat Feb  2 14:18:11 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c (do_ssl_response): Factored out from serve_ssl.
+       (serve_ssl): Use do_ssl_response.
+       (serve_scache, session_cache): New functions.
+
+Thu Jan 31 21:09:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (ca-stamp): New target.
+
+       * makekeys.sh: New helper script.
+
+       * ssl.c (parse_cert, fail_wrongCN, fail_expired, fail_notvalid):
+       New tests.
+       (any_ssl_request, trustall, check_DNs, failreq): New auxiliaries.
+
+Thu Jan 31 20:42:38 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * wrongcn.pem, notvalid.pem, expired.pem, server.key: New files.
+
+       * Makefile.in: Remove targets to generate certs.
+
+Wed Jan 30 21:15:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (wrongcn.pem): New target.
+
+Wed Jan 30 19:58:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c: Updated for ne_buffer API change.
+
+Sat Jan 26 11:23:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Pick up appropriate TESTS, HELPERS from configure.
+       (ssltests*, davtests*): Remove crud.
+
+       * compress.c: Presume zlib support present if built.
+
+Sun Jan 20 23:29:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * ssl.c: New file.
+
+       * Makefile.in (ssltests-no, ssltests-yes, server.pem, server.key):
+       New targets.
+       (check): Conditionally run SSL tests.
+
+Sun Jan 20 13:20:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (davtests-no, davtests-yes): Separate test programs
+       which require DAV support; only run if DAV is enabled.
+
+       * Makefile.in (test): Pass SRCDIR env var through to run.sh.
+
+       * run.sh: Pass SRCDIR as argv[1] to test programs.
+
+       * compress.c (init): New function.  Use 'newsfn' global for
+       filename of NEWS file.
+
+Sun Jan 20 13:06:40 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Fixes for VPATH build
+
+Mon Jan 14 01:58:39 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * basic.c (content_type): Add harsher charset handling tests.
+
+Sun Jan 13 14:01:57 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c (lock_timeout): Use make_session. 
+
+       * acl.c (test_acl): Use make_session.
+
+       * auth.c (basic, retries): Use make_session.
+
+Sun Jan 13 14:01:13 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (make_session): New function.
+
+Sun Jan 13 14:00:34 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * basic.c (content_type): Rename ctype to ct; check if charset is
+       unexpectedly set.
+
+Sun Jan 13 13:58:07 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * basic.c: New file.
+
+       * Makefile.in: Add `basic' test suite.
+
+Mon Jan  7 22:05:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Don't pass CFLAGS to CC when linking.
+
+Mon Jan  7 21:46:03 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * lock.c: New file.
+
+       * Makefile.in: Add 'lock' to TESTS, build lock.
+
+Mon Jan  7 21:17:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * skeleton.c: Add skeleton test suite.
+
+Tue Jan  1 21:47:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Use CPPFLAGS correctly.
+
+Sun Dec  9 14:02:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (ONCMP): New macro.  (everywhere): Use it.
+       (grow): Add ne_buffer_grow test.
+
+Sun Dec  9 13:12:27 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (concat2, concat3): New ne_buffer_concat tests.
+
+Sat Dec  1 18:35:29 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (any_request): Don't set the error context.
+
+Sat Dec  1 12:21:48 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * auth.c (retry_failure, retry_fail_cb, retry_fail_serve): New
+       functions.
+
+Tue Nov 27 21:24:22 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (s_progress, provide_progress, send_progress): New
+       functions.
+
+Sun Nov 18 19:11:23 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * auth.c (send_response): New function.  (auth_serve): Simplify
+       using send_response.  (retry_serve, retry_cb, retries): New
+       functions.
+
+Sat Nov 17 22:32:29 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * auth.c (auth_serve, basic): Simplify, use a persistent
+       connection and any_request() to work with --disable-dav builds.
+
+Sat Nov 17 22:30:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (any_request): New function.
+
+Sun Oct 28 19:38:05 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in: Use explicit link rules.
+
+Fri Oct 26 20:08:33 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (persist_timeout): Test behaviour when connection
+       closes after between 1 and 10 requests.
+
+Fri Oct 26 20:04:27 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c (many_serve_string): New function.
+
+Sun Oct  7 17:48:53 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.c: New file.
+
+       * request.c (single_serve_string): Moved to utils.c.
+
+       * Makefile.in: Link utils.o into all libtest.a. Move libtest.a
+       into this directory.
+
+Sun Oct  7 15:01:47 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (persist, persist_timeout, serve_twice, is_alive): New
+       functions.  (closed_connection): Avoid race condition.
+
+Sat Oct  6 14:33:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (prepare_request, finish_request): Renamed from
+       make_request, destroy_request.  (skip_interim_1xx, skip_many_1xx,
+       skip_1xx_hdrs): New functions.
+
+Wed Oct  3 00:03:33 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (fail_request): Optionally include a request body, and
+       optionally presume the server runs "forever".  (all callers
+       changed).  (serve_close, closed_connection): New function.
+
+Sat Sep 29 14:08:16 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * compress.c (fetch): Update for new decompression API.
+
+Sat Sep 29 11:21:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * compress.c: New file.
+
+       * Makefile.in: Build compress test, and some its helpers.  Add
+       -lneon to LIBS, and pick up NEON_CFLAGS.
+
+Thu Sep 27 20:31:51 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * utils.h: New file.
+
+       * request.c: Moved ONREQ() into utils.h
+
+Mon Aug 27 00:34:56 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * regress.c: New file.
+
+Mon Aug 27 00:33:13 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (discard_request): Moved into common/child.c.
+       (make_request, destroy_request): Convenience functions.
+       (serve_non_http, not_http): New test.
+
+Sun Jun 24 22:15:46 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * test.[ch], child.[ch]: Moved into 'common' subdir.
+
+       * Makefile.in: Updated likewise.
+
+Tue Jun 19 22:00:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (parse_dates): Test date parsers.
+
+Sun Jun 10 17:36:11 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (infinite_headers, unbounded_headers): New test.
+
+Sun Jun 10 16:38:53 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c [HAVE_PIPE]: Use a pipe between child and parent to know
+       when the child is ready to accept connections.  Avoids boring
+       sleep()ing.
+
+Fri Jun  8 21:19:35 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (segv, main): Remove SEGV handler in favour of useful
+       core dumps.
+
+Mon Jun  4 01:15:52 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (server_socket): Set socket family correctly.
+
+Thu May 31 08:58:41 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (md5_alignment): New test for MD5 alignment issue
+       on Sparc.
+
+Thu May 31 00:40:43 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (minisleep): Just sleep for a second anyway.
+
+Thu May 31 00:19:16 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (server_socket): Casts for bind and setsockopt arguments.
+
+Thu May 31 00:02:21 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (send_bodies): Test callback-provided request bodies.
+
+Wed May 30 22:37:08 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (child_segv): New function.  (in_child): Install
+       different SEGV handler.  (segv): Sleep so the re-raised SEGV
+       signal gets handled and we dump core.
+
+Wed May 30 19:24:32 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (send_bodies): New test for sending request bodies.
+
+Wed May 16 21:19:49 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (expect_response): Renamed, fold together
+       single_request and do_get_request.  (all callers changed)
+
+Wed May 16 20:59:19 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (construct_get, run_request): New functions.
+       (fold_headers, fold_many_headers, multi_header): New tests.
+
+Sat May 12 17:37:36 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * server.c: Renamed from http-tests.c.
+
+Sat May 12 17:35:05 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (minisleep): New function.  (spawn_server, reap_server):
+       New functions.  (server_child): Call in_child.
+
+Sat May 12 17:33:57 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Open two log files for debugging messages.
+       (in_child): Switch to debug using child log.
+
+Sat May 12 11:18:18 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Call sock_init.  (segv): Re-raise SEGV signal
+       after printing message.
+
+Mon May  7 10:38:50 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (chunk_syntax_1, chunk_syntax_2, chunk_syntax_3,
+       chunk_syntax_4, chunk_syntax_5): Split down from chunk_syntax.
+
+Mon May  7 10:37:38 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (base64): Update for ne_base64() changes.  Add
+       tests for binary data.
+
+Sun May  6 23:55:36 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.h (ON): Use global buffer 'on_err_buf'.  Make 'name'
+       variable public.
+
+Sun May  6 23:53:06 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (single_serve_string): General version of
+       single_serve_*.  (single_request): Pass in expected response body.
+       (single_get_*): Use new single_request/single_serve_string.
+       (chunk_syntax): Add some tests for chunk syntax.
+
+Sun May  6 22:29:36 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c, child.h: New files, split down from request.c.
+
+Sun May  6 21:53:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (spawn_server): Sleep for a while to let the server
+       get going.  (do_request): Use passed parameters when creating
+       request.
+
+Sun May  6 21:34:27 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c (spawn_server): Use callback to handle the server side
+       of connection.  (single_request): New function.  (single_get_eof,
+       single_get_clength, single_get_chunked): New functions.
+       (reap_server): New function.
+
+Sun May  6 20:02:32 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * request.c: New file.
+
+Wed May  2 12:08:53 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c (token1, token2, nulls, empty, quoted, badquotes,
+       shave, combo): New tests for ne_token and ne_shave.
+
+Wed May  2 12:04:52 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * string-tests.c: Updated for sbuffer -> ne_buffer changes.
+
+Wed May  2 01:08:45 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (check): Alias for test goal.
+
+Wed May  2 01:08:36 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (segv): Disable SEGV handler once handling it.
+
 Sun Apr 29 14:57:59 2001  Joe Orton  <joe@manyfish.co.uk>
 
        * uri-tests.c (slash): Check behaviour of passing zero-length URI.
index 7a51457..9d89e42 100644 (file)
+# Makefile for neon test suite.
 
 SHELL = @SHELL@
-CFLAGS = @CFLAGS@ -I. -I../src
-LDFLAGS = -L. @LDFLAGS@ -L../src -L../src/.libs
+CPPFLAGS = @CPPFLAGS@ -I. -I$(top_srcdir)/src -I$(top_srcdir)/test/common
+CFLAGS = @CFLAGS@ @NEON_CFLAGS@
+LDFLAGS = @LDFLAGS@
 DEFS = @DEFS@
+
 top_builddir = ..
 top_srcdir = @top_srcdir@
-
+srcdir = @srcdir@
 VPATH = @srcdir@
 
 AR = ar
 
 RANLIB = @RANLIB@
-LIBS = @LIBS@ @NEON_LIBS@
+LIBS = $(LIBTEST)
 CC = @CC@
-
-TESTS = uri-tests util-tests string-tests sock-tests http-tests
-
-LIBTEST = libtest.a
-
+OPENSSL = @OPENSSL@
+
+HELPERS = @HELPERS@
+BASIC_TESTS = uri-tests util-tests string-tests session socket \
+       request auth basic stubs redirect
+ZLIB_TESTS = compress
+ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz corrupt1.gz corrupt2.gz
+DAV_TESTS = xml acl props lock
+SSL_TESTS = socket-ssl ssl
+SSL_HELPERS = ca-stamp
+TESTS = @TESTS@
+VALGRIND = valgrind --gdb-attach=yes --leak-check=yes
+
+# Make every object depend on libneon.la to force a rebuild on any src/* changes
+OBJDEPS = $(srcdir)/common/tests.h $(srcdir)/common/child.h $(srcdir)/utils.h \
+       $(top_builddir)/config.h $(top_builddir)/src/libneon.la
+# Test program just depends on libtest
+DEPS = $(LIBTEST)
+
+LIBTEST = libtest.la
+LIBNEON = $(top_builddir)/src/libneon.la
+
+LIBTOOL = @LIBTOOL@ --silent
+LINK = $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -no-install
+COMPILE = $(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .lo .c
+
+# By default, compile but don't run the tests.
 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
+       rm -f $(TESTS) $(HELPERS) *.*o common/*.*o libtest.a *.log
+       rm -rf ca .libs
+       rm -f ca-stamp client.key *.csr ssigned.pem wrongcn.pem \
+          server.cert client.cert client.p12 *.cert
+
+check: $(TESTS) $(HELPERS)
+       @SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh $(TESTS)
+
+grind: $(TESTS) $(HELPERS)
+       @SRCDIR=$(srcdir) HARNESS="$(VALGRIND)" $(SHELL) $(srcdir)/run.sh $(TESTS)
+
+NEWS = $(top_srcdir)/NEWS
+
+file1.gz: $(NEWS)
+       gzip -c --no-name $(NEWS) > $@
+
+file2.gz: $(NEWS)
+       gzip -c --name $(NEWS) > $@
+
+# gzip file with trailing bytes.
+trailing.gz: $(NEWS)
+       gzip -c --no-name $(NEWS) > $@
+       echo "hello, world" >> $@
+
+truncated.gz: file1.gz
+       dd if=file1.gz of=$@ bs=2048 count=2
+
+badcsum.gz: file1.gz
+       dd of=$@ if=file1.gz bs=1 count=`perl -e 'printf "%d", (stat("file1.gz"))[7] - 8;'`
+       echo 'broken!' >> $@
+
+corrupt1.gz: file1.gz
+       dd of=$@ if=file1.gz bs=1 count=500
+       cat $(NEWS) >> $@
+
+corrupt2.gz: 
+       cat $(NEWS) > $@
+
+# Dummy target to create the CA keys etc.  makekeys stderr is redirected
+# since it changes for every invocation; not helpful for regression
+# testing.
+ca-stamp: $(srcdir)/makekeys.sh $(srcdir)/openssl.conf
+       rm -rf ca
+       OPENSSL=$(OPENSSL) \
+        $(SHELL) $(srcdir)/makekeys.sh $(srcdir) 2>makekeys.out
+       @echo timestamp > ca-stamp
+
+Makefile: $(srcdir)/Makefile.in
+       cd .. && ./config.status test/Makefile
+
+LIBOBJS = common/tests.lo common/child.lo utils.lo
+
+$(LIBTEST): $(LIBOBJS)
+       $(LINK) -o $(LIBTEST) $(LIBOBJS) $(LIBNEON)
+
+.c.lo:
+       $(COMPILE) -c $< -o $@
+
+.lo:
+       $(LINK) -o $@ $< $(LIBS)
+
+# Recompile socket.c with SOCKET_SSL defined
+socket-ssl.lo: $(srcdir)/socket.c $(HDRS)
+       $(COMPILE) -DSOCKET_SSL -c $(srcdir)/socket.c -o $@
+
+socket-ssl: socket-ssl.lo $(LIBTEST)
+       $(LINK) -o $@ socket-ssl.lo $(LIBS)
+
+resolve: resolve.lo $(LIBNEON)
+       $(LINK) -o $@ resolve.lo $(LIBNEON)
+
+auth.lo: $(srcdir)/auth.c $(OBJDEPS)
+uri-tests.lo: $(srcdir)/uri-tests.c $(OBJDEPS)
+util-tests.lo: $(srcdir)/util-tests.c $(OBJDEPS)
+string-tests.lo: $(srcdir)/string-tests.c $(OBJDEPS)
+socket.lo: $(srcdir)/socket.c $(OBJDEPS)
+server.lo: $(srcdir)/server.c $(OBJDEPS)
+request.lo: $(srcdir)/request.c $(OBJDEPS)
+regress.lo: $(srcdir)/regress.c $(OBJDEPS)
+compress.lo: $(srcdir)/compress.c $(OBJDEPS)
+acl.lo: $(srcdir)/acl.c $(OBJDEPS)
+utils.lo: $(srcdir)/utils.c $(OBJDEPS)
+stubs.lo: $(srcdir)/stubs.c $(OBJDEPS)
+props.lo: $(srcdir)/props.c $(OBJDEPS)
+session.lo: $(srcdir)/session.c $(OBJDEPS)
+redirect.lo: $(srcdir)/redirect.c $(OBJDEPS)
+basic.lo: $(srcdir)/basic.c $(OBJDEPS)
+ssl.lo: $(srcdir)/ssl.c $(OBJDEPS)
+cookies.lo: $(srcdir)/cookies.c $(OBJDEPS)
+
+auth: auth.lo $(DEPS)
+basic: basic.lo $(DEPS)
+uri-tests: uri-tests.lo $(DEPS)
+util-tests: util-tests.lo $(DEPS)
+string-tests: string-tests.lo $(DEPS)
+socket: socket.lo $(DEPS)
+server: server.lo $(DEPS)
+request: request.lo $(DEPS)
+regress: regress.lo $(DEPS)
+compress: compress.lo $(DEPS)
+acl: acl.lo $(DEPS)
+utils: utils.lo $(DEPS)
+stubs: stubs.lo $(DEPS)
+props: props.lo $(DEPS)
+session: session.lo $(DEPS)
+redirect: redirect.lo $(DEPS)
+basic: basic.lo $(DEPS)
+ssl: ssl.lo $(DEPS)
+xml: xml.lo $(DEPS)
+lock: lock.lo $(DEPS)
+cookies: cookies.lo $(DEPS)
index 1b1efa7..14008b1 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Dummy ACL tests
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2003, 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
 #include "child.h"
 #include "utils.h"
 
+#ifdef NEON_NODAV
+
+static int skip(void)
+{ 
+    t_context("built without WebDAV support");
+    return SKIP;
+}
+
+ne_test tests[] = { T(skip), T(NULL) };
+
+#else
+
 /**** 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 *sess;
 
-    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"));
+    CALL(make_session(&sess, 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());
+    ne_session_destroy(sess);
     
     return OK;
 }
 
 static int grant_all(void)
 {
-    ne_acl_entry e;
+    ne_acl_entry e = {0};
 
     e.apply = ne_acl_all;
     e.type = ne_acl_grant;
@@ -56,7 +68,7 @@ static int grant_all(void)
 
 static int deny_all(void)
 {
-    ne_acl_entry e;
+    ne_acl_entry e = {0};
 
     e.apply = ne_acl_all;
     e.type = ne_acl_deny;
@@ -68,7 +80,7 @@ static int deny_all(void)
 
 static int deny_one(void)
 {
-    ne_acl_entry e;
+    ne_acl_entry e = {0};
 
     e.apply = ne_acl_href;
     e.type = ne_acl_deny;
@@ -81,7 +93,7 @@ static int deny_one(void)
 
 static int deny_byprop(void)
 {
-    ne_acl_entry e;
+    ne_acl_entry e = {0};
 
     e.apply = ne_acl_property;
     e.type = ne_acl_deny;
@@ -99,3 +111,5 @@ ne_test tests[] = {
     T(deny_byprop),
     T(NULL)
 };
+
+#endif /* NEON_NODAV */
index 729f55b..30fe88e 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Authentication tests
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2003, 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
@@ -37,7 +37,7 @@
 #include "child.h"
 #include "utils.h"
 
-char username[BUFSIZ], password[BUFSIZ];
+static const char username[] = "Aladdin", password[] = "open sesame";
 static int auth_failed;
 
 static const char www_wally[] = "WWW-Authenticate: Basic realm=WallyWorld";
@@ -53,9 +53,9 @@ static int auth_cb(void *userdata, const char *realm, int 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);
+    NE_DEBUG(NE_DBG_HTTP, "Got auth header: [%s]\nWanted header:   [%s]\n"
+            "Result: %d\n", value, B, auth_failed);
 #undef B
 }
 
@@ -63,7 +63,7 @@ static void auth_hdr(char *value)
  * 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)
+static int send_response(ne_socket *sock, const char *hdr, int code, int eoc)
 {
     char buffer[BUFSIZ];
     
@@ -80,12 +80,14 @@ static int send_response(nsocket *sock, const char *hdr, int code, int eoc)
        strcat(buffer, "Content-Length: 0" EOL EOL);
     }
        
-    return sock_send_string(sock, buffer);
+    return SEND_STRING(sock, buffer);
 }
 
-static int auth_serve(nsocket *sock, void *userdata)
+/* Server function which sends two responses: first requires auth,
+ * second doesn't. */
+static int auth_serve(ne_socket *sock, void *userdata)
 {
-    auth_failed = 0;
+    auth_failed = 1;
 
     /* Register globals for discard_request. */
     got_header = auth_hdr;
@@ -102,29 +104,19 @@ static int auth_serve(nsocket *sock, void *userdata)
 
 static int basic(void)
 {
-    int ret;
-    ne_session *sess = ne_session_create();
-
-    ne_session_server(sess, "localhost", 7777);
+    ne_session *sess;
 
+    CALL(make_session(&sess, auth_serve, NULL));
     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");
+    CALL(any_2xx_request(sess, "/norman"));
 
     ne_session_destroy(sess);
-
     CALL(await_server());
-
-    ONREQ(ret);
     return OK;
 }
 
-static int retry_serve(nsocket *sock, void *ud)
+static int retry_serve(ne_socket *sock, void *ud)
 {
     discard_request(sock);
     send_response(sock, www_wally, 401, 0);
@@ -152,6 +144,18 @@ static int retry_serve(nsocket *sock, void *ud)
 
     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, www_wally, 401, 0);
+
+    discard_request(sock);
+    send_response(sock, www_wally, 401, 0);
+
+    discard_request(sock);
+    send_response(sock, NULL, 200, 0);
     
     return OK;
 }
@@ -191,6 +195,14 @@ static int retry_cb(void *userdata, const char *realm, int tries,
            return 1;
        }
        break;
+    case 4:
+    case 5:
+       if (tries > 1) {
+           t_context("Attempt counter reached #%d", tries);
+           *count = -1;
+           return 1;
+       }
+       return tries;
     default:
        t_context("Count reached %d!?", *count);
        *count = -1;
@@ -201,13 +213,12 @@ static int retry_cb(void *userdata, const char *realm, int tries,
 /* Test that auth retries are working correctly. */
 static int retries(void)
 {
-    ne_session *sess = ne_session_create();
+    ne_session *sess;
     int count = 0;
     
-    ne_session_server(sess, "localhost", 7777);
-    ne_set_server_auth(sess, retry_cb, &count);
+    CALL(make_session(&sess, retry_serve, NULL));
 
-    CALL(spawn_server(7777, retry_serve, NULL));
+    ne_set_server_auth(sess, retry_cb, &count);
 
     /* This request will be 401'ed twice, then succeed. */
     ONREQ(any_request(sess, "/foo"));
@@ -231,6 +242,18 @@ static int retries(void)
     /* auth_cb will have set up context. */
     CALL(count != 4);
 
+    /* First request is 401'ed by the server at both attempts. */
+    ONV(any_request(sess, "/foo") != NE_AUTH,
+       ("auth succeeded, should have failed: %s", ne_get_error(sess)));
+
+    count++;
+
+    /* Second request is 401'ed first time, then will succeed if
+     * retried.  0.18.0 didn't reset the attempt counter though so 
+     * this didn't work. */
+    ONV(any_request(sess, "/foo") == NE_AUTH,
+       ("auth failed on second try, should have succeeded: %s", ne_get_error(sess)));
+
     ne_session_destroy(sess);
 
     CALL(await_server());
@@ -238,6 +261,37 @@ static int retries(void)
     return OK;
 }
 
+/* crashes with neon <0.22 */
+static int forget_regress(void)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    ne_forget_auth(sess);
+    ne_session_destroy(sess);
+    return OK;    
+}
+
+static int fail_auth_cb(void *ud, const char *realm, int attempt, 
+                       char *un, char *pw)
+{
+    return 1;
+}
+
+/* this may trigger a segfault in neon 0.21.x and earlier. */
+static int tunnel_regress(void)
+{
+    ne_session *sess = ne_session_create("https", "localhost", 443);
+    ne_session_proxy(sess, "localhost", 7777);
+    ne_set_server_auth(sess, fail_auth_cb, NULL);
+    CALL(spawn_server(7777, single_serve_string,
+                     "HTTP/1.1 401 Auth failed.\r\n"
+                     "WWW-Authenticate: Basic realm=asda\r\n"
+                     "Content-Length: 0\r\n\r\n"));
+    any_request(sess, "/foo");
+    ne_session_destroy(sess);
+    CALL(await_server());
+    return OK;
+}
+
 /* test digest auth 2068-style. */
 
 /* test digest auth 2617-style. */
@@ -252,7 +306,10 @@ static int retries(void)
 /* proxy auth, proxy AND origin */
 
 ne_test tests[] = {
+    T(lookup_localhost),
     T(basic),
     T(retries),
+    T(forget_regress),
+    T(tunnel_regress),
     T(NULL)
 };
index 84be1ae..9ffe0e1 100644 (file)
@@ -1,6 +1,6 @@
 /* 
-   neon test suite
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Tests for high-level HTTP interface (ne_basic.h)
+   Copyright (C) 2002-2004, 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
@@ -29,6 +29,8 @@
 #include <unistd.h>
 #endif
 
+#include <fcntl.h>
+
 #include "ne_basic.h"
 
 #include "tests.h"
 static int content_type(void)
 {
     int n;
-    struct {
+    static const struct {
        const char *value, *type, *subtype, *charset;
     } ctypes[] = {
-       { "text/plain", "text", "plain", NULL },
-       { "text/plain  ", "text", "plain", NULL },
+       { "foo/bar", "foo", "bar", NULL },
+       { "foo/bar  ", "foo", "bar", NULL },
        { "application/xml", "application", "xml", NULL },
+       /* text/ subtypes default to charset ISO-8859-1. */
+       { "text/lemon", "text", "lemon", "ISO-8859-1" },
 #undef TXU
 #define TXU "text", "xml", "utf-8"
        /* 2616 doesn't *say* that charset can be quoted, but bets are
@@ -60,7 +64,7 @@ static int content_type(void)
        { "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 },
+       { "foo/lemon; charset=\"utf-8", "foo", "lemon", NULL },
        { NULL }
     };
 
@@ -92,10 +96,133 @@ static int content_type(void)
     return OK;
 }
 
-ne_test tests[] = {
-    T(content_type), /* test functions here */
+/* Do ranged GET for range 'start' to 'end'; with 'resp' as response.
+ * If 'fail' is non-NULL, expect ne_get_range to fail, and fail the
+ * test with given message if it doesn't. */
+static int do_range(off_t start, off_t end, const char *fail,
+                   char *resp)
+{
+    ne_session *sess;
+    ne_content_range range = {0};
+    int fd, ret;
+
+    CALL(make_session(&sess, single_serve_string, resp));
+    
+    range.start = start;
+    range.end = end;
+    
+    fd = open("/dev/null", O_WRONLY);
+    
+    ret = ne_get_range(sess, "/foo", &range, fd);
+
+    close(fd);
+    CALL(await_server());
+    
+    if (fail) {
+#if 0
+       t_warning("error was %s", ne_get_error(sess));
+#endif
+       ONV(ret == NE_OK, ("%s", fail));
+    } else {
+       ONREQ(ret);
+    }
+    
+    ne_session_destroy(sess);
+    return OK;
+}
+
+static int get_range(void)
+{
+    return do_range(1, 10, NULL,
+                   "HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
+                   "Content-Range: bytes 1-10\r\n"
+                   "Content-Length: 10\r\n\r\nabcdefghij");
+}
+
+static int fail_range_length(void)
+{
+    return do_range(1, 10, "range response length mismatch should fail",
+                   "HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
+                   "Content-Range: bytes 1-2\r\n"
+                   "Content-Length: 2\r\n\r\nab");
+}
+
+static int fail_range_units(void)
+{
+    return do_range(1, 2, "range response units check should fail",
+                   "HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
+                   "Content-Range: fish 1-2\r\n"
+                   "Content-Length: 2\r\n\r\nab");
+}
+
+static int fail_range_notrange(void)
+{
+    return do_range(1, 2, "non-ranged response should fail",
+                   "HTTP/1.1 200 Widgets\r\n" "Connection: close\r\n"
+                   "Content-Range: bytes 1-2\r\n"
+                   "Content-Length: 2\r\n\r\nab");
+}
 
-    /* end of test functions. */
+static int fail_range_unsatify(void)
+{
+    return do_range(1, 2, "unsatisfiable range should fail",
+                   "HTTP/1.1 416 No Go\r\n" "Connection: close\r\n"
+                   "Content-Length: 2\r\n\r\nab");
+}
+
+static int dav_capabilities(void)
+{
+    static const struct {
+       const char *hdrs;
+       unsigned int class1, class2, exec;
+    } caps[] = {
+       { "DAV: 1,2\r\n", 1, 1, 0 },
+       { "DAV: 1 2\r\n", 0, 0, 0 },
+       /* these aren't strictly legal DAV: headers: */
+       { "DAV: 2,1\r\n", 1, 1, 0 },
+       { "DAV:  1, 2  \r\n", 1, 1, 0 },
+       { "DAV: 1\r\nDAV:2\r\n", 1, 1, 0 },
+       { NULL, 0, 0, 0 }
+    };
+    char resp[BUFSIZ];
+    int n;
+
+    for (n = 0; caps[n].hdrs != NULL; n++) {
+       ne_server_capabilities c = {0};
+       ne_session *sess;
+
+       ne_snprintf(resp, BUFSIZ, "HTTP/1.0 200 OK\r\n"
+                   "Connection: close\r\n"
+                   "%s" "\r\n", caps[n].hdrs);
+
+       CALL(make_session(&sess, single_serve_string, resp));
+
+       ONREQ(ne_options(sess, "/foo", &c));
+
+       ONV(c.dav_class1 != caps[n].class1,
+           ("class1 was %d not %d", c.dav_class1, caps[n].class1));
+       ONV(c.dav_class2 != caps[n].class2,
+           ("class2 was %d not %d", c.dav_class2, caps[n].class2));
+       ONV(c.dav_executable != caps[n].exec,
+           ("class2 was %d not %d", c.dav_executable, caps[n].exec));
+
+       CALL(await_server());
+
+        ne_session_destroy(sess);
+    }
+
+    return OK; 
+}
+
+ne_test tests[] = {
+    T(lookup_localhost),
+    T(content_type),
+    T(get_range),
+    T(fail_range_length),
+    T(fail_range_units),
+    T(fail_range_notrange),
+    T(fail_range_unsatify),
+    T(dav_capabilities),
     T(NULL) 
 };
 
diff --git a/neon/test/common/.cvsignore b/neon/test/common/.cvsignore
new file mode 100644 (file)
index 0000000..70f06c3
--- /dev/null
@@ -0,0 +1,2 @@
+*.lo
+.libs
index a7ca54d..7e89c08 100644 (file)
@@ -1,3 +1,209 @@
+Tue Oct  7 21:19:56 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (close_socket): New function.
+       (server_child, spawn_server_repeat): Use it.
+
+Fri Jul 25 12:13:59 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Add support for test type which is expected to fail
+       memory leak checks.
+       
+       * tests.h (T_XLEAKY, T_EXPECT_LEAKS): New defines.
+
+       * test.c (main) [NEON_MEMLEAK]: If T_EXPECT_LEAKS is set, fail if
+       the test did not leak memory.
+
+Wed Jun 18 20:10:45 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (server_child, spawn_server_repeat): Adapt for new
+       socket API.
+
+Sun Mar  9 17:52:11 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * test.h (T_EXPECT_FAIL): New constant.
+       (T_XFAIL): New test function wrapper.
+       
+       * tests.c (main): Handle expected failures.
+
+Sat Mar  1 21:04:35 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       Extend the ne_test structure with a 'flags' field which can
+       optionally request leak checking at run-time.
+       
+       * tests.h (ne_test): Add 'flags' field.
+       (T_CHECK_LEAKS): New flag.
+       (T): Use T_CHECK_LEAKS flag by default.
+       (T_LEAKY): Like T, but with no flags set.
+
+       * tests.c (main) [NEON_MEMLEAK]: If leak checking is requested,
+       if a test passes, but leaks memory, fail the test.
+
+Wed Feb 26 21:52:15 2003  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Test the "disable debugging" mode of
+       ne_debug_init, NE_DBG_FLUSH, and a ne_debug() with no output.
+
+Fri Aug 23 22:54:35 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Call ne_sock_init after ne_debug_init, so that
+       debugging messages are caught from ne_sock_init.  Print a warning
+       message if ne_sock_init fails.
+
+Wed Aug 21 13:29:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.h (ONCMP): New macro.
+
+Mon Aug 19 16:53:20 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (lookup_localhost): Just use inet_addr to resolve
+       127.0.0.1.
+
+Sun Aug 18 13:50:30 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (TEST_DEBUG): Add NE_DBG_SSL.
+
+Sat Aug 10 10:19:18 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (server_send): Fix declaration.
+       (discard_body): Use NE_FMT_SSIZE_T for print ssize_t's.
+
+Sat Jul  6 08:42:37 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (discard_body): New function.
+
+Sun Jun 30 10:26:33 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (server_send): New function.
+       (discard_request): Fail with appropriate error.
+
+Sun Jun 30 09:03:51 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Reap server after each test has run.
+
+Sun Jun 30 09:00:43 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (reap_server): Set `child' to 0 so child can't be reaped
+       twice.
+
+Sun Jun 23 12:09:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (serve_file): Use large buffer when sending in chunked
+       mode to support large chunk sizes.
+
+Sun Jun 23 09:35:09 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (serve_file): Use NE_FMT_OFF_T and NE_FMT_SSIZE_T.
+
+Thu May 30 21:57:39 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (minisleep): Export function.
+
+Sun May 19 18:23:19 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c, tests.c: s/sock_/ne_sock_/, s/SOCK_/NE_SOCK_/ for
+       socket layer API change.
+
+       * child.h (SEND_STRING): New macro.
+
+Sun May 19 08:57:21 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (lookup_hostname): Conditionally use hstrerror().
+
+Mon Feb 25 20:54:56 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (t_context): Use ne_vsnprintf.
+
+Mon Feb 11 21:52:23 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (lookup_hostname): New function.
+       (server_child, do_listen): Pass around struct in_addr argument.
+       (spawn_server_addr): Renamed from spawn_server, take bind_local
+       flag to use localhost or "real" hostname to bind to.
+       (spawn_server): New function, use spawn_server
+
+Mon Feb 11 20:51:27 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (minisleep) [HAVE_USLEEP]: Use nice short usleep()
+       rather than boring long sleep().
+
+Sat Feb  2 14:15:25 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (spawn_server_repeat): Exit child process (with failure)
+       if the server callback fails.
+
+Fri Jan  4 22:06:17 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.h: Add SKIPREST result value.
+       
+       * tests.c (TEST_DEBUG): Add NE_DBG_LOCKS.  (main): Support
+       SKIPREST.
+
+Tue Jan  1 20:36:58 2002  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.h: Make test_suite symbol have external linkage.
+
+Sat Nov 10 22:28:55 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c, test.h: Export name of test suite.
+
+Sun Nov  4 13:56:42 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (discard_request): Support retrieving arbitrary header
+       values from request via want_request, got_request globals.
+
+Wed Oct 24 21:41:39 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.h (ONV): New macro.  (ON, ONN): Redefine in terms of ONV.
+
+Wed Oct 24 20:44:59 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main, t_warning, segv): Use colours when stdout is a
+       terminal device.
+
+Wed Oct 24 20:36:38 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (t_context, t_warning): Renamed from i_am, warning.
+       (t_context): Take printf varargs. 
+
+       * tests.h (ONN, ON): Update, simplify.
+
+       * child.c: Update.
+
+Tue Oct 23 22:15:17 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Vertically align results after warnings.
+
+Tue Oct  2 23:36:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (do_listen): Refactored from server_socket, only does
+       bind/listen.  (spawn_server): Moved awaken/accept calls here.
+       (spawn_server_repeat, dead_server): New functions.
+
+Sun Sep 30 10:14:35 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.h: Use a function/name structure for tests, add 'T' macro
+       for easily writing initializers.  Rename 'name' global variable to
+       'test_context' to avoid parameter name collisions.
+
+       * child.c (spawn_server): Update accordingly.
+
+       * tests.c (i_am): Update accordingly.  (main): Update; prettify
+       output using new 'name' from test structure. Cope better when all
+       tests in a suite are skipped.
+
+Sat Sep 29 11:04:40 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (serve_file): If 'chunks' is set in argument object,
+       then deliver the file as a series of chunks.
+
+Thu Sep 27 20:28:45 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (serve_file): New function.
+
+Thu Sep 27 20:28:41 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (discard_request): Reset clength.
+
 Mon Aug 27 00:31:09 2001  Joe Orton  <joe@manyfish.co.uk>
 
        * tests.c (test_num): Expose test counter.  (segv): Handle
index 5331548..5756882 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Framework for testing with a server process
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-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
 
 #include <sys/wait.h>
 #include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/stat.h>
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #include <stdlib.h>
 #endif
 
+#include <fcntl.h>
+#include <netdb.h>
 #include <signal.h>
 
 #include "ne_socket.h"
+#include "ne_utils.h"
+#include "ne_string.h"
 
 #include "tests.h"
 #include "child.h"
 
 static pid_t child = 0;
 
-static struct in_addr lh_addr;
+int clength;
+
+static struct in_addr lh_addr = {0}, hn_addr = {0};
+
+const char *want_header = NULL;
+got_header_fn got_header = NULL;
+char *local_hostname = NULL;
 
 /* If we have pipe(), then use a pipe between the parent and child to
  * know when the child is ready to accept incoming connections.
@@ -56,74 +70,118 @@ static struct in_addr lh_addr;
 
 int lookup_localhost(void)
 {
-    if (sock_name_lookup("localhost", &lh_addr))
-       return FAILHARD;
-    
+    /* this will break if a system is set up so that `localhost' does
+     * not resolve to 127.0.0.1, but... */
+    lh_addr.s_addr = inet_addr("127.0.0.1");
     return OK;
 }
 
-static nsocket *server_socket(int readyfd, int port)
+int lookup_hostname(void)
+{
+    char buf[BUFSIZ];
+    struct hostent *ent;
+
+    local_hostname = NULL;
+    ONV(gethostname(buf, BUFSIZ) < 0,
+       ("gethostname failed: %s", strerror(errno)));
+
+    ent = gethostbyname(buf);
+#ifdef HAVE_HSTRERROR
+    ONV(ent == NULL,
+       ("could not resolve `%s': %s", buf, hstrerror(h_errno)));
+#else
+    ONV(ent == NULL, ("could not resolve `%s'", buf));
+#endif
+
+    local_hostname = ne_strdup(ent->h_name);
+
+    return OK;
+}
+
+static int do_listen(struct in_addr addr, int port)
 {
     int ls = socket(AF_INET, SOCK_STREAM, 0);
-    struct sockaddr_in addr;
-    nsocket *s;
+    struct sockaddr_in saddr = {0};
     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;
+    saddr.sin_addr = addr;
+    saddr.sin_port = htons(port);
+    saddr.sin_family = AF_INET;
 
-    if (bind(ls, (struct sockaddr *)&addr, sizeof(addr))) {
+    if (bind(ls, (struct sockaddr *)&saddr, sizeof(saddr))) {
        printf("bind failed: %s\n", strerror(errno));
-       return NULL;
+       return -1;
     }
     if (listen(ls, 5)) {
        printf("listen failed: %s\n", strerror(errno));
-       return NULL;
+       return -1;
     }
 
-#ifdef USE_PIPE
-    /* wakey wakey. */
-    write(readyfd, "a", 1);
-#endif
-
-    s = sock_accept(ls);
-    close(ls);
-    
-    return s;
+    return ls;
 }
 
-static void minisleep(void)
+void minisleep(void)
 {
+#ifdef HAVE_USLEEP
+    usleep(500);
+#else
     sleep(1);
+#endif
+}
+
+/* close 'sock', performing lingering close to avoid premature RST. */
+static int close_socket(ne_socket *sock)
+{
+#ifdef HAVE_SHUTDOWN
+    char buf[20];
+    int fd = ne_sock_fd(sock);
+    
+    shutdown(fd, 0);
+    while (ne_sock_read(sock, buf, sizeof buf) > 0);
+#endif
+    return ne_sock_close(sock);
 }
 
 /* This runs as the child process. */
-static int server_child(int readyfd, int port,
+static int server_child(int readyfd, struct in_addr addr, int port,
                        server_fn callback, void *userdata)
 {
-    nsocket *s;
-    int ret;
+    ne_socket *s = ne_sock_create();
+    int ret, listener;
 
     in_child();
 
-    s = server_socket(readyfd, port);
-
-    if (s == NULL)
+    listener = do_listen(addr, port);
+    if (listener < 0)
        return FAIL;
 
+#ifdef USE_PIPE
+    /* Tell the parent we're ready for the request. */
+    write(readyfd, "a", 1);
+#endif
+
+    ONN("accept failed", ne_sock_accept(s, listener));
+
     ret = callback(s, userdata);
 
-    sock_close(s);
+    close_socket(s);
 
     return ret;
 }
 
 int spawn_server(int port, server_fn fn, void *ud)
 {
+    return spawn_server_addr(1, port, fn, ud);
+}
+
+int spawn_server_addr(int bind_local, int port, server_fn fn, void *ud)
+{
     int fds[2];
+    struct in_addr addr;
+
+    addr = bind_local?lh_addr:hn_addr;
 
 #ifdef USE_PIPE
     if (pipe(fds)) {
@@ -143,7 +201,7 @@ int spawn_server(int port, server_fn fn, void *ud)
        /* this is the child. */
        int ret;
 
-       ret = server_child(fds[1], port, fn, ud);
+       ret = server_child(fds[1], addr, port, fn, ud);
 
 #ifdef USE_PIPE
        close(fds[0]);
@@ -152,7 +210,7 @@ int spawn_server(int port, server_fn fn, void *ud)
 
        /* print the error out otherwise it gets lost. */
        if (ret) {
-           printf("server child failed: %s\n", name);
+           printf("server child failed: %s\n", test_context);
        }
        
        /* and quit the child. */
@@ -174,18 +232,104 @@ int spawn_server(int port, server_fn fn, void *ud)
     }
 }
 
+int spawn_server_repeat(int port, server_fn fn, void *userdata, int n)
+{
+    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();
+    
+    if (child == 0) {
+       /* this is the child. */
+       int listener, count = 0;
+       
+       in_child();
+       
+       listener = do_listen(lh_addr, port);
+
+#ifdef USE_PIPE
+       write(fds[1], "Z", 1);
+#endif
+
+       close(fds[1]);
+       close(fds[0]);
+
+       /* Loop serving requests. */
+       while (++count < n) {
+           ne_socket *sock = ne_sock_create();
+           int ret;
+
+           NE_DEBUG(NE_DBG_HTTP, "child awaiting connection #%d.\n", count);
+           ONN("accept failed", ne_sock_accept(sock, listener));
+           ret = fn(sock, userdata);
+           close_socket(sock);
+           NE_DEBUG(NE_DBG_HTTP, "child served request, %d.\n", ret);
+           if (ret) {
+               printf("server child failed: %s\n", test_context);
+               exit(-1);
+           }
+           /* don't send back notification to parent more than
+            * once. */
+       }
+       
+       NE_DEBUG(NE_DBG_HTTP, "child aborted.\n");
+       close(listener);
+
+       exit(-1);
+    
+    } else {
+       char ch;
+       /* this is the parent. wait for the child to get ready */
+#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 dead_server(void)
+{
+    int status;
+
+    if (waitpid(child, &status, WNOHANG)) {
+       /* child quit already! */
+       return FAIL;
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "child has not quit.\n");
+
+    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);
+    ONN("error from server process", WEXITSTATUS(status));
+
+    return OK;
 }
 
 int reap_server(void)
@@ -196,7 +340,128 @@ int reap_server(void)
        (void) kill(child, SIGTERM);
        minisleep();
        (void) wait(&status);
+       child = 0;
     }
 
     return OK;
 }
+
+ssize_t server_send(ne_socket *sock, const char *str, size_t len)
+{
+    NE_DEBUG(NE_DBG_HTTP, "Sending: %.*s\n", (int)len, str);
+    return ne_sock_fullwrite(sock, str, len);
+}
+
+int discard_request(ne_socket *sock)
+{
+    char buffer[1024];
+    size_t offset = want_header?strlen(want_header):0;
+
+    clength = 0;
+
+    NE_DEBUG(NE_DBG_HTTP, "Discarding request...\n");
+    do {
+       ONV(ne_sock_readline(sock, buffer, 1024) < 0,
+           ("error reading line: %s", ne_sock_error(sock)));
+       NE_DEBUG(NE_DBG_HTTP, "[req] %s", buffer);
+       if (strncasecmp(buffer, "content-length:", 15) == 0) {
+           clength = atoi(buffer + 16);
+       }
+       if (got_header != NULL && want_header != NULL && 
+           strncasecmp(buffer, want_header, offset) == 0 &&
+           buffer[offset] == ':') {
+           char *value = buffer + offset + 1;
+           if (*value == ' ') value++;
+           got_header(ne_shave(value, "\r\n"));
+       }
+    } while (strcmp(buffer, "\r\n") != 0);
+    
+    return OK;
+}
+
+int discard_body(ne_socket *sock)
+{
+    while (clength > 0) {
+       char buf[BUFSIZ];
+       size_t bytes = clength;
+       ssize_t ret;
+       if (bytes > sizeof(buf)) bytes = sizeof(buf);
+       NE_DEBUG(NE_DBG_HTTP, "Discarding %" NE_FMT_SIZE_T " bytes.\n",
+                bytes);
+       ret = ne_sock_read(sock, buf, bytes);
+       ONV(ret < 0, ("socket read failed (%" NE_FMT_SSIZE_T "): %s", 
+                     ret, ne_sock_error(sock)));
+       clength -= ret;
+       NE_DEBUG(NE_DBG_HTTP, "Got %" NE_FMT_SSIZE_T " bytes.\n", ret);
+    }
+    NE_DEBUG(NE_DBG_HTTP, "Discard successful.\n");
+    return OK;
+}
+
+int serve_file(ne_socket *sock, void *ud)
+{
+    char buffer[BUFSIZ];
+    struct stat st;
+    struct serve_file_args *args = ud;
+    ssize_t ret;
+    int fd;
+
+    CALL(discard_request(sock));
+
+    ne_sock_fullread(sock, buffer, clength);
+
+    fd = open(args->fname, O_RDONLY);
+    if (fd < 0) {
+       SEND_STRING(sock, 
+                   "HTTP/1.0 404 File Not Found\r\n"
+                   "Content-Length: 0\r\n\r\n");
+       return 0;
+    }
+
+    ONN("fstat fd", fstat(fd, &st));
+
+    SEND_STRING(sock, "HTTP/1.0 200 OK\r\n");
+    if (args->chunks) {
+       sprintf(buffer, "Transfer-Encoding: chunked\r\n");
+    } else {
+       sprintf(buffer, "Content-Length: %" NE_FMT_OFF_T "\r\n", 
+               st.st_size);
+    } 
+    
+    if (args->headers) {
+       strcat(buffer, args->headers);
+    }
+
+    strcat(buffer, "\r\n");
+
+    SEND_STRING(sock, buffer);
+
+    NE_DEBUG(NE_DBG_HTTP, "Serving %s (%" NE_FMT_OFF_T " bytes).\n",
+            args->fname, st.st_size);
+
+    if (args->chunks) {
+       char buf[1024];
+       
+       while ((ret = read(fd, &buf, args->chunks)) > 0) {
+           /* this is a small integer, cast it explicitly to avoid
+            * warnings with printing an ssize_t. */
+           sprintf(buffer, "%x\r\n", (unsigned int)ret);
+           SEND_STRING(sock, buffer);
+           ONN("writing body", ne_sock_fullwrite(sock, buf, ret));
+           SEND_STRING(sock, "\r\n");
+       }
+       
+       SEND_STRING(sock, "0\r\n\r\n");
+       
+    } else {
+       while ((ret = read(fd, buffer, BUFSIZ)) > 0) {
+           ONN("writing body", ne_sock_fullwrite(sock, buffer, ret));
+       }
+    }
+
+    ONN("error reading from file", ret < 0);
+
+    (void) close(fd);
+
+    return OK;
+}
index b24e6d3..83f0c52 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Stupidly simple test framework
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-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
@@ -20,6 +20,8 @@
 
 #include "config.h"
 
+#include <sys/types.h>
+
 #include <sys/signal.h>
 
 #include <stdio.h>
 #include "ne_socket.h"
 
 #include "tests.h"
+#include "child.h"
+
+char test_context[BUFSIZ];
+int have_context = 0;
+
+static FILE *child_debug, *debug;
+
+char **test_argv;
+int test_argc;
+
+const char *test_suite;
+int test_num;
+
+/* statistics for all tests so far */
+static int passes = 0, fails = 0, skipped = 0, warnings = 0;
+
+/* per-test globals: */
+static int warned, aborted = 0;
+static const char *test_name; /* current test name */
 
-const char *name = NULL;
-static FILE *child_debug;
+static int use_colour = 0;
 
-static int passes = 0, fails = 0, aborted = 0;
-static const char *suite;
+/* resource for ANSI escape codes:
+ * http://www.isthe.com/chongo/tech/comp/ansi_escapes.html */
+#define COL(x) do { if (use_colour) printf("\033[" x "m"); } while (0)
 
-void i_am(const char *testname)
+#define NOCOL COL("00")
+
+void t_context(const char *context, ...)
 {
-    name = testname;
+    va_list ap;
+    va_start(ap, context);
+    ne_vsnprintf(test_context, BUFSIZ, context, ap);
+    va_end(ap);
+    have_context = 1;
 }
 
-#define TEST_DEBUG (NE_DBG_HTTP | NE_DBG_SOCKET)
+void t_warning(const char *str, ...)
+{
+    va_list ap;
+    COL("43;01"); printf("WARNING:"); NOCOL;
+    putchar(' ');
+    va_start(ap, str);
+    vprintf(str, ap);
+    va_end(ap);
+    warnings++;
+    warned++;
+    putchar('\n');
+}    
+
+#define TEST_DEBUG \
+(NE_DBG_HTTP | NE_DBG_SOCKET | NE_DBG_HTTPBODY | NE_DBG_HTTPAUTH | \
+ NE_DBG_LOCKS | NE_DBG_XMLPARSE | NE_DBG_XML | NE_DBG_SSL)
 
 #define W(m) write(0, m, strlen(m))
 
@@ -66,76 +108,203 @@ static void child_segv(int signo)
     exit(-1);
 }
 
+static void segv(int signo)
+{
+    signal(SIGSEGV, SIG_DFL);
+    if (use_colour) {
+       W("\033[41;37;01m");
+    }
+    W("FAILED - segmentation fault.");
+    if (use_colour) {
+       W("\033[00m");
+    }
+    W("\n");
+    fflush(debug);
+    reap_server();
+    kill(getpid(), SIGSEGV);
+}
+
 void in_child(void)
 {
     ne_debug_init(child_debug, TEST_DEBUG);    
-    NE_DEBUG(TEST_DEBUG, "**** Child forked ****\n");
+    NE_DEBUG(TEST_DEBUG, "**** Child forked for test %s ****\n", test_name);
     signal(SIGSEGV, child_segv);
 }
 
 int main(int argc, char *argv[])
 {
     int n;
-    FILE *debug;
+    static const char dots[] = "......................";
     
     /* get basename(argv[0]) */
-    suite = strrchr(argv[0], '/');
-    if (suite == NULL) {
-       suite = argv[0];
+    test_suite = strrchr(argv[0], '/');
+    if (test_suite == NULL) {
+       test_suite = argv[0];
     } else {
-       suite++;
+       test_suite++;
     }
-    
-    sock_init();
+
+#if defined(HAVE_ISATTY) && defined(STDOUT_FILENO)
+    if (isatty(STDOUT_FILENO)) {
+       use_colour = 1;
+    }
+#endif
+
+    test_argc = argc;
+    test_argv = argv;
 
     debug = fopen("debug.log", "a");
     if (debug == NULL) {
-       fprintf(stderr, "%s: Could not open debug.log: %s\n", suite,
+       fprintf(stderr, "%s: Could not open debug.log: %s\n", test_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,
+       fprintf(stderr, "%s: Could not open child.log: %s\n", test_suite,
                strerror(errno));
        fclose(debug);
        return -1;
     }
 
+    if (tests[0].fn == NULL) {
+       printf("-> no tests found in `%s'\n", test_suite);
+       return -1;
+    }
+
+    /* install special SEGV handler. */
+    signal(SIGSEGV, segv);
+
+    /* test the "no-debugging" mode of ne_debug. */
+    ne_debug_init(NULL, 0);
+    NE_DEBUG(TEST_DEBUG, "This message should go to /dev/null");
+
+    /* enable debugging for real. */
     ne_debug_init(debug, TEST_DEBUG);
+    NE_DEBUG(TEST_DEBUG | NE_DBG_FLUSH, "Version string: %s\n", 
+             ne_version_string());
+    
+    /* another silly test. */
+    NE_DEBUG(0, "This message should also go to /dev/null");
 
-    if (tests[0] == NULL) {
-       printf("-> no tests found in %s\n", suite);
-       return -1;
+    if (ne_sock_init()) {
+       COL("43;01"); printf("WARNING:"); NOCOL;
+       printf(" Socket library initalization failed.\n");
     }
 
-    printf("-> running %s:\n", suite);
+    printf("-> running `%s':\n", test_suite);
     
-    for (n = 0; !aborted && tests[n] != NULL; n++) {
-       printf("%d: ", n);
-       name = NULL;
+    for (n = 0; !aborted && tests[n].fn != NULL; n++) {
+       int result;
+#ifdef NEON_MEMLEAK
+        size_t allocated = ne_alloc_used;
+#endif
+
+       test_name = tests[n].name;
+       printf("%2d. %s%.*s ", n, test_name, 
+              (int) (strlen(dots) - strlen(test_name)), dots);
+       have_context = 0;
+       test_num = n;
+       warned = 0;
        fflush(stdout);
-       NE_DEBUG(TEST_DEBUG, "******* Running test %d ********\n", n);
-       switch (tests[n]()) {
+       NE_DEBUG(TEST_DEBUG, "******* Running test %d: %s ********\n", 
+                n, test_name);
+
+       /* run the test. */
+       result = tests[n].fn();
+
+#ifdef NEON_MEMLEAK
+        /* issue warnings for memory leaks, if requested */
+        if ((tests[n].flags & T_CHECK_LEAKS) && result == OK &&
+            ne_alloc_used > allocated) {
+            t_context("memory leak of %" NE_FMT_SIZE_T " bytes",
+                      ne_alloc_used - allocated);
+            fprintf(debug, "Blocks leaked: ");
+            ne_alloc_dump(debug);
+            result = FAIL;
+        } else if (tests[n].flags & T_EXPECT_LEAKS && result == OK &&
+                   ne_alloc_used == allocated) {
+            t_context("expected memory leak not detected");
+            result = FAIL;
+        }
+#endif
+
+        if (tests[n].flags & T_EXPECT_FAIL) {
+            if (result == OK) {
+                t_context("test passed but expected failure");
+                result = FAIL;
+            } else if (result == FAIL)
+                result = OK;
+        }
+
+       /* align the result column if we've had warnings. */
+       if (warned) {
+           printf("    %s ", dots);
+       }
+
+       switch (result) {
        case OK:
-           printf("pass.\n");
+           COL("32"); printf("pass"); NOCOL;
+           if (warned) {
+               printf(" (with %d warning%s)", warned, (warned > 1)?"s":"");
+           }
+           putchar('\n');
            passes++;
            break;
        case FAILHARD:
            aborted = 1;
            /* fall-through */
        case FAIL:
-           if (name != NULL) {
-               printf("%s - ", name);
+           COL("41;37;01"); printf("FAIL"); NOCOL;
+           if (have_context) {
+               printf(" (%s)", test_context);
            }
-           printf("FAIL.\n");
+           putchar('\n');
            fails++;
            break;
+       case SKIPREST:
+           aborted = 1;
+           /* fall-through */
+       case SKIP:
+           COL("44"); printf("SKIPPED"); NOCOL;
+           if (have_context) {
+               printf(" (%s)", test_context);
+           }
+           putchar('\n');
+           skipped++;
+           break;
+       default:
+           COL("41;37;01"); printf("OOPS"); NOCOL;
+           printf(" unexpected test result `%d'\n", result);
+           break;
        }
+
+       reap_server();
     }
 
-    printf("-> summary for %s: of %d tests: %d passed, %d failed. %.1f%%\n", 
-          suite, n, passes, fails, 100*(float)passes/n);
+    /* discount skipped tests */
+    if (skipped) {
+       printf("-> %d %s.\n", skipped,
+              skipped==1?"test was skipped":"tests were skipped");
+       n -= skipped;
+       if (passes + fails != n) {
+           printf("-> ARGH! Number of test results does not match "
+                  "number of tests.\n"
+                  "-> ARGH! Test Results are INRELIABLE.\n");
+       }
+    }
+    /* print the summary. */
+    if (skipped && n == 0) {
+       printf("<- all tests skipped for `%s'.\n", test_suite);
+    } else {
+       printf("<- summary for `%s': "
+              "of %d tests run: %d passed, %d failed. %.1f%%\n",
+              test_suite, n, passes, fails, 100*(float)passes/n);
+       if (warnings) {
+           printf("-> %d warning%s issued.\n", warnings, 
+                  warnings==1?" was":"s were");
+       }
+    }
 
     if (fclose(debug)) {
        fprintf(stderr, "Error closing debug.log: %s\n", strerror(errno));
@@ -146,6 +315,8 @@ int main(int argc, char *argv[])
        fprintf(stderr, "Error closing child.log: %s\n", strerror(errno));
        fails = 1;
     }
+
+    ne_sock_exit();
     
     return fails;
 }
index a299148..2e4e31a 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Stupidly simple test framework
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2002, 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
 
 #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. */
+#define FAILHARD 2 /* fail and skip all succeeding tests in this suite. */
+#define SKIP 3 /* test was skipped because precondition was not met */
+#define SKIPREST 4 /* skipped, and skip all succeeding tests in suite */
+
+/* A test function. Must return any of OK, FAIL, FAILHARD, SKIP, or
+ * SKIPREST.  May call t_warning() any number of times.  If not
+ * returning OK, optionally call t_context to provide an error
+ * message. */
+typedef int (*test_func)(void);
 
-static char on_err_buf[BUFSIZ];
+typedef struct {
+    test_func fn; /* the function to test. */
+    const char *name; /* the name of the test. */
+    int flags;
+} ne_test;
+
+/* possible values for flags: */
+#define T_CHECK_LEAKS (1) /* check for memory leaks */
+#define T_EXPECT_FAIL (2) /* expect failure */
+#define T_EXPECT_LEAKS (4) /* expect memory leak failures */
+
+/* array of tests to run: must be defined by each test suite. */
+extern ne_test tests[];
+
+/* define a test function which has the same name as the function,
+ * and does check for memory leaks. */
+#define T(fn) { fn, #fn, T_CHECK_LEAKS }
+/* define a test function which is expected to return FAIL. */
+#define T_XFAIL(fn) { fn, #fn, T_EXPECT_FAIL | T_CHECK_LEAKS }
+/* define a test function which isn't checked for memory leaks. */
+#define T_LEAKY(fn) { fn, #fn, 0 }
+/* define a test function which is expected to fail memory leak checks */
+#define T_XLEAKY(fn) { fn, #fn, T_EXPECT_LEAKS }
+
+/* current test number */
+extern int test_num;
+
+/* name of test suite */
+extern const char *test_suite;
+
+/* Provide result context message. */
+void t_context(const char *ctx, ...)
+#ifdef __GNUC__
+                __attribute__ ((format (printf, 1, 2)))
+#endif /* __GNUC__ */
+    ;
+
+extern char test_context[];
+
+/* the command-line arguments passed in to the test suite: */
+extern char **test_argv;
+extern int test_argc;
 
 /* 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)
+/* issue a warning. */
+void t_warning(const char *str, ...)
+#ifdef __GNUC__
+                __attribute__ ((format (printf, 1, 2)))
+#endif /* __GNUC__ */
+;
+
+/* Macros for easily writing is-not-zero comparison tests; the ON*
+ * macros fail the function if a comparison is not zero.
+ *
+ * ONV(x,vs) takes a comparison X, and a printf varargs list for
+ * the failure message.
+ *  e.g.   ONV(strcmp(bar, "foo"), ("bar was %s not 'foo'", bar))
+ *
+ * ON(x) takes a comparison X, and uses the line number for the failure
+ * message.   e.g.  ONV(strcmp(bar, "foo"))
+ *
+ * ONN(n, x) takes a comparison X, and a flat string failure message.
+ *  e.g.  ONN("foo was wrong", strcmp(bar, "foo")) */
+
+#define ONV(x,vs) do { if ((x)) { t_context vs; return FAIL; } } while (0)
+#define ON(x) ONV((x), ("line %d", __LINE__ ))
+#define ONN(n,x) ONV((x), (n))
+
+/* ONCMP(exp, act, name): 'exp' is the expected string, 'act' is the
+ * actual string for some field 'name'.  Succeeds if strcmp(exp,act)
+ * == 0 or both are NULL. */
+#define ONCMP(exp, act, ctx, name) do { \
+ONV(exp && !act, ("%s: " name " was NULL, expected non-NULL", ctx)); \
+ONV(!exp && act, ("%s: " name " was non-NULL, expected NULL", ctx)); \
+ONV(exp && strcmp(exp, act), ("%s: " name " was %s not %s", ctx, exp, act)); \
+} while (0)
+
+/* return immediately with result of test 'x' if it fails. */
+#define CALL(x) do { int t_ret = (x); if (t_ret != OK) return t_ret; } while (0)
+
+/* PRECOND: skip current test if condition 'x' is not true. */
+#define PRECOND(x) do { if (!(x)) { return SKIP; } } while (0)
 
 #endif /* TESTS_H */
index 1e33d3f..cb90c23 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    tests for compressed response handling.
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2004, 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
 
 #include <fcntl.h>
 
+#include "ne_compress.h"
+#include "ne_auth.h"
+
 #include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static enum { f_partial = 0, f_mismatch, f_complete } failed;
 
-#ifndef NEON_ZLIB
+static char *newsfn = "../NEWS";
 
-static int skip_compress(void)
+static int init(void)
 {
-    return SKIP;
+    if (test_argc > 1) {
+       newsfn = ne_concat(test_argv[1], "/../NEWS", NULL);
+    }
+    return lookup_localhost();
 }
 
-test_func tests[] = {
-    skip_compress,
-    NULL
-};
-
-#else
-
-#include "ne_compress.h"
-
-#include "child.h"
-#include "utils.h"
-
-static int failed = 0;
+#define EXTRA_DEBUG 0 /* disabled by default */
 
 static void reader(void *ud, const char *block, size_t len)
 {
-    const char **pnt = ud;
-    
-    if (memcmp(*pnt, block, len) != 0)
-       failed = 1;
+    struct string *b = ud;
+
+#if EXTRA_DEBUG
+    NE_DEBUG(NE_DBG_HTTP, "reader: got (%d): [[[%.*s]]]\n", (int)len,
+             (int)len, block);
+#endif
 
-    *pnt += len;
+    if (failed == f_mismatch) return;
+
+    if (failed == f_partial && len == 0) {
+        if (b->len != 0) {
+            NE_DEBUG(NE_DBG_HTTP, "reader: got length %d at EOF\n",
+                     (int)b->len);
+            failed = f_mismatch;
+        } else {
+            failed = f_complete;
+        }
+        return;
+    }
+
+    if (len > b->len || memcmp(b->data, block, len) != 0) {
+        NE_DEBUG(NE_DBG_HTTP, "reader: failed, got [[%.*s]] not [[%.*s]]\n",
+                 (int)len, block, (int)b->len, b->data);
+       failed = f_mismatch;
+    } else {
+       b->data += len;
+       b->len -= len;
+#if EXTRA_DEBUG
+        NE_DEBUG(NE_DBG_HTTP, "reader: OK, %d bytes remaining\n", 
+                 (int)b->len);
+#endif
+    }
 }
 
 static int file2buf(int fd, ne_buffer *buf)
@@ -64,31 +88,34 @@ static int file2buf(int fd, ne_buffer *buf)
     char buffer[BUFSIZ];
     ssize_t n;
     
-    while ((n = read(fd, buffer, fd)) > 0) {
+    while ((n = read(fd, buffer, BUFSIZ)) > 0) {
        ne_buffer_append(buf, buffer, n);
     }
     
     return 0;
 }
 
-static int fetch(const char *realfn, const char *gzipfn, int chunked)
+static int do_fetch(const char *realfn, const char *gzipfn,
+                   int chunked, int expect_fail)
 {
-    ne_session *sess = ne_session_create();
+    ne_session *sess;
     ne_request *req;
     int fd;
     ne_buffer *buf = ne_buffer_create();
-    const char *pnt;
     struct serve_file_args sfargs;
     ne_decompress *dc;
+    struct string body;
     
     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);
+
+    body.data = buf->data;
+    body.len = buf->used - 1;
     
+    failed = f_partial;
+
     if (gzipfn) {
        sfargs.fname = gzipfn;
        sfargs.headers = "Content-Encoding: gzip\r\n";
@@ -98,14 +125,18 @@ static int fetch(const char *realfn, const char *gzipfn, int chunked)
     }
     sfargs.chunks = chunked;
     
-    CALL(spawn_server(7777, serve_file, &sfargs));
+    CALL(make_session(&sess, serve_file, &sfargs));
     
     req = ne_request_create(sess, "GET", "/");
-    dc = ne_decompress_reader(req, ne_accept_2xx, reader, &pnt);
+    dc = ne_decompress_reader(req, ne_accept_2xx, reader, &body);
 
     ONREQ(ne_request_dispatch(req));
 
-    ONN("error during decompress", ne_decompress_destroy(dc));
+    ONN("file not served", ne_get_status(req)->code != 200);
+
+    ONN("decompress succeeded", expect_fail && !ne_decompress_destroy(dc));
+    ONV(!expect_fail && ne_decompress_destroy(dc),
+        ("decompress failed: %s", ne_get_error(sess)));
 
     ne_request_destroy(req);
     ne_session_destroy(sess);
@@ -113,71 +144,215 @@ static int fetch(const char *realfn, const char *gzipfn, int chunked)
     
     CALL(await_server());
 
-    ONN("inflated response compare", failed);
+    if (!expect_fail) {
+        ONN("inflated response truncated", failed == f_partial);
+        ONN("inflated response mismatch", failed == f_mismatch);
+    }
 
     return OK;
 }
 
+static int fetch(const char *realfn, const char *gzipfn, int chunked)
+{
+    return do_fetch(realfn, gzipfn, chunked, 0);
+}
+
 /* Test the no-compression case. */
-static int compress_0(void)
+static int not_compressed(void)
 {
-    return fetch("../NEWS", NULL, 0);
+    return fetch(newsfn, NULL, 0);
 }
 
-static int compress_1(void)
+static int simple(void)
 {
-    return fetch("../NEWS", "file1.gz", 0);
+    return fetch(newsfn, "file1.gz", 0);
 }
 
 /* file1.gz has an embedded filename. */
-static int compress_2(void)
+static int withname(void)
 {
-    return fetch("../NEWS", "file2.gz", 0);
+    return fetch(newsfn, "file2.gz", 0);
 }
 
 /* deliver various different sizes of chunks: tests the various
  * decoding cases. */
-static int compress_3(void)
+static int chunked_1b_wn(void)
 {
-    return fetch("../NEWS", "file2.gz", 1);
+    return fetch(newsfn, "file2.gz", 1);
 }
 
-static int compress_4(void)
+static int chunked_1b(void)
 {
-    return fetch("../NEWS", "file1.gz", 1);
+    return fetch(newsfn, "file1.gz", 1);
 }
 
-static int compress_5(void)
+static int chunked_12b(void)
 {
-    return fetch("../NEWS", "file2.gz", 12);
+    return fetch(newsfn, "file2.gz", 12);
 }
 
-static int compress_6(void)
+static int chunked_20b(void)
 {
-    return fetch("../NEWS", "file2.gz", 20);
+    return fetch(newsfn, "file2.gz", 20);
 }
 
-static int compress_7(void)
+static int chunked_10b(void)
 {
-    return fetch("../NEWS", "file1.gz", 10);
+    return fetch(newsfn, "file1.gz", 10);
 }
 
-static int compress_8(void)
+static int chunked_10b_wn(void)
 {
-    return fetch("../NEWS", "file2.gz", 10);
+    return fetch(newsfn, "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,
-};
+static int fail_trailing(void)
+{
+    return do_fetch(newsfn, "trailing.gz", 0, 1);
+}
+
+static int fail_truncate(void)
+{
+    return do_fetch(newsfn, "truncated.gz", 0, 1);
+}
 
-#endif /* NEON_ZLIB */
+static int fail_bad_csum(void)
+{
+    return do_fetch(newsfn, "badcsum.gz", 0, 1);
+}
+
+static int fail_corrupt1(void)
+{
+    return do_fetch(newsfn, "corrupt1.gz", 0, 1);
+}
+
+static int fail_corrupt2(void)
+{
+    return do_fetch(newsfn, "corrupt2.gz", 0, 1);
+}
+
+static int auth_cb(void *userdata, const char *realm, int tries, 
+                  char *un, char *pw)
+{
+    strcpy(un, "foo");
+    strcpy(pw, "bar");
+    return tries;
+}
+
+static int retry_compress_helper(ne_accept_response acceptor,
+                                 struct string *resp, struct string *expect)
+{
+    ne_session *sess;
+    ne_request *req;
+    ne_decompress *dc;
+    
+    CALL(make_session(&sess, serve_sstring, resp));
+
+    ne_set_server_auth(sess, auth_cb, NULL);
+
+    req = ne_request_create(sess, "GET", "/");
+    dc = ne_decompress_reader(req, acceptor, reader, expect);
+    failed = f_partial;
+
+    ONREQ(ne_request_dispatch(req));
+    
+    ONV(ne_decompress_destroy(dc),
+        ("decompress failed: %s", ne_get_error(sess)));
+
+    ONN("got bad response body", failed != f_complete);
+
+    CALL(await_server());
+
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+static char retry_gz_resp[] = 
+"HTTP/1.1 401 Get Away\r\n"
+"Content-Encoding: gzip\r\n"
+"WWW-Authenticate: Basic realm=WallyWorld\r\n"
+"Content-Length: 5\r\n"
+"\r\n"
+"abcde"
+"HTTP/1.1 200 OK\r\n"
+"Server: foo\r\n"
+"Content-Length: 5\r\n"
+"Connection: close\r\n"
+"\r\n"
+"hello";
+
+/* Test where the response to the retried request does *not* have
+ * a content-encoding, whereas the original 401 response did. */
+static int retry_notcompress(void)
+{
+    struct string response = { retry_gz_resp, strlen(retry_gz_resp) };
+    struct string expect = { "hello", 5 };
+    return retry_compress_helper(ne_accept_2xx, &response, &expect);
+}
+
+static unsigned char retry_gz_resp2[] = 
+"HTTP/1.1 401 Get Away\r\n"
+"Content-Encoding: gzip\r\n"
+"WWW-Authenticate: Basic realm=WallyWorld\r\n"
+"Content-Length: 25\r\n"
+"\r\n"
+"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xcb\x48\xcd\xc9\xc9\x07"
+"\x00\x86\xa6\x10\x36\x05\x00\x00\x00"
+"HTTP/1.1 200 OK\r\n"
+"Server: foo\r\n"
+"Content-Encoding: gzip\r\n"
+"Content-Length: 25\r\n"
+"Connection: close\r\n"
+"\r\n"
+"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\x2b\xcf\x2f\xca\x49\x01"
+"\x00\x43\x11\x77\x3a\x05\x00\x00\x00";
+
+static int retry_accept(void *ud, ne_request *req, const ne_status *st)
+{
+    struct string *expect = ud;
+
+    NE_DEBUG(NE_DBG_HTTP, "retry_accept callback for %d response\n",
+             st->code);
+
+    if (expect->len == 4 && strcmp(expect->data, "fish") == 0) {
+        /* first time through */
+        expect->data = "hello";
+    } else {
+        expect->data = "world";
+    }
+
+    expect->len = 5;
+    return 1;
+}
+
+/* Test where the response to the retried request *does* have a
+ * content-encoding, as did the original 401 response. */
+static int retry_compress(void)
+{
+    struct string resp = { retry_gz_resp2, sizeof(retry_gz_resp2) - 1 };
+    struct string expect = { "fish", 4 };
+    return retry_compress_helper(retry_accept, &resp, &expect);
+}
+
+ne_test tests[] = {
+    T_LEAKY(init),
+    T(not_compressed),
+    T(simple),
+    T(withname),
+    T(fail_trailing),
+    T(fail_bad_csum),
+    T(fail_truncate),
+    T(fail_corrupt1),
+    T(fail_corrupt2),
+    T(chunked_1b), 
+    T(chunked_1b_wn),
+    T(chunked_12b), 
+    T(chunked_20b),
+    T(chunked_10b),
+    T(chunked_10b_wn),
+    T(retry_notcompress),
+    T(retry_compress),
+    T(NULL)
+};
index a0ce3ed..abd1f36 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Test for cookies interface (ne_cookies.h)
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
@@ -75,9 +75,13 @@ static int parsing(void)
        const char *hdr, *name, *value;
     } cookies[] = {
        { "Set-Cookie: alpha=bar", "alpha", "bar" },
+#if 0
        { "Set-Cookie2: alpha=bar", "alpha", "bar" },
+#endif
        { "Set-Cookie: beta = bar", "beta", "bar" },
        { "Set-Cookie: delta = bar; norman=fish", "delta", "bar" },
+        /* parsing bug in <0.24.1 */
+        { "Set-Cookie: alpha=beta; path", "alpha", "beta" },
        { NULL, NULL, NULL }
     };
     int n;
index 94e75b5..0adf9e8 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    lock tests
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
 #include "ne_request.h"
 #include "ne_locks.h"
 #include "ne_socket.h"
+#include "ne_basic.h"
 
 #include "tests.h"
 #include "child.h"
 #include "utils.h"
 
+/* returns an activelock XML element. */
+static char *activelock(enum ne_lock_scope scope,
+                       int depth,
+                       const char *owner,
+                       long timeout,
+                       const char *token_href)
+{
+    static char buf[BUFSIZ];
+    
+    ne_snprintf(buf, BUFSIZ,
+               "<D:activelock>\n"
+               "<D:locktype><D:write/></D:locktype>\n"
+               "<D:lockscope><D:%s/></D:lockscope>\n"
+               "<D:depth>%d</D:depth>\n"
+               "<D:owner>%s</D:owner>\n"
+               "<D:timeout>Second-%ld</D:timeout>\n"
+               "<D:locktoken><D:href>%s</D:href></D:locktoken>\n"
+               "</D:activelock>",
+               scope==ne_lockscope_exclusive?"exclusive":"shared",
+               depth, owner, timeout, token_href);
+
+    return buf;
+}      
+
 /* return body of LOCK response for given lock. */
 static char *lock_response(enum ne_lock_scope scope,
-                          const char *depth,
+                          int depth,
                           const char *owner,
-                          const char *timeout,
+                          long 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);
+           "<D:prop xmlns:D=\"DAV:\">"
+           "<D:lockdiscovery>%s</D:lockdiscovery></D:prop>\n",
+           activelock(scope, depth, owner, timeout, token_href));
     return buf;
-}          
+}
+
+/* return body of LOCK response where response gives multiple
+ * activelocks (i.e. shared locks). */
+static char *multi_lock_response(struct ne_lock **locks)
+{
+    ne_buffer *buf = ne_buffer_create();
+    int n;
+
+    ne_buffer_zappend(buf,
+                     "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                     "<D:prop xmlns:D=\"DAV:\">"
+                     "<D:lockdiscovery>");
+    
+    for (n = 0; locks[n] != NULL; n++) {
+       char *lk = activelock(locks[n]->scope, locks[n]->depth,
+                             locks[n]->owner, locks[n]->timeout,
+                             locks[n]->token);
+       ne_buffer_zappend(buf, lk);
+    }
+
+    ne_buffer_zappend(buf, "</D:lockdiscovery></D:prop>");
+    return ne_buffer_finish(buf);
+}
+
+static char *discover_response(const char *href, const struct ne_lock *lk)
+{
+    static char buf[BUFSIZ];
+    ne_snprintf(buf, BUFSIZ,
+               "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+               "<D:multistatus xmlns:D='DAV:'>\n"
+               "<D:response><D:href>%s</D:href><D:propstat>\n"
+               "<D:prop><D:lockdiscovery>%s</D:lockdiscovery></D:prop>\n"
+               "<D:status>HTTP/1.1 200 OK</D:status></D:propstat>\n"
+               "</D:response></D:multistatus>\n",
+               href, activelock(lk->scope, lk->depth, lk->owner,
+                                7200, lk->token));
+    return buf;
+}
+
+static struct ne_lock *make_lock(const char *path, const char *token,
+                                enum ne_lock_scope scope, int depth)
+{
+    struct ne_lock *lock = ne_calloc(sizeof *lock);
+
+    lock->token = ne_strdup(token);
+    lock->scope = scope;
+    lock->depth = depth;
+    lock->uri.host = ne_strdup("localhost");
+    lock->uri.scheme = ne_strdup("http");
+    lock->uri.path = ne_strdup(path);
+    lock->uri.port = 7777;
+
+    return lock;
+}
+
+/* Tests for lock store handling. */
+static int store_single(void)
+{
+    ne_lock_store *store = ne_lockstore_create();
+    struct ne_lock *lk = make_lock("/foo", "blah", ne_lockscope_exclusive, 0);
+    struct ne_lock *lk2;
+
+    ONN("create failed", store == NULL);
+
+    ONN("new lock store not empty", ne_lockstore_first(store) != NULL);
+
+    ne_lockstore_add(store, lk);
+
+    ONN("lock not found in store", ne_lockstore_first(store) != lk);
+
+    ONN(">1 locks in store?", ne_lockstore_next(store) != NULL);
+
+    lk2 = ne_lockstore_findbyuri(store, &lk->uri);
+
+    ONN("lock not found by URI", lk2 == NULL);
+    ONN("other lock found by URI", lk2 != lk);
+    
+    ne_lockstore_remove(store, lk);
+
+    ONN("store not empty after removing lock",
+       ne_lockstore_first(store) != NULL);
+
+    ONN("lock still found after removing lock",
+       ne_lockstore_findbyuri(store, &lk->uri) != NULL);
+
+    ne_lockstore_destroy(store);
+    ne_lock_destroy(lk);
+    
+    return OK;
+}
+
+static int store_several(void)
+{
+    ne_lock_store *store = ne_lockstore_create();
+    struct ne_lock *lk = make_lock("/foo", "blah", ne_lockscope_exclusive, 0);
+    struct ne_lock *lk2 = make_lock("/bar", "blee", ne_lockscope_exclusive, 0);
+    struct ne_lock *lf, *lf2;
+
+    ONN("create failed", store == NULL);
+
+    ne_lockstore_add(store, lk);
+    ne_lockstore_add(store, lk2);
+
+    lf = ne_lockstore_first(store);
+    ONN("lock store empty", lf == NULL);
+    lf2 = ne_lockstore_next(store);
+    ONN("lock store >2 locks", ne_lockstore_next(store) != NULL);
+
+    /* guarantee that _first, _next returned either of the
+     * combinations: (lf, lf2) or (lf2, lf) */
+    ONN("found wrong locks", ((lf != lk && lf != lk2) ||
+                             (lf2 != lk && lf2 != lk2) ||
+                             (lf == lf2)));
+
+    ONN("first find failed",
+       ne_lockstore_findbyuri(store, &lk->uri) != lk);
+    ONN("second find failed",
+       ne_lockstore_findbyuri(store, &lk2->uri) != lk2);
+    
+    ne_lockstore_remove(store, lk);
+    ne_lock_destroy(lk);
+
+    ONN("remove left stray lock?", ne_lockstore_first(store) != lk2);
+
+    ONN("remove left >1 lock?", ne_lockstore_next(store) != NULL);
+
+    ne_lockstore_remove(store, lk2);
+    ne_lock_destroy(lk2);
+
+    ONN("store not empty after removing all locks",
+       ne_lockstore_first(store) != NULL);
+
+    ne_lockstore_destroy(store);
+    
+    return OK;
+}
 
 /* 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};
+    ne_session *sess;
+    char *resp, *rbody = lock_response(ne_lockscope_exclusive, 0, "me",
+                                      6500, "opaquelocktoken:foo");
+    struct ne_lock *lock = ne_lock_create();
 
-    ON(ne_session_server(sess, "localhost", 7777));
+    resp = ne_concat("HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
+                    "Lock-Token: <opaquelocktoken:foo>" EOL
+                    "Connection: close\r\n\r\n", rbody, NULL);
 
-    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));
+    CALL(make_session(&sess, single_serve_string, resp));
+    ne_free(resp);
 
-    lock.uri = "/foo";
-    lock.depth = 0;
-    lock.scope = ne_lockscope_exclusive;
-    lock.type = ne_locktype_write;
-    lock.timeout = 5;
+    ne_fill_server_uri(sess, &lock->uri);
+    lock->uri.path = ne_strdup("/foo");
+    lock->timeout = 5;
 
-    ONREQ(ne_lock(sess, &lock));
+    ONREQ(ne_lock(sess, lock));
 
     ONN("lock timeout ignored in response",
-       lock.timeout != 6500);
+       lock->timeout != 6500);
 
     ne_session_destroy(sess);
+    ne_lock_destroy(lock);
+
+    CALL(await_server());
+
+    return OK;
+}
+
+static int verify_if;
+static const char *verify_if_expect;
+
+static void got_if_header(char *value)
+{
+    verify_if = !strcmp(verify_if_expect, value);
+    NE_DEBUG(NE_DBG_HTTP, "Verified If header, %d: got [%s] expected [%s]\n", 
+            verify_if, value, verify_if_expect);
+}
+
+/* Server callback which checks that an If: header is recevied. */
+static int serve_verify_if(ne_socket *sock, void *userdata)
+{
+    /* tell us about If headers in the request. */
+    want_header = "If";
+    got_header = got_if_header;
+    verify_if_expect = userdata;
+
+    verify_if = 0;
+
+    CALL(discard_request(sock));
+
+    if (verify_if) {
+       ON(SEND_STRING(sock, "HTTP/1.1 200 OK" EOL));
+    } else {
+       ON(SEND_STRING(sock, "HTTP/1.1 403 Wrong If Header" EOL));
+    }
+    
+    ON(SEND_STRING(sock, "Connection: close" EOL EOL));
+    
+    return OK;
+}
+
+/* Make a request which will require a lock. */
+static int do_request(ne_session *sess, const char *path, int depth,
+                     int modparent)
+{
+    ne_request *req = ne_request_create(sess, "RANDOM", path);
+
+    if (depth > 0) {
+       ne_add_depth_header(req, depth);
+    }
+
+    if (depth != -1)
+       ne_lock_using_resource(req, path, depth);
+    if (modparent)
+       ne_lock_using_parent(req, path);
+
+    ONREQ(ne_request_dispatch(req));
+
+    ONV(ne_get_status(req)->code != 200,
+       ("request failed: %s", ne_get_error(sess)));
     
+    ne_request_destroy(req);
+
+    return OK;
+}
+
+/* Tests If: header submission, for a lock of depth 'lockdepth' at
+ * 'lockpath', with a request to 'reqpath' which Depth header of
+ * 'reqdepth'.  If modparent is non-zero; the request is flagged to
+ * modify the parent resource too. */
+static int submit_test(const char *lockpath, int lockdepth,
+                      const char *reqpath, int reqdepth,
+                      int modparent)
+{
+    ne_lock_store *store = ne_lockstore_create();
+    ne_session *sess;
+    struct ne_lock *lk = ne_lock_create();
+    char *expect_if;
+    int ret;
+    
+    expect_if = ne_concat("<http://localhost:7777", lockpath, 
+                         "> (<somelocktoken>)", NULL);
+    CALL(make_session(&sess, serve_verify_if, expect_if));
+    ne_free(expect_if);
+
+    ne_fill_server_uri(sess, &lk->uri);
+    lk->uri.path = ne_strdup(lockpath);
+    lk->token = ne_strdup("somelocktoken");
+    lk->depth = lockdepth;
+    
+    /* register the lock store, and add our lock for "/foo" to it. */
+    ne_lockstore_register(store, sess);
+    ne_lockstore_add(store, lk);
+
+    ret = do_request(sess, reqpath, reqdepth, modparent);
+    CALL(await_server());
+
+    ne_lockstore_destroy(store);
+    ne_session_destroy(sess);
+
+    return ret;
+}
+
+static int if_simple(void)
+{
+    return submit_test("/foo", 0, "/foo", 0, 0);
+}
+
+static int if_under_infinite(void)
+{
+    return submit_test("/foo", NE_DEPTH_INFINITE, "/foo/bar", 0, 0);
+}
+
+static int if_infinite_over(void)
+{
+    return submit_test("/foo/bar", 0, "/foo/", NE_DEPTH_INFINITE, 0);
+}
+
+static int if_child(void)
+{
+    return submit_test("/foo/", 0, "/foo/bar", 0, 1);
+}
+
+/* this is a special test, where the PARENT resource of "/foo/bar" is
+ * modified, but NOT "/foo/bar" itself.  An UNLOCK request on a
+ * lock-null resource can do this; see ne_unlock() for the comment.
+ * Regression test for neon <= 0.19.3, which didn't handle this
+ * correctly. */
+static int if_covered_child(void)
+{
+    return submit_test("/", NE_DEPTH_INFINITE, "/foo/bar", -1, 1);
+}
+
+static int serve_discovery(ne_socket *sock, void *userdata)
+{
+    char buf[BUFSIZ], *resp = userdata;
+
+    ON(discard_request(sock));
+    ONN("no PROPFIND body", clength == 0);
+    ON(ne_sock_read(sock, buf, clength) < 0);
+    ON(SEND_STRING(sock, "HTTP/1.0 207 OK" EOL
+                  "Connection: close" EOL EOL));
+    ON(SEND_STRING(sock, resp));
+    return OK;
+}
+
+struct result_args {
+    struct ne_lock *lock;
+    int result;
+};
+
+static int lock_compare(const char *ctx,
+                       const struct ne_lock *a, const struct ne_lock *b)
+{
+    ONV(!a->uri.host || !a->uri.scheme || !a->uri.path,
+       ("URI structure incomplete in %s", ctx));
+    ONV(ne_uri_cmp(&a->uri, &b->uri) != 0,
+       ("URI comparison failed for %s: %s not %s", ctx,
+        ne_uri_unparse(&a->uri), ne_uri_unparse(&b->uri)));
+    ONV(a->depth != b->depth, 
+       ("%s depth was %d not %d", ctx, a->depth, b->depth));
+    ONV(a->scope != b->scope,
+       ("%s scope was %d not %d", ctx, a->scope, b->scope));
+    ONV(a->type != b->type,
+       ("%s type was %d not %d", ctx, a->type, b->type));
+    return OK;
+}
+
+static void discover_result(void *userdata, const struct ne_lock *lk,
+                           const char *path, const ne_status *st)
+{
+    struct result_args *args = userdata;
+    args->result = lock_compare("discovered lock", lk, args->lock);
+}
+
+static int discover(void)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    char *response;
+    int ret;
+    struct result_args args;
+    
+    args.lock = ne_lock_create();
+
+    args.lock->owner = ne_strdup("someowner");
+    args.lock->token = ne_strdup("sometoken");
+
+    /* default */
+    args.result = FAIL;
+    t_context("results callback never invoked");
+
+    ne_fill_server_uri(sess, &args.lock->uri);
+    args.lock->uri.path = ne_strdup("/lockme");
+
+    response = discover_response("/lockme", args.lock);
+    CALL(spawn_server(7777, serve_discovery, response));
+
+    ret = ne_lock_discover(sess, "/lockme", discover_result, &args);
+    CALL(await_server());
+    ONREQ(ret);
+
+    ne_lock_destroy(args.lock);
+    ne_session_destroy(sess);
+
+    return args.result;
+}
+
+/* Check that the token for the response header */
+static int lock_shared(void)
+{
+    ne_session *sess;
+    char *resp, *rbody;
+    struct ne_lock *lock, *resplocks[3];
+
+#define FILLK(l, s) do { \
+(l)->token = strdup("opaquelocktoken:" s); \
+(l)->owner = strdup("owner " s); \
+(l)->uri.path = strdup("/" s); (l)->uri.host = strdup("localhost"); \
+(l)->uri.scheme = strdup("http"); (l)->uri.port = 7777; } while (0)
+    
+    resplocks[0] = ne_lock_create();
+    resplocks[1] = ne_lock_create();
+    resplocks[2] = NULL;
+    FILLK(resplocks[0], "alpha");
+    FILLK(resplocks[1], "beta");
+    resplocks[0]->timeout = 100;
+    resplocks[1]->timeout = 200;
+
+    rbody = multi_lock_response(resplocks);
+    
+    resp = ne_concat("HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
+                    "Lock-Token: <opaquelocktoken:beta>" EOL
+                    "Connection: close\r\n\r\n", rbody, NULL);
+    ne_free(rbody);
+
+    CALL(make_session(&sess, single_serve_string, resp));
+    ne_free(resp);
+
+    lock = ne_lock_create();
+    ne_fill_server_uri(sess, &lock->uri);
+    lock->uri.path = ne_strdup("/beta");
+
+    ONREQ(ne_lock(sess, lock));
+
+    CALL(await_server());
+
+    CALL(lock_compare("returned lock", resplocks[1], lock));
+    
+    ne_session_destroy(sess);
+    ne_lock_destroy(lock);
+    ne_lock_destroy(resplocks[0]);
+    ne_lock_destroy(resplocks[1]);
+
+    return OK;
+}
+
+static void dummy_discover(void *userdata, const struct ne_lock *lock,
+                           const char *uri, const ne_status *status)
+{
+}
+
+/* This failed with neon 0.23.x and earlier when memory leak detection
+ * is enabled. */
+static int fail_discover(void)
+{
+    ne_session *sess;
+    int ret;
+    
+    CALL(make_session(&sess, single_serve_string, 
+                      "HTTP/1.0 207 OK\r\n" "Connection: close\r\n" "\r\n"
+                      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                      "<D:multistatus xmlns:D='DAV:'>\n"
+                      "<D:response><D:href>/foo/bar</D:href><D:propstat>\n"
+                      "</parse this, my friend>\n"));
+    
+    ret = ne_lock_discover(sess, "/foo", dummy_discover, NULL);
     CALL(await_server());
 
+    ONN("discovery okay for response with invalid XML!?", ret != NE_ERROR);
+
+    ne_session_destroy(sess);
     return OK;
 }
 
 ne_test tests[] = {
+    T(lookup_localhost),
+    T(store_single),
+    T(store_several),
+    T(if_simple),
+    T(if_under_infinite),
+    T(if_infinite_over),
+    T(if_child),
+    T(if_covered_child),
     T(lock_timeout),
+    T(lock_shared),
+    T(discover),
+    T_XLEAKY(fail_discover),
     T(NULL)
 };
 
index e66dabf..d9548b8 100755 (executable)
@@ -3,14 +3,27 @@
 
 srcdir=${1-.}
 
+OPENSSL=${OPENSSL-openssl}
+CONF=${srcdir}/openssl.conf
+REQ="${OPENSSL} req -config ${CONF}"
+CA="${OPENSSL} ca -config ${CONF} -batch"
+# MKCERT makes a self-signed cert
+MKCERT="${REQ} -x509 -new -days 9000"
+
+REQDN=reqDN
+STRMASK=default
+export REQDN STRMASK
+
 set -ex
 
 mkdir ca
+touch ca/index.txt
+echo 01 > ca/serial
 
-openssl genrsa -rand ${srcdir}/../configure > ca/key.pem
-openssl genrsa -rand ${srcdir}/../configure > client.key
+${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
+${MKCERT} -key ca/key.pem -out ca/cert.pem <<EOF
 US
 California
 Oakland
@@ -20,73 +33,134 @@ nowhere.example.com
 neon@webdav.org
 EOF
 
-openssl req -new -key ${srcdir}/server.key -out server.csr <<EOF
+# Function to generate appropriate output for `openssl req'.
+csr_fields() {
+CN=${2-"localhost"}
+OU=${1-"Neon QA Dept"}
+Org=${3-"Neon Hackers Ltd"}
+Locality=${4-"Cambridge"}
+State=${5-"Cambridgeshire"}
+cat <<EOF
 GB
-Cambridgeshire
-Cambridge
-Neon Hackers Ltd
-Neon QA Dept
-localhost
+${State}
+${Locality}
+${Org}
+${OU}
+${CN}
 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
+csr_fields | ${REQ} -new -key ${srcdir}/server.key -out server.csr
+
+csr_fields "Upper Case Dept" lOcALhost | \
+${REQ} -new -key ${srcdir}/server.key -out caseless.csr
+
+csr_fields "Use AltName Dept" nowhere.example.com | \
+${REQ} -new -key ${srcdir}/server.key -out altname1.csr
+
+csr_fields "Two AltName Dept" nowhere.example.com | \
+${REQ} -new -key ${srcdir}/server.key -out altname2.csr
+
+csr_fields "Third AltName Dept" nowhere.example.com | \
+${REQ} -new -key ${srcdir}/server.key -out altname3.csr
+
+csr_fields "Fourth AltName Dept" localhost | \
+${REQ} -new -key ${srcdir}/server.key -out altname4.csr
+
+csr_fields "Fifth Altname Dept" localhost | \
+${REQ} -new -key ${srcdir}/server.key -out altname5.csr
+
+csr_fields "Self-Signed" | \
+${MKCERT} -key ${srcdir}/server.key -out ssigned.pem
+
+csr_fields "Bad Hostname Department" nohost.example.com | \
+${MKCERT} -key ${srcdir}/server.key -out wrongcn.pem
+
+# default => T61String
+csr_fields "`echo -e 'H\350llo World'`" localhost |
+${MKCERT} -key ${srcdir}/server.key -out t61subj.cert
+
+STRMASK=pkix # => BMPString
+csr_fields "`echo -e 'H\350llo World'`" localhost |
+${MKCERT} -key ${srcdir}/server.key -out bmpsubj.cert
+
+STRMASK=utf8only # => UTF8String
+csr_fields "`echo -e 'H\350llo World'`" localhost |
+${MKCERT} -key ${srcdir}/server.key -out utf8subj.cert
+
+STRMASK=default
+
+### produce a set of CA certs
+
+csr_fields "First Random CA" "first.example.com" "CAs Ltd." Lincoln Lincolnshire | \
+${MKCERT} -key ${srcdir}/server.key -out ca1.pem
+
+csr_fields "Second Random CA" "second.example.com" "CAs Ltd." Falmouth Cornwall | \
+${MKCERT} -key ${srcdir}/server.key -out ca2.pem
 
-# 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`
+csr_fields "Third Random CA" "third.example.com" "CAs Ltd." Ipswich Suffolk | \
+${MKCERT} -key ${srcdir}/server.key -out ca3.pem
+
+csr_fields "Fourth Random CA" "fourth.example.com" "CAs Ltd." Norwich Norfolk | \
+${MKCERT} -key ${srcdir}/server.key -out ca4.pem
+
+cat ca[1234].pem > calist.pem
+
+# Only works with a Linuxy hostname command: continue without it,
+# as appropriate tests are skipped if these fail.
+hostname=`hostname -s 2>/dev/null` || true
+domain=`hostname -d 2>/dev/null` || true
+fqdn=`hostname -f 2>/dev/null` || true
 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
+  csr_fields "Wildcard Cert Dept" "*.${domain}" | \
+  ${REQ} -new -key ${srcdir}/server.key -out wildcard.csr
+  ${CA} -days 9000 -in wildcard.csr -out wildcard.cert
 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
+csr_fields "Neon Client Cert" ignored.example.com | \
+${REQ} -new -key client.key -out client.csr
 
-touch ca/index.txt
-echo 01 > ca/serial
+### requests using special DN.
+
+REQDN=reqDN.doubleCN
+csr_fields "Double CN Dept" "nohost.example.com
+localhost" | ${REQ} -new -key ${srcdir}/server.key -out twocn.csr
+
+REQDN=reqDN.CNfirst
+echo localhost | ${REQ} -new -key ${srcdir}/server.key -out cnfirst.csr
+
+REQDN=reqDN.missingCN
+echo GB | ${REQ} -new -key ${srcdir}/server.key -out missingcn.csr
 
-openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in server.csr \
-    -out server.cert
+REQDN=reqDN.justEmail
+echo blah@example.com | ${REQ} -new -key ${srcdir}/server.key -out justmail.csr
 
-openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in wildcard.csr \
-    -out wildcard.cert
+### don't put ${REQ} invocations after here
 
-openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in client.csr \
-    -out client.cert
+for f in server client twocn caseless cnfirst missingcn justmail; do
+  ${CA} -days 900 -in ${f}.csr -out ${f}.cert
+done
+
+for n in 1 2 3 4 5; do
+ ${CA} -extensions altExt${n} -days 900 \
+     -in altname${n}.csr -out altname${n}.cert
+done
+
+MKPKCS12="${OPENSSL} pkcs12 -export -passout stdin -in client.cert -inkey client.key"
 
 # 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
+echo foobar | ${MKPKCS12} -name "Just A Neon Client Cert" -out client.p12
+
+# generate a PKCS#12 cert with no password and a friendly name
+echo | ${MKPKCS12} -name "An Unencrypted Neon Client Cert" -out unclient.p12
+
+# generate a PKCS#12 cert with no friendly name
+echo | ${MKPKCS12} -out noclient.p12
+
+### a file containing a complete chain
+
+cat ca/cert.pem server.cert > chain.pem
index 32dfa61..827b44e 100644 (file)
@@ -10,12 +10,73 @@ serial = $dir/serial
 private_key = $dir/key.pem
 policy = policy_any
 default_md = md5
+x509_extensions = issuedExt
 
 [policy_any]
-countryName = supplied
+countryName = optional
 stateOrProvinceName = optional
 localityName = optional
 organizationName = optional
 organizationalUnitName = optional
-commonName = supplied
+commonName = optional
 emailAddress = optional
+
+[req]
+distinguished_name = $ENV::REQDN
+x509_extensions = caExt
+string_mask = $ENV::STRMASK
+
+[caExt]
+basicConstraints = CA:true
+
+[issuedExt]
+basicConstraints = CA:false
+
+# subjectAltName extension sections
+[altExt1]
+subjectAltName = DNS:localhost
+
+# 2+3: AltNames with multiple entries to test the matching logic
+[altExt2]
+subjectAltName = DNS:nohost.example.com, DNS:localhost
+
+[altExt3]
+subjectAltName = DNS:localhost, DNS:nohost.example.com
+
+# an AltName with no DNS entries; should use commonName instead for
+# identity check
+[altExt4]
+subjectAltName = email:neon@webdav.org
+
+# an AltName with IP address
+[altExt5]
+subjectAltName = IP:127.0.0.1
+
+[reqDN]
+countryName = Country Name
+stateOrProvinceName = State or Province Name
+localityName = Locality Name
+organizationName = Organization Name
+organizationalUnitName = Organizational Unit Name
+commonName = Common Name (eg, your name or your server\'s hostname)
+emailAddress = Email Address
+
+# a DN which gives two commonName attributes.
+[reqDN.doubleCN]
+countryName = Country Name
+stateOrProvinceName = State or Province Name
+localityName = Locality Name
+organizationName = Organization Name
+organizationalUnitName = Organizational Unit Name
+0.commonName = Common Name
+1.commonName = Common Name
+emailAddress = Email Address
+
+[reqDN.CNfirst]
+commonName = Common Name
+
+[reqDN.missingCN]
+countryName = CountryName
+
+[reqDN.justEmail]
+emailAddress = CountryName
index 7c2a635..ef0f1e7 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Tests for property handling
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2004, 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
@@ -57,8 +57,495 @@ static int patch_simple(void)
     return await_server();
 }
 
+#define RESP207 "HTTP/1.0 207 Stuff\r\n" "Server: foo\r\n\r\n"
+
+static void dummy_results(void *ud, const char *href,
+                         const ne_prop_result_set *rset)
+{
+    NE_DEBUG(NE_DBG_HTTP, "dummy_results.\n");
+}
+
+/* Regression tests for propfind bodies which caused segfaults. */
+static int regress(void)
+{
+    static const char *bodies[] = { 
+       RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+       "<multistatus xmlns=\"DAV:\">"
+       "<response><propstat><prop><href>"
+       "</href></prop></propstat></response>"
+       "</multistatus>",
+       
+       /* segfaults with neon <= 0.23.5 */
+       RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
+       "<D:response><D:href>/foo/</D:href>"
+       "<D:propstat/>"
+       "<D:status>HTTP/1.1 404 Not Found</D:status>"
+       "</D:multistatus>",
+
+       /* format string handling with neon <= 0.24.4 */
+       RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
+       "<D:response><D:href>/foo/</D:href>"
+       "<D:propstat/>"
+       "<D:status>%s%s%s%s</D:status>"
+       "</D:response></D:multistatus>",
+
+       NULL,
+    };
+    ne_session *sess;
+    int n;
+
+    for (n = 0; bodies[n] != NULL; n++) {
+       CALL(make_session(&sess, single_serve_string, (void *)bodies[n]));
+       ne_simple_propfind(sess, "/", 0, NULL, dummy_results, NULL);
+       ne_session_destroy(sess);
+       CALL(await_server());
+    }
+
+    return OK;
+}
+
+static int patch_regress(void)
+{
+    static const char *bodies[] = { 
+       /* format string handling bugs with neon <= 0.24.4 */
+       RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
+       "<D:response><D:href>/foo/</D:href>"
+       "<D:status>HTTP/1.1 500 Bad Voodoo</D:status>"
+       "<D:responsedescription>%s%s%s%s</D:responsedescription>"
+        "</D:response></D:multistatus>",
+
+       RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
+       "<D:response><D:href>/foo/</D:href>"
+       "<D:status>HTTP/1.1 %s%s%s%s</D:status>",
+
+        NULL
+    };
+    ne_session *sess;
+    int n;
+    static const ne_propname pn = { "DAV:", "foobar" };
+    ne_proppatch_operation pops[] = { 
+        { &pn, ne_propset, "fish" },
+        { NULL, ne_propset, NULL }
+    };
+
+    for (n = 0; bodies[n] != NULL; n++) {
+       CALL(make_session(&sess, single_serve_string, (void *)bodies[n]));
+       ne_proppatch(sess, "/", pops);
+       ne_session_destroy(sess);
+       CALL(await_server());
+    }
+
+    return OK;
+}
+
+static int pstat_count;
+
+/* tos_*: set of 207 callbacks which serialize the data back into a
+ * text stream, which can be easily checked for correctness. */
+static void *tos_startresp(void *buf, const char *href)
+{
+    ne_buffer_concat(buf, "start-resp[", href, "];", NULL);
+    pstat_count = 0;
+    return ne_strdup(href);
+}
+
+static void tos_status_descr(ne_buffer *buf, const ne_status *status,
+                             const char *description)
+{
+    if (status) {
+        char s[50];
+        ne_snprintf(s, sizeof s, "-status={%d %s}", status->code,
+                    status->reason_phrase);
+        ne_buffer_zappend(buf, s);
+    }
+    if (description)
+        ne_buffer_concat(buf, "-descr={", description, "}", NULL);
+}
+
+static void tos_endresp(void *buf, void *response,
+                        const ne_status *status, const char *description)
+{
+    char *href = response;
+    ne_buffer_concat(buf, "end-resp[", href, "]", NULL);
+    ne_free(href);
+    tos_status_descr(buf, status, description);
+    ne_buffer_zappend(buf, ";");
+}
+
+static void *tos_startpstat(void *buf, void *resphref)
+{
+    char num[20], *href;
+    sprintf(num, "-%d", ++pstat_count);
+    href = ne_concat(resphref, num, NULL);
+    ne_buffer_concat(buf, "start-pstat[", href, "];", NULL);
+    return href;
+}
+
+static void tos_endpstat(void *buf, void *href,
+                         const ne_status *status, const char *description)
+{
+    ne_buffer_concat(buf, "end-pstat[", href, "]", NULL);
+    tos_status_descr(buf, status, description);
+    ne_buffer_zappend(buf, ";");
+    ne_free(href);
+}
+
+struct propctx {
+    ne_207_parser *p207;
+    ne_buffer *buf;
+};
+
+#define STATE_myprop (NE_PROPS_STATE_TOP)
+
+static int tos_startprop(void *userdata, int parent,
+                         const char *nspace, const char *name,
+                         const char **atts)
+{
+    if (parent == NE_207_STATE_PROP && 
+        strcmp(nspace, "DAV:") == 0 && 
+        (strcmp(name, "propone") == 0 || strcmp(name, "proptwo") == 0)) {
+        /* Handle this! */
+        struct propctx *ctx = userdata;
+        char *resphref = ne_207_get_current_response(ctx->p207);
+        char *pstathref = ne_207_get_current_propstat(ctx->p207);
+        
+        ne_buffer_concat(ctx->buf, "start-prop[", resphref, ",", pstathref,
+                         ",", name, "];", NULL);
+
+        return STATE_myprop;
+    } else {
+        return NE_XML_DECLINE;
+    }
+}
+
+static int tos_cdata(void *userdata, int state,
+                     const char *cdata, size_t len)
+{
+    struct propctx *ctx = userdata;
+
+    ne_buffer_zappend(ctx->buf, "cdata-prop[");
+    ne_buffer_append(ctx->buf, cdata, len);
+    ne_buffer_zappend(ctx->buf, "];");
+    return 0;
+}
+
+static int tos_endprop(void *userdata, int state,
+                       const char *nspace, const char *name)
+{
+    struct propctx *ctx = userdata;
+
+    ne_buffer_concat(ctx->buf, "end-prop[", name, "];", NULL);
+    return 0;
+}
+
+
+static int run_207_response(char *resp, const char *expected)
+{
+    ne_buffer *buf = ne_buffer_create();
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    ne_xml_parser *p = ne_xml_create();
+    ne_207_parser *p207 = ne_207_create(p, buf);
+    ne_request *req = ne_request_create(sess, "PROPFIND", "/foo");
+    struct propctx ctx;
+
+    ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
+
+    ne_207_set_response_handlers(p207, tos_startresp, tos_endresp);
+    ne_207_set_propstat_handlers(p207, tos_startpstat, tos_endpstat);
+
+    ctx.buf = buf;
+    ctx.p207 = p207;
+    ne_xml_push_handler(p, tos_startprop, tos_cdata, tos_endprop, &ctx);
+
+    CALL(spawn_server(7777, single_serve_string, resp));
+
+    ONREQ(ne_request_dispatch(req));
+
+    CALL(await_server());
+
+    ONV(!ne_xml_valid(p),
+        ("response body was invalid: %s", ne_xml_get_error(p)));
+
+    ONV(strcmp(buf->data, expected),
+        ("comparison failed.\n"
+         "expected string: `%s'\n"
+         "got string:      `%s'", expected, buf->data));
+
+    ne_buffer_destroy(buf);
+    ne_207_destroy(p207);
+    ne_xml_destroy(p);
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+    return OK;
+}
+
+/* Macros for easily writing a 207 response body; all expand to
+ * a string literal. */
+#define MULTI_207(x) "HTTP/1.0 207 Foo\r\nConnection: close\r\n\r\n" \
+"<?xml version='1.0'?>\r\n" \
+"<D:multistatus xmlns:D='DAV:'>" x "</D:multistatus>"
+#define RESP_207(href, x) "<D:response><D:href>" href "</D:href>" x \
+"</D:response>"
+#define PSTAT_207(x) "<D:propstat>" x "</D:propstat>"
+#define STAT_207(s) "<D:status>HTTP/1.1 " s "</D:status>"
+#define DESCR_207(d) "<D:responsedescription>" d "</D:responsedescription>"
+#define DESCR_REM "The end of the world, as we know it"
+
+#define PROPS_207(x) "<D:prop>" x "</D:prop>"
+#define APROP_207(n, c) "<D:" n ">" c "</D:" n ">"
+
+/* Tests for the 207 interface: send a 207 response body, compare the
+ * re-serialized string returned with that expected. */
+static int two_oh_seven(void)
+{
+    static char *ts[][2] = {
+        { MULTI_207(RESP_207("/foo", "")),
+          "start-resp[/foo];end-resp[/foo];" },
+
+        /* test for response status handling */
+        { MULTI_207(RESP_207("/bar", STAT_207("200 OK"))), 
+          "start-resp[/bar];end-resp[/bar]-status={200 OK};" },
+
+        /* test that empty description == NULL description argument */
+        { MULTI_207(RESP_207("/bar", STAT_207("200 OK") DESCR_207(""))), 
+          "start-resp[/bar];end-resp[/bar]-status={200 OK};" },
+
+        /* test multiple responses */
+        { MULTI_207(RESP_207("/hello/world", STAT_207("200 OK"))
+                    RESP_207("/foo/bar", STAT_207("999 French Fries"))), 
+          "start-resp[/hello/world];end-resp[/hello/world]-status={200 OK};"
+          "start-resp[/foo/bar];end-resp[/foo/bar]"
+          "-status={999 French Fries};" 
+        },
+
+        /* test multiple propstats in mulitple responses */
+        { MULTI_207(RESP_207("/al/pha", 
+                             PSTAT_207(STAT_207("321 Une"))
+                             PSTAT_207(STAT_207("432 Deux"))
+                             PSTAT_207(STAT_207("543 Trois")))
+                    RESP_207("/be/ta",
+                             PSTAT_207(STAT_207("787 Quatre"))
+                             PSTAT_207(STAT_207("878 Cinq")))),
+          "start-resp[/al/pha];"
+          "start-pstat[/al/pha-1];end-pstat[/al/pha-1]-status={321 Une};"
+          "start-pstat[/al/pha-2];end-pstat[/al/pha-2]-status={432 Deux};"
+          "start-pstat[/al/pha-3];end-pstat[/al/pha-3]-status={543 Trois};"
+          "end-resp[/al/pha];"
+          "start-resp[/be/ta];"
+          "start-pstat[/be/ta-1];end-pstat[/be/ta-1]-status={787 Quatre};"
+          "start-pstat[/be/ta-2];end-pstat[/be/ta-2]-status={878 Cinq};"
+          "end-resp[/be/ta];"
+        },
+
+        /* test that incomplete responses are completely ignored. */
+        { MULTI_207("<D:response/>"
+                    RESP_207("/", STAT_207("123 Hoorah"))
+                    "<D:response/>"
+                    "<D:response><D:propstat>hello</D:propstat></D:response>"
+                    "<D:response><D:href/></D:response>"
+                    RESP_207("/bar", STAT_207("200 OK"))),
+          "start-resp[/];end-resp[/]-status={123 Hoorah};"
+          "start-resp[/bar];end-resp[/bar]-status={200 OK};" },
+
+        /* tests for propstat status */
+        { MULTI_207(RESP_207("/pstat",
+                            PSTAT_207("<D:prop/>" STAT_207("666 Doomed")))),
+          "start-resp[/pstat];start-pstat[/pstat-1];"
+          "end-pstat[/pstat-1]-status={666 Doomed};end-resp[/pstat];" },
+
+        { MULTI_207(RESP_207("/pstat", PSTAT_207("<D:status/>"))),
+          "start-resp[/pstat];start-pstat[/pstat-1];"
+          "end-pstat[/pstat-1];end-resp[/pstat];" },
+
+        /* tests for responsedescription handling */
+        { MULTI_207(RESP_207("/bar", STAT_207("200 OK") DESCR_207(DESCR_REM))), 
+          "start-resp[/bar];end-resp[/bar]-status={200 OK}"
+          "-descr={" DESCR_REM "};" },
+
+        { MULTI_207(RESP_207("/bar",
+                             PSTAT_207(STAT_207("456 Too Hungry")
+                                       DESCR_207("Not enough food available"))
+                             STAT_207("200 OK") DESCR_207("Not " DESCR_REM))), 
+          "start-resp[/bar];"
+          "start-pstat[/bar-1];end-pstat[/bar-1]-status={456 Too Hungry}"
+          "-descr={Not enough food available};"
+          "end-resp[/bar]-status={200 OK}-descr={Not " DESCR_REM "};" },
+
+        /* intermingle some random elements and cdata to make sure
+         * they are ignored. */
+        { MULTI_207("<D:fish-food/>blargl" 
+                    RESP_207("/b<ping-pong/>ar", "<D:sausages/>"
+                             PSTAT_207("<D:hello-mum/>blergl") 
+                             STAT_207("200 <pong-ping/> OK") "foop"
+                             DESCR_207(DESCR_REM) "carroon") 
+                    "carapi"), 
+          "start-resp[/bar];start-pstat[/bar-1];end-pstat[/bar-1];"
+          "end-resp[/bar]-status={200 OK}-descr={" DESCR_REM "};" },
+
+        /* test for properties within a 207. */
+        { MULTI_207(RESP_207("/alpha",
+                             PSTAT_207(PROPS_207(
+                                           APROP_207("propone", "hello")
+                                           APROP_207("proptwo", "foobar"))
+                                       STAT_207("200 OK")))),
+          "start-resp[/alpha];start-pstat[/alpha-1];"
+          "start-prop[/alpha,/alpha-1,propone];cdata-prop[hello];"
+          "end-prop[propone];"
+          "start-prop[/alpha,/alpha-1,proptwo];cdata-prop[foobar];"
+          "end-prop[proptwo];"
+          "end-pstat[/alpha-1]-status={200 OK};end-resp[/alpha];" }
+
+    };
+    size_t n;
+
+    for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++)
+        CALL(run_207_response(ts[n][0], ts[n][1]));        
+
+    return OK;
+}
+
+/* Serialize propfind result callbacks into a string */
+static int simple_iterator(void *buf, const ne_propname *name,
+                           const char *value, const ne_status *st)
+{
+    char code[20];
+    ne_buffer_concat(buf, "prop:[{", name->nspace, ",", 
+                     name->name, "}=", NULL);
+    if (value)
+        ne_buffer_concat(buf, "'", value, "'", NULL);
+    else 
+        ne_buffer_zappend(buf, "#novalue#");
+    sprintf(code, ":{%d ", st->code);
+    if (st->reason_phrase)
+        ne_buffer_concat(buf, code, st->reason_phrase, "}];", NULL);
+    else
+        ne_buffer_concat(buf, code, "#noreason#}];", NULL);
+    return 0;
+}
+
+static void simple_results(void *buf, const char *href,
+                           const ne_prop_result_set *rset)
+{
+    ne_buffer_concat(buf, "results(", href, ",", NULL);
+    ne_propset_iterate(rset, simple_iterator, buf);
+    ne_buffer_zappend(buf, ")//");
+}
+
+static int diffcmp(const char *expected, const char *actual)
+{
+    size_t n;
+    
+    if (!strcmp(expected, actual)) return OK;
+
+    for (n = 0; expected[n] && actual[n]; n++) {
+        if (expected[n] != actual[n]) {
+            t_context("difference at byte %" NE_FMT_SIZE_T ": "
+                      "`%.6s...' not `%.6s...'",
+                      n, actual+n, expected+n);
+            break;
+        }
+    }
+
+    return FAIL;
+}
+       
+
+static int run_simple_propfind(const ne_propname *props, char *resp, 
+                               int depth, const char *expected)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    ne_buffer *buf = ne_buffer_create();
+
+    CALL(spawn_server(7777, single_serve_string, resp));
+
+    ONREQ(ne_simple_propfind(sess, "/propfind", depth, props,
+                             simple_results, buf));
+
+    CALL(await_server());
+
+    CALL(diffcmp(expected, buf->data));
+
+    ne_buffer_destroy(buf);
+    ne_session_destroy(sess);
+    return OK;
+}
+
+/* a PROPFIND response body for the {DAV:}fishbone property, using
+ * given property value and status. */
+#define FISHBONE_RESP(value, status) MULTI_207(RESP_207("/foop", \
+       PSTAT_207(PROPS_207(APROP_207("fishbone", value)) \
+       STAT_207(status))))
+
+static int pfind_simple(void)
+{
+    static const struct {
+        char *resp;
+        const char *expected;
+        int depth, pset;
+    } ts[] = {
+        /* simple single property. */
+        { FISHBONE_RESP("hello, world", "212 Well OK"),
+          "results(/foop,prop:[{DAV:,fishbone}='hello, world':{212 Well OK}];)//",
+          0, 0 },
+        /* property with some nested elements. */
+        { FISHBONE_RESP("this is <foo/> a property <bar><lemon>fish</lemon></bar> value", 
+                        "299 Just About OK"),
+          "results(/foop,prop:[{DAV:,fishbone}="
+          "'this is <foo></foo> a property "
+          "<bar><lemon>fish</lemon></bar> value':"
+          "{299 Just About OK}];)//",
+          0, 0 },
+
+        /* failed to fetch a property. */
+        { FISHBONE_RESP("property value is ignored", 
+                        "404 Il n'ya pas de property"),
+          "results(/foop,prop:[{DAV:,fishbone}=#novalue#:"
+          "{404 Il n'ya pas de property}];)//",
+          0, 0 },
+
+#if 0
+        /* propstat missing status should be ignored; if a response contains no
+         * valid propstats, it should also be ignored. */
+        { MULTI_207(RESP_207("/alpha", PSTAT_207(APROP_207("fishbone", "unseen")))
+                    RESP_207("/beta", PSTAT_207(APROP_207("fishbone", "hello, world")
+                                                STAT_207("200 OK")))),
+          "results(/beta,prop:[{DAV:,fishbone}='hello, world':{200 OK}];)//", 0, 0},
+#endif
+
+        /* props on several resources */
+        { MULTI_207(RESP_207("/alpha",
+                             PSTAT_207(PROPS_207(APROP_207("fishbone", "strike one"))
+                                       STAT_207("234 First is OK")))
+                    RESP_207("/beta",
+                             PSTAT_207(PROPS_207(APROP_207("fishbone", "strike two"))
+                                       STAT_207("256 Second is OK")))),
+          "results(/alpha,prop:[{DAV:,fishbone}='strike one':{234 First is OK}];)//"
+          "results(/beta,prop:[{DAV:,fishbone}='strike two':{256 Second is OK}];)//",
+          0, 0}          
+    };
+    const ne_propname pset1[] = {
+        { "DAV:", "fishbone", },
+        { NULL, NULL }
+    };
+    size_t n;
+
+    for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
+        const ne_propname *pset = pset1;
+
+        CALL(run_simple_propfind(pset, ts[n].resp, ts[n].depth,
+                                 ts[n].expected));
+    }
+
+
+    return OK;
+}
+
 ne_test tests[] = {
+    T(two_oh_seven),
     T(patch_simple),
+    T(pfind_simple),
+    T(regress),
+    T(patch_regress),
     T(NULL) 
 };
 
index 582462e..80b23bb 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Tests for 3xx redirect interface (ne_redirect.h)
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
@@ -38,6 +38,7 @@
 struct redir_args {
     int code;
     const char *dest;
+    const char *path;
 };
 
 static int serve_redir(ne_socket *sock, void *ud)
@@ -58,53 +59,77 @@ static int serve_redir(ne_socket *sock, void *ud)
     return OK;
 }
 
+/* Run a request to 'path' and retrieve the redirect destination to
+ * *redir. */
+static int process_redir(ne_session *sess, const char *path,
+                         const ne_uri **redir)
+{
+    ONN("did not get NE_REDIRECT", any_request(sess, path) != NE_REDIRECT);
+    *redir = ne_redirect_location(sess);
+    return OK;
+}
+
 static int check_redir(struct redir_args *args, const char *expect)
 {
     ne_session *sess;
-    int ret;
     const ne_uri *loc;
+    char *unp;
     
     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);
-    
+    CALL(process_redir(sess, args->path, &loc));
     ONN("redirect location was NULL", loc == NULL);
 
-    ONV(strcmp(ne_uri_unparse(loc), expect),
-       ("redirected to `%s' not `%s'", ne_uri_unparse(loc), expect));
+    unp = ne_uri_unparse(loc);
+    ONV(strcmp(unp, expect), ("redirected to `%s' not `%s'", unp, expect));
+    ne_free(unp);
 
     ne_session_destroy(sess);
+    CALL(await_server());
 
     return OK;
 }
 
 #define DEST "http://foo.com/blah/blah/bar"
+#define PATH "/redir/me"
 
 static int simple(void)
 {
-    struct redir_args args = {302, DEST};
-    return check_redir(&args, DEST);
-}
+    struct redir_args args[] = {
+        {301, DEST, PATH},
+        {302, DEST, PATH},
+        {303, DEST, PATH},
+        {307, DEST, PATH},
+        {0, NULL, NULL}
+    };
+    int n;
+    
+    for (n = 0; args[n].code; n++)
+        CALL(check_redir(&args[n], DEST));
 
-static int redir_303(void)
-{
-    struct redir_args args = {303, DEST};
-    return check_redir(&args, DEST);
+    return OK;
 }
 
 /* check that a non-absoluteURI is qualified properly */
 static int non_absolute(void)
 {
-    struct redir_args args = {302, "/foo/bar/blah"};
+    struct redir_args args = {302, "/foo/bar/blah", PATH};
     return check_redir(&args, "http://localhost:7777/foo/bar/blah");
 }
 
+static int relative_1(void)
+{
+    struct redir_args args = {302, "norman", "/foo/bar"};
+    return check_redir(&args, "http://localhost:7777/foo/norman");
+}
+    
+static int relative_2(void)
+{
+    struct redir_args args = {302, "wishbone", "/foo/bar/"};
+    return check_redir(&args, "http://localhost:7777/foo/bar/wishbone");
+}    
+
 #if 0
 /* could implement failure on self-referential redirects, but
  * realistically, the application must implement a max-redirs count
@@ -125,11 +150,40 @@ static int fail_loop(void)
 }
 #endif
 
+/* ensure that ne_redirect_location returns NULL when no redirect has
+ * been encountered, or redirect hooks aren't registered. */
+static int no_redirect(void)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    const ne_uri *loc;
+
+    ONN("redirect non-NULL before register", ne_redirect_location(sess));
+    ne_redirect_register(sess);
+    ONN("initial redirect non-NULL", ne_redirect_location(sess));
+
+    CALL(spawn_server(7777, single_serve_string, 
+                      "HTTP/1.0 200 OK\r\n\r\n\r\n"));
+    ONREQ(any_request(sess, "/noredir"));
+    CALL(await_server());
+
+    ONN("redirect non-NULL after non-redir req", ne_redirect_location(sess));
+
+    CALL(spawn_server(7777, single_serve_string, "HTTP/1.0 302 Get Ye Away\r\n"
+                      "Location: /blah\r\n" "\r\n"));
+    CALL(process_redir(sess, "/foo", &loc));
+    CALL(await_server());
+
+    ne_session_destroy(sess);
+    return OK;
+}
+
 ne_test tests[] = {
     T(lookup_localhost),
     T(simple),
-    T(redir_303),
     T(non_absolute),
+    T(relative_1),
+    T(relative_2),
+    T(no_redirect),
     T(NULL) 
 };
 
diff --git a/neon/test/regress.c b/neon/test/regress.c
deleted file mode 100644 (file)
index a2d70e5..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/* 
-   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
-};
index 7d4180c..4df5c78 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    HTTP request handling tests
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2003, 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
@@ -22,6 +22,8 @@
 
 #include <sys/types.h>
 
+#include <time.h> /* for time() */
+
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
 
 #include "tests.h"
 #include "child.h"
+#include "utils.h"
 
 static char buffer[BUFSIZ];
-static int clength;
 
-static int discard_request(nsocket *sock)
+static ne_session *def_sess;
+static ne_request *def_req;
+
+static int prepare_request(server_fn fn, void *ud)
 {
-    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;
-}
+    static char uri[100];
 
-#define RESP200 "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
-#define TE_CHUNKED "Transfer-Encoding: chunked\r\n"
+    def_sess = ne_session_create("http", "localhost", 7777);
 
-static int single_serve_string(nsocket *s, void *userdata)
-{
-    const char *str = userdata;
+    sprintf(uri, "/test%d", test_num);
 
-    ON(discard_request(s));
-    ON(sock_send_string(s, str));
+    def_req = ne_request_create(def_sess, "GET", uri);
+
+    CALL(spawn_server(7777, fn, ud));
 
     return OK;
 }
 
+static int finish_request(void)
+{
+    ne_request_destroy(def_req);
+    ne_session_destroy(def_sess);
+    return await_server();
+}
+
+#define RESP200 "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
+#define TE_CHUNKED "Transfer-Encoding: chunked\r\n"
+
 /* takes response body chunks and appends them to a buffer. */
 static void collector(void *ud, const char *data, size_t len)
 {
@@ -72,11 +75,9 @@ static void collector(void *ud, const char *data, size_t len)
     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);
 
+/* construct a get request, callback for run_request. */
 static ne_request *construct_get(ne_session *sess, void *userdata)
 {
     ne_request *r = ne_request_create(sess, "GET", "/");
@@ -87,6 +88,7 @@ static ne_request *construct_get(ne_session *sess, void *userdata)
     return r;
 }
 
+/* run a request created by callback 'cb' in session 'sess'. */
 static int run_request(ne_session *sess, int status,
                       construct_request cb, void *userdata)
 {
@@ -96,38 +98,117 @@ static int run_request(ne_session *sess, int status,
     
     ONREQ(ne_request_dispatch(req));
  
-    ONN("response status", ne_get_status(req)->code != status);
+    ONV(ne_get_status(req)->code != status,
+       ("response status-code was %d not %d",
+        ne_get_status(req)->code, status));
+
+    ne_request_destroy(req);
+
+    return OK;
+}
 
+/* Runs a server function 'fn', expecting to get a header 'name' with value
+ * 'value' in the response. */
+static int expect_header_value(const char *name, const char *value,
+                              server_fn fn, void *userdata)
+{
+    ne_session *sess;
+    ne_request *req;
+    char *gotval = NULL;
+
+    CALL(make_session(&sess, fn, userdata));
+
+    req = ne_request_create(sess, "FOO", "/bar");
+    ne_add_response_header_handler(req, name, ne_duplicate_header, &gotval);
+    ONREQ(ne_request_dispatch(req));
+    CALL(await_server());
+    
+    ONN("no header value set", gotval == NULL);
+    ONV(strcmp(gotval, value),
+       ("header value mis-match: got [%s] not [%s]", gotval, value));
+    
     ne_request_destroy(req);
+    ne_session_destroy(sess);
+    ne_free(gotval);
 
     return OK;
 }
 
+/* runs a server function 'fn', expecting response body to be equal to
+ * 'expect' */
 static int expect_response(const char *expect, server_fn fn, void *userdata)
 {
-    ne_session *sess = ne_session_create();
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
     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;
-    }
+    CALL(run_request(sess, 200, construct_get, buf));
 
     ON(await_server());
 
     ONN("response body match", strcmp(buf->data, expect));
 
     ne_session_destroy(sess);
+    ne_buffer_destroy(buf);
+    
+    return OK;
+}
+
+#define EMPTY_RESP RESP200 "Content-Length: 0\r\n\r\n"
+
+/* Process a request with given method and response, expecting to get
+ * a zero-length response body.  A second request is sent down the
+ * connection (to ensure that the response isn't silently eaten), so
+ * 'resp' must be an HTTP/1.1 response with no 'Connection: close'
+ * header. */
+static int expect_no_body(const char *method, const char *resp)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    ne_request *req = ne_request_create(sess, method, "/first");
+    ssize_t ret;
+    char *r = ne_malloc(strlen(resp) + sizeof(EMPTY_RESP));
     
+    strcpy(r, resp);
+    strcat(r, EMPTY_RESP);
+    ON(spawn_server(7777, single_serve_string, r));
+    ne_free(r);
+
+    ONN("failed to begin request", ne_begin_request(req));
+    ret = ne_read_response_block(req, buffer, BUFSIZ);
+    ONV(ret != 0, ("got response block of size %" NE_FMT_SSIZE_T, ret));
+    ONN("failed to end request", ne_end_request(req));
+
+    /* process following request; makes sure that nothing extra has
+     * been eaten by the first request. */
+    ONV(any_request(sess, "/second"),
+       ("second request on connection failed: %s",ne_get_error(sess)));
+
+    ON(await_server());
+
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
     return OK;
 }
 
+static int reason_phrase(void)
+{
+    ne_session *sess;
+
+    CALL(make_session(&sess, single_serve_string, RESP200
+                     "Connection: close\r\n\r\n"));
+    CALL(any_request(sess, "/foo"));
+    CALL(await_server());
+    
+    ONV(strcmp(ne_get_error(sess), "200 OK"),
+       ("reason phrase mismatch: got `%s' not `200 OK'",
+        ne_get_error(sess)));
+
+    ne_session_destroy(sess);
+    return OK;    
+}
+
 static int single_get_eof(void)
 {
     return expect_response("a", single_serve_string, 
@@ -158,266 +239,1418 @@ static int single_get_chunked(void)
                           "g;lkjalskdjalksjd");
 }
 
+static int no_body_304(void)
+{
+    return expect_no_body("GET", "HTTP/1.1 304 Not Mfodified\r\n"
+                         "Content-Length: 5\r\n\r\n");
+}
+
+static int no_body_204(void)
+{
+    return expect_no_body("GET", "HTTP/1.1 204 Not Modified\r\n"
+                         "Content-Length: 5\r\n\r\n");
+}
+
+static int no_body_205(void)
+{
+    return expect_no_body("GET", "HTTP/1.1 205 Reset Content\r\n"
+                         "Content-Length: 5\r\n\r\n");
+}
+
+static int no_body_HEAD(void)
+{
+    return expect_no_body("HEAD", "HTTP/1.1 200 OK\r\n"
+                         "Content-Length: 5\r\n\r\n");
+}
+
+static int no_body_empty_clength(void)
+{
+    return expect_no_body("GET", "HTTP/1.1 200 OK\r\n"
+                         "Content-Length:\r\n\r\n");
+}
+
+static int no_body_bad_clength(void)
+{
+    return expect_no_body("GET", "HTTP/1.1 200 OK\r\n"
+                         "Content-Length: foobar\r\n\r\n");
+}
+
+static int no_headers(void)
+{
+    return expect_response("abcde", single_serve_string,
+                          "HTTP/1.1 200 OK\r\n\r\n"
+                          "abcde");
+}
+
 #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)
+static int chunks(void)
 {
     /* lots of little chunks. */
-    ON(expect_response("abcde", single_serve_string,
-                      RESP200 TE_CHUNKED
-                      "\r\n"
-                      ABCDE_CHUNKS));
-    return OK;
+    return expect_response("abcde", single_serve_string,
+                          RESP200 TE_CHUNKED
+                          "\r\n"
+                          ABCDE_CHUNKS);
+}
+
+static int te_header(void)
+{
+    return expect_response("abcde", single_serve_string,
+                          RESP200 "Transfer-Encoding: CHUNKED\r\n"
+                          "\r\n" ABCDE_CHUNKS);
+}
+
+/* test that the presence of *any* t-e header implies a chunked
+ * response. */
+static int any_te_header(void)
+{
+    return expect_response("abcde", single_serve_string, RESP200
+                           "Transfer-Encoding: punked\r\n" "\r\n"
+                           ABCDE_CHUNKS);
 }
 
-static int chunk_syntax_2(void)
+static int chunk_numeric(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;
+    return expect_response("0123456789abcdef", single_serve_string,
+                          RESP200 TE_CHUNKED
+                          "\r\n"
+                          "000000010\r\n" "0123456789abcdef\r\n"
+                          "000000000\r\n" "\r\n");
 }
 
-static int chunk_syntax_3(void)
+static int chunk_extensions(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;
+    return 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");
 }
 
-static int chunk_syntax_4(void)
+static int chunk_trailers(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 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");
+}
+
+static int chunk_oversize(void)
+{
+#define BIG (20000)
+    char *body = ne_malloc(BIG + 1);
+    static const char rnd[] = "abcdefghijklm";
+    int n;
+    ne_buffer *buf = ne_buffer_create();
+    
+    for (n = 0; n < BIG; n++) {
+       body[n] = rnd[n % (sizeof(rnd) - 1)];
+    }
+    body[n] = '\0';
+#undef BIG
+
+    ne_buffer_concat(buf, RESP200 TE_CHUNKED "\r\n" 
+                    "4E20\r\n", body, "\r\n",
+                    "0\r\n\r\n", NULL);
+
+    CALL(expect_response(body, single_serve_string, buf->data));
+    
+    ne_buffer_destroy(buf);
+    ne_free(body);
+
     return OK;
 }
 
-static int chunk_syntax_5(void)
+static int te_over_clength(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;
+    return expect_response("abcde", single_serve_string,
+                          RESP200 TE_CHUNKED
+                          "Content-Length: 300\r\n" 
+                          "\r\n"
+                          ABCDE_CHUNKS);
 }
 
-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;
+/* te_over_clength with the headers the other way round; check for
+ * ordering problems. */
+static int te_over_clength2(void)
+{   
+    return expect_response("abcde", single_serve_string,
+                          RESP200 "Content-Length: 300\r\n" 
+                          TE_CHUNKED
+                          "\r\n"
+                          ABCDE_CHUNKS);
 }
 
-static int fold_many_headers(void)
+/* obscure case which is possibly a valid request by 2616, but should
+ * be handled correctly in any case.  neon <0.22.0 tries to 
+ * eat the response body, which is probably incorrect. */
+static int no_body_chunks(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;    
+    return expect_no_body("HEAD", "HTTP/1.1 204 Not Modified\r\n"
+                         TE_CHUNKED "\r\n");
 }
 
-static void mh_header(void *ctx, const char *value)
+static int serve_twice(ne_socket *sock, void *userdata)
 {
-    int *state = ctx;
-    static const char *hdrs[] = { "jim", "jab", "jar" };
+    const char *resp = userdata;
+    
+    CALL(discard_request(sock));
+    SEND_STRING(sock, resp);
 
-    if (*state < 0 || *state > 2) {
-       /* already failed. */
-       return;
-    }
+    CALL(discard_request(sock));
+    SEND_STRING(sock, resp);
 
-    if (strcmp(value, hdrs[*state]))
-       *state = -*state;
-    else
-       (*state)++;
+    return OK;
 }
 
-/* check headers callbacks are working correctly. */
-static int multi_header(void)
+/* Test persistent connection handling: serve 'response' twice on a
+ * single TCP connection, expecting to get a response body equal to
+ * 'body' both times. */
+static int test_persist(const char *response, const char *body)
 {
-    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_session *sess = ne_session_create("http", "localhost", 7777);
+    ne_buffer *buf = ne_buffer_create();
 
-    ne_add_response_header_handler(req, "x-header", mh_header, &state);
+    ON(sess == NULL || buf == NULL);
+    ON(spawn_server(7777, serve_twice, (char *)response));
+    
+    CALL(run_request(sess, 200, construct_get, buf));
+    
+    ONV(strcmp(buf->data, body),
+       ("response #1 mismatch: [%s] not [%s]", buf->data, body));
 
-    ret = ne_request_dispatch(req);
-    if (ret) {
-       reap_server();
-       ONREQ(ret);
-       ONN("shouldn't get here.", 1);
-    }
+    /* Run it again. */
+    ne_buffer_clear(buf);
+    CALL(run_request(sess, 200, construct_get, buf));
 
     ON(await_server());
 
-    ON(state != 3);
+    ONV(strcmp(buf->data, body),
+       ("response #2 mismatch: [%s] not [%s]", buf->data, body));
 
-    ne_request_destroy(req);
     ne_session_destroy(sess);
+    ne_buffer_destroy(buf);
 
     return OK;
 }
 
-struct body {
-    char *body;
-    size_t size;
-};
+static int persist_http11(void)
+{
+    return test_persist(RESP200 "Content-Length: 5\r\n\r\n" "abcde",
+                       "abcde");
+}
 
-static int want_body(nsocket *sock, void *userdata)
+static int persist_chunked(void)
 {
-    struct body *b = userdata;
-    char *buf = ne_malloc(b->size);
+    return test_persist(RESP200 TE_CHUNKED "\r\n" ABCDE_CHUNKS,
+                       "abcde");
+}
 
-    clength = 0;
+static int persist_http10(void)
+{
+    return test_persist("HTTP/1.0 200 OK\r\n"
+                       "Connection: keep-alive\r\n"
+                       "Content-Length: 5\r\n\r\n" "abcde",
+                       "abcde");
+}
+
+/* Server function for fail_early_eof */
+static int serve_eof(ne_socket *sock, void *ud)
+{
+    const char *resp = ud;
+
+    /* dummy request/response. */
     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"));
+    CALL(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n"));
+    /* real request/response. */
+    CALL(discard_request(sock));
+    CALL(SEND_STRING(sock, resp));
 
-    ON(memcmp(buf, b->body, b->size));
+    return OK;
+}
 
+/* Utility function: 'resp' is a truncated response; such that an EOF
+ * arrives early during response processing; but NOT as a valid
+ * premature EOF due to a persistent connection timeout.  It is an
+ * error if the request is then retried, and the test fails. */
+static int fail_early_eof(const char *resp)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+
+    CALL(spawn_server_repeat(7777, serve_eof, (char *)resp, 3));
+
+    ONREQ(any_request(sess, "/foo"));
+    ONN("request retried after early EOF",
+       any_request(sess, "/foobar") == NE_OK);
+    
+    CALL(reap_server());
+    ne_session_destroy(sess);
     return OK;
 }
 
-static ssize_t provide_body(void *userdata, char *buf, size_t buflen)
+/* This failed with neon <0.22. */
+static int fail_eof_continued(void)
 {
-    static const char *pnt;
-    static size_t left;
-    struct body *b = userdata;
+    return fail_early_eof("HTTP/1.1 100 OK\r\n\r\n");
+}
 
-    if (buflen == 0) {
-       pnt = b->body;
-       left = b->size;
-    } else {
-       if (left < buflen) buflen = left;
-       memcpy(buf, pnt, buflen);
-       left -= buflen;
-    }
+static int fail_eof_headers(void)
+{
+    return fail_early_eof("HTTP/1.1 200 OK\r\nJimbob\r\n");
+}
+
+static int fail_eof_chunk(void)
+{
+    return fail_early_eof(RESP200 TE_CHUNKED "\r\n" "1\r\n" "a");
+}
+
+static int fail_eof_badclen(void)
+{
+    return fail_early_eof(RESP200 "Content-Length: 10\r\n\r\n" "abcde");
+}
+
+/* Persistent connection timeout where a FIN is sent to terminate the
+ * connection, which is caught by a 0 return from the read() when the
+ * second request reads the status-line. */
+static int ptimeout_eof(void)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+
+    CALL(spawn_server_repeat(7777, single_serve_string, 
+                            RESP200 "Content-Length: 0\r\n" "\r\n", 4));
     
-    return buflen;
+    CALL(any_2xx_request(sess, "/first"));
+    CALL(any_2xx_request(sess, "/second"));
+    
+    ONN("server died prematurely?", dead_server());
+    reap_server();
+
+    ne_session_destroy(sess);
+    return OK;
 }
 
-static int send_bodies(void)
+/* Persistent connection timeout where a FIN is sent to terminate the
+ * connection, but the request fails in the write() call which sends
+ * the body. */
+static int ptimeout_eof2(void)
 {
-    int n, m;
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
 
-#define B(x) { x, strlen(x) }
-    struct body bodies[] = { 
-       B("abcde"), 
-       { "\0\0\0\0\0\0", 6 },
-       { NULL, 50000 },
-       { NULL }
-    };
+    CALL(spawn_server_repeat(7777, single_serve_string, 
+                            RESP200 "Content-Length: 0\r\n" "\r\n", 4));
+    
+    CALL(any_2xx_request(sess, "/first"));
+    minisleep();
+    CALL(any_2xx_request_body(sess, "/second"));
+    
+    ONN("server died prematurely?", dead_server());
+    reap_server();
 
-#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;
-    }
+    ne_session_destroy(sess);
+    return OK;
+}
 
-    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])));
+/* TODO: add a ptimeout_reset too, if an RST can be reliably generated
+ * mid-connection. */
 
-           req = ne_request_create(sess, "PUT", "/");
-           ON(req == NULL);
+/* Emulates a persistent connection timeout on the server. This tests
+ * the timeout occuring after between 1 and 10 requests down the
+ * connection. */
+static int persist_timeout(void)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    ne_buffer *buf = ne_buffer_create();
+    int n;
+    struct many_serve_args args;
 
-           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]);
-           }
+    ON(sess == NULL || buf == NULL);
 
-           ONREQ(ne_request_dispatch(req));
+    args.str = RESP200 "Content-Length: 5\r\n\r\n" "abcde";
+    
+    for (args.count = 1; args.count < 10; args.count++) {
+
+       ON(spawn_server(7777, many_serve_string, &args));
+
+       for (n = 0; n < args.count; n++) {
            
-           CALL(await_server());
+           ONV(run_request(sess, 200, construct_get, buf),
+               ("%d of %d, request failed: %s", n, args.count,
+                ne_get_error(sess)));
            
-           ne_request_destroy(req);
-           ne_session_destroy(sess);
+           ONV(strcmp(buf->data, "abcde"),
+               ("%d of %d, response body mismatch", n, args.count));
+
+           /* Ready for next time. */
+           ne_buffer_clear(buf);
        }
+
+       ON(await_server());
+
     }
 
+    ne_session_destroy(sess);
+    ne_buffer_destroy(buf);
+
+    return OK;
+}   
+
+/* Test that an HTTP/1.0 server is not presumed to support persistent
+ * connections by default. */
+static int no_persist_http10(void)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+
+    CALL(spawn_server_repeat(7777, single_serve_string,
+                            "HTTP/1.0 200 OK\r\n"
+                            "Content-Length: 5\r\n\r\n"
+                            "abcde"
+                            "Hello, world - what a nice day!\r\n",
+                            4));
+
+    /* if the connection is treated as persistent, the status-line for
+     * the second request will be "Hello, world...", which will
+     * fail. */
+
+    ONREQ(any_request(sess, "/foobar"));
+    ONREQ(any_request(sess, "/foobar"));
+
+    ONN("server died prematurely?", dead_server());
+    CALL(reap_server());
+    ne_session_destroy(sess);
+    return OK;
+}
+
+static int ignore_bad_headers(void)
+{
+    return expect_response("abcde", single_serve_string,
+                          RESP200 
+                          "Stupid Header\r\n"
+                          "ReallyStupidHeader\r\n"
+                          "Content-Length: 5\r\n"
+                          "\r\n"
+                          "abcde");
+}
+
+static int fold_headers(void)
+{
+    return expect_response("abcde", single_serve_string,
+                          RESP200 "Content-Length: \r\n   5\r\n"
+                          "\r\n"
+                          "abcde");
+}
+
+static int fold_many_headers(void)
+{
+    return expect_response("abcde", single_serve_string,
+                          RESP200 "Content-Length: \r\n \r\n \r\n \r\n  5\r\n"
+                          "\r\n"
+                          "abcde");
+}
+
+#define NO_BODY "Content-Length: 0\r\n\r\n"
+
+static int empty_header(void)
+{
+    return expect_header_value("ranDom-HEader", "",
+                              single_serve_string,
+                              RESP200 "RANDom-HeADEr:\r\n"
+                              NO_BODY);
+}
+
+static int ignore_header_case(void)
+{
+    return expect_header_value("ranDom-HEader", "noddy",
+                              single_serve_string,
+                              RESP200 "RANDom-HeADEr: noddy\r\n"
+                              NO_BODY);
+}
+
+static int ignore_header_ws(void)
+{
+    return expect_header_value("ranDom-HEader", "fishy",
+                              single_serve_string,
+                              RESP200 "RANDom-HeADEr:    fishy\r\n"
+                              NO_BODY);
+}
+
+static int ignore_header_ws2(void)
+{
+    return expect_header_value("ranDom-HEader", "fishy",
+                              single_serve_string,
+                              RESP200 "RANDom-HeADEr \t :    fishy\r\n"
+                              NO_BODY);
+}
+
+static int ignore_header_ws3(void)
+{
+    return expect_header_value("ranDom-HEader", "fishy",
+                              single_serve_string,
+                              RESP200 "RANDom-HeADEr: fishy  \r\n"
+                              NO_BODY);
+}
+
+static int ignore_header_tabs(void)
+{
+    return expect_header_value("ranDom-HEader", "geezer",
+                              single_serve_string,
+                              RESP200 "RANDom-HeADEr: \t \tgeezer\r\n"
+                              NO_BODY);
+}
+
+static int trailing_header(void)
+{
+    return expect_header_value("gONe", "fishing",
+                              single_serve_string,
+                              RESP200 TE_CHUNKED
+                              "\r\n0\r\n"
+                              "Hello: world\r\n"
+                              "GONE: fishing\r\n"
+                              "\r\n");
+}
+
+static int continued_header(void)
+{
+    return expect_header_value("hello", "w o r l d", single_serve_string,
+                              RESP200 "Hello:  \n\tw\r\n\to r l\r\n\td  \r\n"
+                              NO_BODY);
+}
+
+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("http", "localhost", 7777);
+    ne_request *req;
+    int state = 0;
+
+    ON(sess == NULL);
+    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);
+
+    ONREQ(ne_request_dispatch(req));
+
+    ON(await_server());
+
+    ON(state != 3);
+
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+struct s1xx_args {
+    int count;
+    int hdrs;
+};
+
+static int serve_1xx(ne_socket *sock, void *ud)
+{
+    struct s1xx_args *args = ud;
+    CALL(discard_request(sock));
+    
+    do {
+       if (args->hdrs) {
+           SEND_STRING(sock, "HTTP/1.1 100 Continue\r\n"
+                       "Random: header\r\n"
+                       "Another: header\r\n\r\n");
+       } else {
+           SEND_STRING(sock, "HTTP/1.1 100 Continue\r\n\r\n");
+       }
+    } while (--args->count > 0);
+    
+    SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n");
+    
+    return OK;
+}
+
+#define sess def_sess
+
+static int skip_interim_1xx(void)
+{
+    struct s1xx_args args = {0, 0};
+    ON(prepare_request(serve_1xx, &args));
+    ONREQ(ne_request_dispatch(def_req));
+    return finish_request();
+}
+
+static int skip_many_1xx(void)
+{
+    struct s1xx_args args = {5, 0};
+    ON(prepare_request(serve_1xx, &args));
+    ONREQ(ne_request_dispatch(def_req));
+    return finish_request();
+}
+
+static int skip_1xx_hdrs(void)
+{
+    struct s1xx_args args = {5, 5};
+    ON(prepare_request(serve_1xx, &args));
+    ONREQ(ne_request_dispatch(def_req));
+    return finish_request();
+}
+
+#undef sess
+
+/* server for expect_100_once: eats a dummy request, then serves a
+ * 100-continue request, and fails if the request body is sent
+ * twice. */
+static int serve_100_once(ne_socket *sock, void *ud)
+{
+    struct s1xx_args args = {2, 0};
+    char ch;
+    /* dummy first request. */
+    CALL(discard_request(sock));
+    CALL(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n"));
+    /* now the real 1xx request. */
+    CALL(serve_1xx(sock, &args));
+    CALL(discard_body(sock));
+    ONN("body was served twice", ne_sock_read(sock, &ch, 1) == 1);
+    return OK;
+}
+
+/* regression test; fails with neon <0.22, where the request body was
+ * served *every* time a 1xx response was received, rather than just
+ * once. */
+static int expect_100_once(void)
+{
+    ne_session *sess;
+    ne_request *req;
+    char body[BUFSIZ];
+
+    CALL(make_session(&sess, serve_100_once, NULL));
+    ne_set_expect100(sess, 1);
+
+    /* 100-continue is only used if the server is known to claim
+     * HTTP/1.1 compliance; make a dummy request on the socket first,
+     * to trigger that logic. */
+    CALL(any_request(sess, "/foo"));
+    
+    /* now the real request. */
+    req = ne_request_create(sess, "GET", "/foo");
+    memset(body, 'A', sizeof(body));
+    ne_set_request_body_buffer(req, body, sizeof(body));
+    ONN("request failed", ne_request_dispatch(req));
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+    CALL(await_server());
+    return OK;
+}
+
+struct body {
+    char *body;
+    size_t size;
+};
+
+static int want_body(ne_socket *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 %" NE_FMT_SIZE_T " bytes...\n", b->size);
+    
+    ON(ne_sock_fullread(sock, buf, b->size));
+    
+    ON(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n"));
+
+    ON(memcmp(buf, b->body, b->size));
+    
+    ne_free(buf);
+    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)
+{
+    unsigned int n, m;
+
+    struct body bodies[] = { 
+       { "abcde", 5 }, 
+       { "\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("http", "localhost", 7777);
+           ne_request *req;
+           
+           ON(sess == NULL);
+           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);
+       }
+    }
+
+    ne_free(bodies[BIG].body);
+    return OK;
+}
+
+static int serve_infinite_headers(ne_socket *sock, void *userdata)
+{
+    CALL(discard_request(sock));
+
+    SEND_STRING(sock, RESP200);
+    
+    for (;;) {
+       SEND_STRING(sock, "x-foo: bar\r\n");
+    }
+
+    return 0;  
+}
+
+/* Utility function: run a request using the given server fn, and the
+ * request should fail. If 'error' is non-NULL, it must be a substring
+ * of the error string. */
+static int fail_request_with_error(int with_body, server_fn fn, void *ud, 
+                                   int forever, const char *error)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    ne_request *req;
+    int ret;
+
+    ON(sess == NULL);
+    
+    if (forever) {
+       ON(spawn_server_repeat(7777, fn, ud, 100));
+    } else {
+       ON(spawn_server(7777, fn, ud));
+    }
+    
+    req = ne_request_create(sess, "GET", "/");
+    ON(req == NULL);
+
+    if (with_body) {
+       static const char *body = "random stuff";
+       
+       ne_set_request_body_buffer(req, body, strlen(body));
+    }
+
+    /* request should fail. */
+    ret = ne_request_dispatch(req);
+    ONN("request succeeded", ret == NE_OK);
+
+    if (!forever) {
+       /* reap the server, don't care what it's doing. */
+       reap_server();
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "Response gave error `%s'\n", ne_get_error(sess));
+    ONV(error && strstr(ne_get_error(sess), error) == NULL,
+        ("failed with error `%s', no `%s'", ne_get_error(sess), error));
+
+    if (!forever)
+        ONV(any_request(sess, "/fail/to/connect") != NE_CONNECT,
+            ("subsequent request re-used connection?"));
+
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+   
+    return OK;    
+}
+
+/* Run a random GET request which is given 'body' as the response; the
+ * request must fail, and 'error' must be found in the error
+ * string. */
+static int invalid_response_gives_error(const char *resp, const char *error)
+{
+    return fail_request_with_error(0, single_serve_string, (void *)resp, 0, error);
+}
+
+/* Utility function: run a request using the given server fn, and the
+ * request must fail. */
+static int fail_request(int with_body, server_fn fn, void *ud, int forever)
+{
+    return fail_request_with_error(with_body, fn, ud, forever, NULL);
+}
+
+static int unbounded_headers(void)
+{
+    return fail_request(0, serve_infinite_headers, NULL, 0);
+}
+
+static int blank_response(void)
+{
+    return fail_request(0, single_serve_string, "\r\n", 0);
+}
+
+static int serve_non_http(ne_socket *sock, void *ud)
+{
+    SEND_STRING(sock, "Hello Mum.\n");
+    ne_sock_readline(sock, buffer, BUFSIZ);
+    return OK;
+}
+
+/* Test behaviour when not speaking to an HTTP server. Regression test
+ * for infinite loop. */
+static int not_http(void)
+{
+    return fail_request(0, serve_non_http, NULL, 0);
+}
+
+static int serve_infinite_folds(ne_socket *sock, void *ud)
+{
+    SEND_STRING(sock, "HTTP/1.0 200 OK\r\nFoo: bar\r\n");
+    for (;;) {
+       SEND_STRING(sock, "  hello there.\r\n");
+    }
+    return OK;
+}
+
+static int unbounded_folding(void)
+{
+    return fail_request(0, serve_infinite_folds, NULL, 0);
+}
+
+static int serve_close(ne_socket *sock, void *ud)
+{
+    /* do nothing; the socket will be closed. */
+    return 0;
+}
+
+/* Returns non-zero if port is alive. */
+static int is_alive(int port)
+{
+    ne_sock_addr *addr;
+    ne_socket *sock = ne_sock_create();
+    const ne_inet_addr *ia;
+    int connected = 0;
+
+    addr = ne_addr_resolve("localhost", 0);
+    for (ia = ne_addr_first(addr); ia && !connected; ia = ne_addr_next(addr))
+       connected = ne_sock_connect(sock, ia, 7777) == 0;
+    ne_addr_destroy(addr);
+    if (sock == NULL)
+       return 0;
+    else {
+       ne_sock_close(sock);
+       return 1;
+    }
+}
+
+/* This is a regression test for neon 0.17.0 and earlier, which goes
+ * into an infinite loop if a request with a body is sent to a server
+ * which simply closes the connection. */
+static int closed_connection(void)
+{
+    int ret;
+
+    /* This spawns a server process which will run the 'serve_close'
+     * response function 200 times, then die. This guarantees that the
+     * request eventually fails... */
+    CALL(fail_request(1, serve_close, NULL, 1));
+    /* if server died -> infinite loop was detected. */
+    ret = !is_alive(7777);
+    reap_server();
+    ONN("server aborted, infinite loop?", ret);
+    return OK;
+}
+
+static int serve_close2(ne_socket *sock, void *userdata)
+{
+    int *count = userdata;
+    *count += 1;
+    if (*count == 1)
+       return 0;
+    NE_DEBUG(NE_DBG_HTTP, "Re-entered! Buggy client.\n");
+    CALL(discard_request(sock));
+    CALL(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n"));
+    return 0;
+}
+
+/* As closed_connection(); but check that the client doesn't retry
+ * after receiving the EOF on the first request down a new
+ * connection.  */
+static int close_not_retried(void)
+{
+    int count = 0;
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    CALL(spawn_server_repeat(7777, serve_close2, &count, 3));
+    ONN("request was retried after EOF", any_request(sess, "/foo") == NE_OK);
+    reap_server();
+    ne_session_destroy(sess);
+    return OK;
+}
+
+static enum {
+    prog_error, /* error */
+    prog_transfer, /* doing a transfer */
+    prog_done /* finished. */
+} prog_state = prog_transfer;
+
+static off_t prog_last = -1, prog_total;
+
+/* callback for send_progress. */
+static void s_progress(void *userdata, off_t prog, off_t total)
+{
+    NE_DEBUG(NE_DBG_HTTP, 
+            "progress callback: %" NE_FMT_OFF_T "/%" NE_FMT_OFF_T ".\n",
+            prog, total);
+
+    switch (prog_state) {
+    case prog_error:
+    case prog_done:
+       return;
+    case prog_transfer:
+       if (total != prog_total) {
+           t_context("total unexpected: %ld not %ld", total, prog_total);
+           prog_state = prog_error;
+       }
+       else if (prog > total) {
+           t_context("first progress was invalid (%ld/%ld)", prog, total);
+           prog_state = prog_error;
+       }
+       else if (prog_last != -1 && prog_last > prog) {
+           t_context("progess went backwards: %ld to %ld", prog_last, prog);
+           prog_state = prog_error;
+       }
+       else if (prog_last == prog) {
+           t_context("no progress made! %ld to %ld", prog_last, prog);
+           prog_state = prog_error;
+       }
+       else if (prog == total) {
+           prog_state = prog_done;
+       }
+       break;
+    }
+           
+    prog_last = prog;
+}
+
+static ssize_t provide_progress(void *userdata, char *buf, size_t bufsiz)
+{
+    int *count = userdata;
+
+    if (*count >= 0 && buf != NULL) {
+       buf[0] = 'a';
+       *count -= 1;
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+static int send_progress(void)
+{
+    static int count = 200;
+
+    ON(prepare_request(single_serve_string, 
+                      RESP200 "Connection: close\r\n\r\n"));
+
+    prog_total = 200;
+
+    ne_set_progress(def_sess, s_progress, NULL);
+    ne_set_request_body_provider(def_req, count,
+                                provide_progress, &count);
+
+#define sess def_sess
+    ONREQ(ne_request_dispatch(def_req));
+#undef sess
+    
+    ON(finish_request());
+
+    CALL(prog_state == prog_error);
+
+    return OK;    
+}
+
+static int read_timeout(void)
+{
+    ne_session *sess;
+    ne_request *req;
+    time_t start, finish;
+    int ret;
+
+    CALL(make_session(&sess, sleepy_server, NULL));
+    
+    /* timeout after one second. */
+    ne_set_read_timeout(sess, 1);
+    
+    req = ne_request_create(sess, "GET", "/timeout");
+
+    time(&start);
+    ret = ne_request_dispatch(req);
+    time(&finish);
+
+    reap_server();
+
+    ONN("request succeeded, should have timed out", ret == NE_OK);
+    ONV(ret != NE_TIMEOUT,
+       ("request failed non-timeout error: %s", ne_get_error(sess)));
+    ONN("timeout ignored, or very slow machine", finish - start > 3);
+
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+
+    return OK;    
+}
+
+/* expect failure code 'code', for request to given hostname and port,
+ * without running a server. */
+static int fail_noserver(const char *hostname, unsigned int port, int code)
+{
+     ne_session *sess = ne_session_create("http", hostname, port);
+     int ret = any_request(sess, "/foo");
+     ne_session_destroy(sess);
+
+     ONV(ret == NE_OK,
+        ("request to server at %s:%u succeded?!", hostname, port));
+     ONV(ret != code, ("request failed with %d not %d", ret, code));
+
+     return OK;
+}
+
+static int fail_lookup(void)
+{
+    return fail_noserver("no.such.domain", 7777, NE_LOOKUP);
+}
+
+/* neon 0.23.0 to 0.23.3: if a nameserver lookup failed, subsequent
+ * requests on the session would crash. */
+static int fail_double_lookup(void)
+{
+     ne_session *sess = ne_session_create("http", "nohost.example.com", 80);
+     ONN("request did not give lookup failure",
+        any_request(sess, "/foo") != NE_LOOKUP);
+     ONN("second request did not give lookup failure",
+        any_request(sess, "/bar") != NE_LOOKUP);
+     ne_session_destroy(sess);
+     return OK;
+}
+
+static int fail_connect(void)
+{
+    return fail_noserver("localhost", 7777, NE_CONNECT);
+}
+
+/* Test that the origin server hostname is NOT resolved for a proxied
+ * request. */
+static int proxy_no_resolve(void)
+{
+     ne_session *sess = ne_session_create("http", "no.such.domain", 80);
+     int ret;
+     
+     ne_session_proxy(sess, "localhost", 7777);
+     CALL(spawn_server(7777, single_serve_string,
+                      RESP200 "Content-Length: 0\r\n\r\n"));
+     
+     ret = any_request(sess, "/foo");
+     ne_session_destroy(sess);
+
+     ONN("origin server name resolved when proxy used", ret == NE_LOOKUP);
+
+     CALL(await_server());
+
+     return OK;
+}
+
+/* If the chunk size is entirely invalid, the request should be
+ * aborted.  Fails with neon <0.22; invalid chunk sizes would be
+ * silently treated as 'zero'. */
+static int fail_chunksize(void)
+{
+    return fail_request(0, single_serve_string,
+                       RESP200 TE_CHUNKED "\r\n" "ZZZZZ\r\n\r\n", 0);
+}
+
+/* in neon <0.22, if an error occcurred whilst reading the response
+ * body, the connection would not be closed (though this test will
+ * succeed in neon <0.22 since it the previous test fails). */
+static int abort_respbody(void)
+{
+    ne_session *sess;
+    
+    CALL(make_session(&sess, single_serve_string,
+                     RESP200 TE_CHUNKED "\r\n"
+                     "zzz\r\n"
+                     RESP200 "Content-Length: 0\r\n\r\n"));
+    
+    /* connection must be aborted on the first request, since it
+     * contains an invalid chunk size. */
+    ONN("invalid chunk size was accepted?",
+       any_request(sess, "/foo") != NE_ERROR);
+
+    CALL(await_server());
+    
+    /* second request should fail since server has gone away. */
+    ONN("connection was not aborted", any_request(sess, "/foo") == NE_OK);
+
+    ne_session_destroy(sess);
+    return OK;
+}
+
+static int serve_abort(ne_socket *sock, void *ud)
+{
+    exit(0);
+}
+
+/* Test that after an aborted request on a peristent connection, a
+ * failure of the *subsequent* request is not treated as a persistent
+ * connection timeout and retried.  */
+static int retry_after_abort(void)
+{
+    ne_session *sess;
+    
+    /* Serve two responses down a single persistent connection, the
+     * second of which is invalid and will cause the request to be
+     * aborted. */
+    CALL(make_session(&sess, single_serve_string, 
+                     RESP200 "Content-Length: 0\r\n\r\n"
+                     RESP200 TE_CHUNKED "\r\n"
+                     "zzzzz\r\n"));
+
+    CALL(any_request(sess, "/first"));
+    ONN("second request should fail", any_request(sess, "/second") == NE_OK);
+    CALL(await_server());
+
+    /* spawn a server, abort the server immediately.  If the
+     * connection reset is interpreted as a p.conn timeout, a new
+     * connection will be attempted, which will fail with
+     * NE_CONNECT. */
+    CALL(spawn_server(7777, serve_abort, NULL));
+    ONN("third request was retried",
+       any_request(sess, "/third") == NE_CONNECT);
+    reap_server();
+
+    ne_session_destroy(sess);    
+    return OK;
+}
+
+/* Fail to parse the response status line: check the error message is
+ * sane.  Failed during 0.23-dev briefly, and possibly with 0.22.0
+ * too. */
+static int fail_statusline(void)
+{
+    ne_session *sess;
+    int ret;
+
+    CALL(make_session(&sess, single_serve_string, "Fish.\r\n"));
+    
+    ret = any_request(sess, "/fail");
+    ONV(ret != NE_ERROR, ("request failed with %d not NE_ERROR", ret));
+    
+    /* FIXME: will break for i18n. */
+    ONV(strcmp(ne_get_error(sess), "Could not parse response status line."),
+       ("session error was `%s'", ne_get_error(sess)));
+
+    ne_session_destroy(sess);
+    return OK;    
+}
+
+#define LEN (9000)
+static int fail_long_header(void)
+{
+    char resp[LEN + 500] = "HTTP/1.1 200 OK\r\n"
+       "Server: fish\r\n";
+    size_t len = strlen(resp);
+    
+    /* add a long header */
+    memset(resp + len, 'a', LEN);
+    resp[len + LEN] = '\0';
+    
+    strcat(resp, "\r\n\r\n");
+
+    return invalid_response_gives_error(resp, "Line too long");
+}
+
+static int fail_corrupt_chunks(void)
+{
+    static const struct {
+        const char *resp, *error;
+    } ts[] = {
+        /* not CRLF */
+        { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcdeFISH", 
+          "delimiter was invalid" },
+        /* short CRLF */
+        { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcde\n",
+          "not read chunk delimiter" },
+        /* CR-notLF */
+        { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcde\rZZZ",
+          "delimiter was invalid" },
+        { NULL, NULL }
+    };
+    int n;
+
+    for (n = 0; ts[n].resp; n++)
+        CALL(invalid_response_gives_error(ts[n].resp, ts[n].error));
+
+    return OK;
+}
+
+static int versions(void)
+{
+    ne_session *sess;
+
+    CALL(make_session(&sess, single_serve_string, 
+                     "HTTP/1.1 200 OK\r\n"
+                     "Content-Length: 0\r\n\r\n"
+
+                     "HTTP/1.0 200 OK\r\n"
+                     "Content-Length: 0\r\n\r\n"));
+    
+    CALL(any_request(sess, "/http11"));
+        
+    ONN("did not detect HTTP/1.1 compliance",
+       ne_version_pre_http11(sess) != 0);
+        
+    CALL(any_request(sess, "/http10"));
+
+    ONN("did not detect lack of HTTP/1.1 compliance",
+       ne_version_pre_http11(sess) == 0);
+
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+struct cr_args {
+    const char *method, *uri;
+    int result;
+};
+
+static void hk_createreq(ne_request *req, void *userdata,
+                        const char *method, const char *requri)
+{
+    struct cr_args *args = userdata;
+    
+    args->result = 1; /* presume failure */
+    
+    if (strcmp(args->method, method))
+       t_context("Hook got method %s not %s", method, args->method);
+    else if (strcmp(args->uri, requri))
+       t_context("Hook got Req-URI %s not %s", requri, args->uri);
+    else
+       args->result = 0;
+}
+
+static int hook_create_req(void)
+{
+    ne_session *sess;
+    struct cr_args args;
+
+    CALL(make_session(&sess, single_serve_string, EMPTY_RESP EMPTY_RESP));
+
+    ne_hook_create_request(sess, hk_createreq, &args);
+
+    args.method = "GET";
+    args.uri = "/foo";
+    args.result = -1;
+
+    CALL(any_request(sess, "/foo"));
+    
+    ONN("first hook never called", args.result == -1);
+    if (args.result) return FAIL;
+
+    args.uri = "http://localhost:7777/bar";
+    args.result = -1;
+    
+    /* force use of absoluteURI in request-uri */
+    ne_session_proxy(sess, "localhost", 7777);
+
+    CALL(any_request(sess, "/bar"));
+    
+    ONN("second hook never called", args.result == -1);
+    if (args.result) return FAIL;
+
+    ne_session_destroy(sess);
+
+    return OK;    
+}
+
+static int serve_check_method(ne_socket *sock, void *ud)
+{
+    char *method = ud;
+    char buf[20];
+    size_t methlen = strlen(method);
+
+    if (ne_sock_read(sock, buf, methlen) != (ssize_t)methlen)
+        return -1;
+    
+    ONN("method corrupted", memcmp(buf, method, methlen));
+    
+    return single_serve_string(sock, "HTTP/1.1 204 OK\r\n\r\n");
+}
+                             
+
+/* Test that the method string passed to ne_request_create is
+ * strdup'ed. */
+static int dup_method(void)
+{
+    char method[] = "FOO";
+    ne_session *sess;
+    ne_request *req;
+
+    CALL(make_session(&sess, serve_check_method, method));
+    
+    req = ne_request_create(sess, method, "/bar");
+    
+    strcpy(method, "ZZZ");
+
+    ONREQ(ne_request_dispatch(req));
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+    CALL(await_server());
+
     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
+ne_test tests[] = {
+    T(lookup_localhost),
+    T(single_get_clength),
+    T(single_get_eof),
+    T(single_get_chunked),
+    T(no_body_204),
+    T(no_body_205),
+    T(no_body_304),
+    T(no_body_HEAD),
+    T(no_body_empty_clength),
+    T(no_body_bad_clength),
+    T(no_headers),
+    T(chunks),
+    T(te_header),
+    T(any_te_header),
+    T(reason_phrase),
+    T(chunk_numeric),
+    T(chunk_extensions),
+    T(chunk_trailers),
+    T(chunk_oversize),
+    T(te_over_clength),
+    T(te_over_clength2),
+    T(no_body_chunks),
+    T(persist_http11),
+    T(persist_chunked),
+    T(persist_http10),
+    T(persist_timeout),
+    T(no_persist_http10),
+    T(ptimeout_eof),
+    T(ptimeout_eof2),
+    T(closed_connection),
+    T(close_not_retried),
+    T(send_progress),
+    T(ignore_bad_headers),
+    T(fold_headers),
+    T(fold_many_headers),
+    T(multi_header),
+    T(empty_header),
+    T(trailing_header),
+    T(ignore_header_case),
+    T(ignore_header_ws),
+    T(ignore_header_ws2),
+    T(ignore_header_ws3),
+    T(ignore_header_tabs),
+    T(continued_header),
+    T(skip_interim_1xx),
+    T(skip_many_1xx),
+    T(skip_1xx_hdrs),
+    T(send_bodies),
+    T(expect_100_once),
+    T(unbounded_headers),
+    T(unbounded_folding),
+    T(blank_response),
+    T(not_http),
+    T(fail_eof_continued),
+    T(fail_eof_headers),
+    T(fail_eof_chunk),
+    T(fail_eof_badclen),
+    T(fail_long_header),
+    T(fail_corrupt_chunks),
+    T(read_timeout),
+    T(fail_lookup),
+    T(fail_double_lookup),
+    T(fail_connect),
+    T(proxy_no_resolve),
+    T(fail_chunksize),
+    T(abort_respbody),
+    T(retry_after_abort),
+    T(fail_statusline),
+    T(dup_method),
+    T(versions),
+    T(hook_create_req),
+    T(NULL)
 };
index be3cd68..bd0789e 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Test program for the neon resolver interface
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
@@ -32,26 +32,28 @@ int main(int argc, char **argv)
 
     if (argc < 2) {
        printf("Usage: %s hostname\n", argv[0]);
-       return -1;
+       return 1;
     }
     
-    if (ne_sock_init())
+    if (ne_sock_init()) {
        printf("%s: Failed to initialize socket library.\n", argv[0]);
+        return 1;
+    }
 
     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;
+       ret = 2;
     } else {
-       ne_inet_addr *ia;
+       const 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));
+           printf(" <%s>", ne_iaddr_print(ia, buf, sizeof buf));
        }
        putchar('\n');
     }
     ne_addr_destroy(addr);
 
-    return 0;
+    return ret;
 }
index 2e0b7f1..0464571 100755 (executable)
@@ -1,11 +1,20 @@
 #!/bin/sh
 
+rm -f debug.log
+rm -f child.log
+
+# enable an safety-checking malloc in glibc which will abort() if
+# heap corruption is detected.
+MALLOC_CHECK_=2
+
+export MALLOC_CHECK_
+
 for f in $*; do
-    if ./$f; then
+    if ${HARNESS} ./$f ${SRCDIR}; then
        :
     else
        echo FAILURE
-       exit -1
+       [ -z "$CARRYON" ] && exit 1
     fi
 done
 
index d183ebf..6fe04bd 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Tests for session handling
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
@@ -64,7 +64,7 @@ static int match_hostport(const char *scheme, const char *hostname, int port,
 
 static int hostports(void)
 {
-    const static struct {
+    static const struct {
        const char *scheme, *hostname;
        int port;
        const char *hostport;
@@ -106,10 +106,53 @@ static int errors(void)
     return OK;    
 }
 
+#define ID1 "foo"
+#define ID2 "bar"
+
+static int privates(void)
+{
+    ne_session *sess = ne_session_create("http", "localhost", 80);
+    char *v1 = "hello", *v2 = "world";
+
+    ne_set_session_private(sess, ID1, v1);
+    ne_set_session_private(sess, ID2, v2);
+
+#define PRIV(msg, id, val) \
+ONN(msg, ne_get_session_private(sess, id) != val)
+
+    PRIV("private #1 wrong", ID1, v1);
+    PRIV("private #2 wrong", ID2, v2);
+    PRIV("unknown id wrong", "no such ID", NULL);
+
+    ne_session_destroy(sess);
+    return OK;    
+}
+
+/* test that ne_session_create doesn't really care what scheme you
+ * give it, and that ne_get_scheme() works. */
+static int get_scheme(void)
+{
+    static const char *schemes[] = {
+        "http", "https", "ftp", "ldap", "foobar", NULL
+    };
+    int n;
+
+    for (n = 0; schemes[n]; n++) {
+        ne_session *sess = ne_session_create(schemes[n], "localhost", 80);
+        ONV(strcmp(ne_get_scheme(sess), schemes[n]),
+            ("scheme was `%s' not `%s'!", ne_get_scheme(sess), schemes[n]));
+        ne_session_destroy(sess);
+    }
+    
+    return OK;
+}
+
 ne_test tests[] = {
     T(fill_uri),
     T(hostports),
     T(errors),
-    T(NULL) 
+    T(privates),
+    T(get_scheme),
+    T(NULL)
 };
 
index 9260c85..9574bdb 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    neon test suite
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
index 5d85f78..687ff1d 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Socket handling tests
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2004, 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
 
 */
 
+/* This module can be compiled with -DSOCKET_SSL enabled, to run all
+ * the tests over an SSL connection. */
+
 #include "config.h"
 
+#include <sys/types.h>
+#include <sys/socket.h> /* for AF_INET6 */
+
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#include <time.h> /* for time() */
 
 #include "ne_socket.h"
+#include "ne_utils.h"
+#include "ne_alloc.h"
 
 #include "child.h"
 #include "tests.h"
+#include "utils.h"
+
+#ifdef SOCKET_SSL
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include "ne_ssl.h"
+
 
-static struct in_addr localhost;
+SSL_CTX *server_ctx;
+ne_ssl_context *client_ctx;
+#endif
+
+static ne_sock_addr *localhost;
 static char buffer[BUFSIZ];
 
+#if defined(AF_INET6) && defined(USE_GETADDRINFO)
+#define TEST_IPV6
+#endif
+
+/* tests for doing init/finish multiple times. */
+static int multi_init(void)
+{
+    int res1 = ne_sock_init(), res2 = ne_sock_init();
+    
+    ONV(res1 != res2, ("cached init result changed from %d to %d",
+                       res1, res2));
+    
+    ne_sock_exit();
+    res1 = ne_sock_init();
+    
+    ONV(res1 != res2, ("re-init after exit gave %d not %d",
+                       res1, res2));
+    
+    res2 = ne_sock_init();
+    
+    ONV(res1 != res2, ("second time, cached init result changed from %d to %d",
+                       res1, res2));
+    
+    return OK;
+}
+
+static ne_socket *do_connect(ne_sock_addr *addr, unsigned int port)
+{
+    ne_socket *sock = ne_sock_create();
+    const ne_inet_addr *ia;
+
+    if (!sock) return NULL;
+
+    for (ia = ne_addr_first(addr); ia; ia = ne_addr_next(addr)) {
+       if (ne_sock_connect(sock, ia, port) == 0)
+            return sock;
+    }
+    
+    ne_sock_close(sock);
+    return NULL;
+}
+
+#ifdef SOCKET_SSL
+
+/* FIXME: largely cut'n'pasted from ssl.c. */
+static int init_ssl(void)
+{
+    char *server_key;
+    ne_ssl_certificate *cert;
+    /* take srcdir as argv[1]. */
+    if (test_argc > 1) {
+       server_key = ne_concat(test_argv[1], "/server.key", NULL);
+    } else {
+       server_key = "server.key";
+    }
+
+    ONN("sock_init failed.\n", ne_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.cert", 
+                                     SSL_FILETYPE_PEM));
+
+    client_ctx = ne_ssl_context_create();
+    ONN("SSL_CTX_new failed for client", client_ctx == NULL);
+    
+    cert = ne_ssl_cert_read("ca/cert.pem");
+    ONN("could not load ca/cert.pem", cert == NULL);
+
+    ne_ssl_ctx_trustcert(client_ctx, cert);
+
+    return OK;
+}
+#endif
+
 static int resolve(void)
 {
-    ONN("could not resolve `localhost'",
-       ne_name_lookup("localhost", &localhost));
+    char buf[256];
+    localhost = ne_addr_resolve("localhost", 0);
+    ONV(ne_addr_result(localhost),
+       ("could not resolve `localhost': %s", 
+        ne_addr_error(localhost, buf, sizeof buf)));
     /* and again for child.c */
-    CALL(lookup_localhost());
-    return OK;
+    return lookup_localhost();
 }
 
 static int serve_close(ne_socket *sock, void *ud)
@@ -49,11 +151,177 @@ static int serve_close(ne_socket *sock, void *ud)
     return 0;
 }
 
+#ifdef SOCKET_SSL
+struct serve_pair {
+    server_fn fn;
+    void *userdata;
+};
+
+static int wrap_serve(ne_socket *sock, void *ud)
+{
+    struct serve_pair *pair = ud;
+    int fd = ne_sock_fd(sock), ret;
+    SSL *ssl = SSL_new(server_ctx);
+    BIO *bio = BIO_new_socket(fd, BIO_NOCLOSE);
+
+    ONN("SSL_new failed", ssl == NULL);
+    SSL_set_bio(ssl, bio, bio);
+
+#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))
+
+    NE_DEBUG(NE_DBG_SOCKET, "Doing SSL accept:\n");
+    ret = SSL_accept(ssl);
+    if (ret != 1) {
+       NE_DEBUG(NE_DBG_SOCKET, "SSL_accept failed: %s\n", ERROR_SSL_STRING);
+       return 1;
+    }
+    
+    NE_DEBUG(NE_DBG_SOCKET, "SSL accept okay.\n");
+    ne_sock_switch_ssl(sock, ssl);
+    return pair->fn(sock, pair->userdata);
+}
+
+static int begin(ne_socket **sock, server_fn fn, void *ud)
+{
+    struct serve_pair pair;
+    pair.fn = fn;
+    pair.userdata = ud;
+    CALL(spawn_server(7777, wrap_serve, &pair));
+    *sock = do_connect(localhost, 7777);
+    ONN("could not connect to localhost:7777", *sock == NULL);
+    ONV(ne_sock_connect_ssl(*sock, client_ctx),
+       ("SSL negotation failed: %s", ne_sock_error(*sock)));
+    return OK;
+}
+
+#else
+/* non-SSL begin() function. */
 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);
+    *sock = do_connect(localhost, 7777);
+    ONN("could not connect to localhost:7777", *sock == NULL);
+    return OK;
+}
+#endif
+
+static int resolve_numeric(void)
+{
+    ne_sock_addr *addr = ne_addr_resolve("127.0.0.1", 0);
+    ONV(ne_addr_result(addr),
+       ("failed to resolve 127.0.0.1: %s",
+        ne_addr_error(addr, buffer, sizeof buffer)));
+    ONN("ne_addr_first returned NULL", ne_addr_first(addr) == NULL);
+    ONN("ne_iaddr_print didn't return buffer", 
+       ne_iaddr_print(ne_addr_first(addr), buffer, sizeof buffer) != buffer);
+    ONV(strcmp(buffer, "127.0.0.1"), ("ntop gave `%s' not 127.0.0.1", buffer));
+    ne_addr_destroy(addr);
+    return OK;
+}
+
+#if 0
+static int resolve_ipv6(void)
+{
+    char err[256];
+    ne_sock_addr *addr = ne_addr_resolve("[::1]", 0);
+    ONV(ne_addr_result(addr),
+       ("could not resolve `[::1]': %s",
+        ne_addr_error(addr, err, sizeof err)));
+    ne_addr_destroy(addr);
+    return OK;
+}
+#endif
+
+static const unsigned char raw_127[4] = "\x7f\0\0\01", /* 127.0.0.1 */
+raw6_nuls[16] = /* :: */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+#ifdef TEST_IPV6
+static const unsigned char 
+raw6_cafe[16] = /* feed::cafe */ "\xfe\xed\0\0\0\0\0\0\0\0\0\0\0\0\xca\xfe",
+raw6_babe[16] = /* cafe:babe:: */ "\xca\xfe\xba\xbe\0\0\0\0\0\0\0\0\0\0\0\0";
+#endif
+
+static int addr_make_v4(void)
+{
+    ne_inet_addr *ia;
+    char pr[50];
+    
+    ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
+    ONN("ne_iaddr_make returned NULL", ia == NULL);
+
+    ne_iaddr_print(ia, pr, sizeof pr);
+    ONV(strcmp(pr, "127.0.0.1"), ("address was %s not 127.0.0.1", pr));
+
+    ne_iaddr_free(ia);
+    return OK;
+}
+
+static int addr_make_v6(void)
+{
+#ifdef TEST_IPV6   
+    struct {
+       const unsigned char *addr;
+       const char *rep;
+    } as[] = {
+       { raw6_cafe, "feed::cafe" },
+       { raw6_babe, "cafe:babe::" },
+       { raw6_nuls, "::" },
+       { NULL, NULL }
+    };
+    int n;
+    
+    for (n = 0; as[n].rep != NULL; n++) {
+       ne_inet_addr *ia = ne_iaddr_make(ne_iaddr_ipv6, as[n].addr);
+       char pr[128];
+
+       ONV(ia == NULL, ("could not make address for %d", n));
+       ne_iaddr_print(ia, pr, sizeof pr);
+       ONV(strcmp(pr, as[n].rep), 
+           ("address %d was '%s' not '%s'", n, pr, as[n].rep));
+       
+       ne_iaddr_free(ia);
+    }
+
+    return OK;
+#else
+    /* should fail when lacking IPv6 support. */
+    ne_inet_addr *ia = ne_iaddr_make(ne_iaddr_ipv6, raw6_nuls);
+    ONN("ne_iaddr_make did not return NULL", ia != NULL);
+#endif
+    return OK;
+}
+
+static int addr_compare(void)
+{
+    ne_inet_addr *ia1, *ia2;
+    int ret;
+
+    ia1 = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
+    ia2 = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
+    ONN("addr_make returned NULL", !ia1 || !ia2);
+
+    ret = ne_iaddr_cmp(ia1, ia2);
+    ONV(ret != 0, ("comparison of equal IPv4 addresses was %d", ret));
+    ne_iaddr_free(ia2);
+
+    ia2 = ne_iaddr_make(ne_iaddr_ipv4, "abcd");
+    ret = ne_iaddr_cmp(ia1, ia2);
+    ONN("comparison of unequal IPv4 addresses was zero", ret == 0);
+    
+#ifdef TEST_IPV6
+    ne_iaddr_free(ia2);
+    ia2 = ne_iaddr_make(ne_iaddr_ipv6, "feed::1");
+    ret = ne_iaddr_cmp(ia1, ia2);
+    ONN("comparison of IPv4 and IPv6 addresses was zero", ret == 0);
+
+    ne_iaddr_free(ia1);
+    ia1 = ne_iaddr_make(ne_iaddr_ipv4, "feed::1");
+    ret = ne_iaddr_cmp(ia1, ia2);
+    ONN("comparison of equal IPv6 addresses was zero", ret == 0);
+
+#endif    
+
+    ne_iaddr_free(ia1);
+    ne_iaddr_free(ia2);
     return OK;
 }
 
@@ -63,8 +331,25 @@ static int just_connect(void)
     
     CALL(begin(&sock, serve_close, NULL));
     ne_sock_close(sock);
+    return await_server();
+}
+
+/* Connect to an address crafted using ne_iaddr_make rather than from
+ * the resolver. */
+static int addr_connect(void)
+{
+    ne_socket *sock = ne_sock_create();
+    ne_inet_addr *ia;
+
+    ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
+    ONN("ne_iaddr_make returned NULL", ia == NULL);
+    
+    CALL(spawn_server(7777, serve_close, NULL));
+    ONN("could not connect", ne_sock_connect(sock, ia, 7777));
+    ne_sock_close(sock);
     CALL(await_server());
 
+    ne_iaddr_free(ia);
     return OK;
 }
 
@@ -72,15 +357,34 @@ static int just_connect(void)
 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));
+    ONV(n > 0, ("read got %" NE_FMT_SSIZE_T "bytes not closure", n));
+    ONV(n < 0 && n != NE_SOCK_CLOSED,
+       ("read got error not closure: `%s'", ne_sock_error(sock)));
     return OK;
 }
 
+static int good_close(ne_socket *sock)
+{
+    NE_DEBUG(NE_DBG_SOCKET, "Socket error was %s\n", ne_sock_error(sock));
+    ONN("close failed", ne_sock_close(sock));
+    return OK;    
+}
+
+/* Finish a test, closing socket and rejoining child. If eof is non-zero,
+ * expects to read EOF from the socket before closing. */
+static int finish(ne_socket *sock, int eof)
+{
+    if (eof)
+       CALL(expect_close(sock));
+    CALL(good_close(sock));
+    return await_server();
+}
+
 /* 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));
+    ONV(n != NE_SOCK_CLOSED, ("peek gave %" NE_FMT_SSIZE_T " not closure", n));
     return OK;
 }
 
@@ -92,8 +396,7 @@ static int read_close(void)
     CALL(begin(&sock, serve_close, NULL));
     CALL(expect_close(sock));    
     ONN("close failed", ne_sock_close(sock));
-    CALL(await_server());
-    return OK;
+    return await_server();
 }
 
 /* Test that just does a connect then a close (but gets the close via
@@ -105,23 +408,7 @@ static int peek_close(void)
     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;
+    return await_server();
 }
 
 /* Don't change this string. */
@@ -131,9 +418,11 @@ static int serve_string(ne_socket *sock, void *ud)
 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((ssize_t)len != ret, 
+       ("peek got %" NE_FMT_SSIZE_T " bytes not %" NE_FMT_SIZE_T, ret, len));
     ONV(memcmp(str, buffer, len),
-       ("peek mismatch: `%.*s' not `%.*s'", len, buffer, len, str));    
+       ("peek mismatch: `%.*s' not `%.*s'", 
+        (int)len, buffer, (int)len, str));    
     return OK;
 }
 
@@ -141,69 +430,71 @@ static int peek_expect(ne_socket *sock, const char *str, size_t len)
 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((ssize_t)len != ret,
+       ("peek got %" NE_FMT_SSIZE_T " bytes not %" NE_FMT_SIZE_T, ret, len));
     ONV(memcmp(str, buffer, len),
-       ("read mismatch: `%.*s' not `%.*s'", len, buffer, len, str));    
+       ("read mismatch: `%.*s' not `%.*s'", 
+        (int)len, buffer, (int)len, str));    
     return OK;
 }
 
+/* Declare a struct string */
+#define DECL(var,str) struct string var = { str, 0 }; var.len = strlen(str)
+
 /* Test a simple read. */
 static int single_read(void)
 {
     ne_socket *sock;
-    struct string hello = { STR, strlen(STR) };
+    DECL(hello, STR);
 
-    CALL(begin(&sock, serve_string, &hello));
+    CALL(begin(&sock, serve_sstring, &hello));
     CALL(read_expect(sock, STR, strlen(STR)));
     CALL(expect_close(sock));
+    CALL(good_close(sock));
 
-    CALL(await_server());
-    return OK;
+    return await_server();
 }
 
 /* Test a simple peek. */
 static int single_peek(void)
 {
     ne_socket *sock;
-    struct string hello = { STR, strlen(STR) };
+    DECL(hello, STR);
 
-    CALL(begin(&sock, serve_string, &hello));
+    CALL(begin(&sock, serve_sstring, &hello));
     CALL(peek_expect(sock, STR, strlen(STR)));
-    ONN("close failed", ne_sock_close(sock));
-
-    CALL(await_server());
-    return OK;
+    return finish(sock, 0);
 }
 
 /* Test lots of 1-byte reads. */
 static int small_reads(void)
 {
     ne_socket *sock;
-    struct string hello = { STR, strlen(STR) };
     char *pnt;
+    DECL(hello, STR);
 
-    CALL(begin(&sock, serve_string, &hello));
+    CALL(begin(&sock, serve_sstring, &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;
+    return finish(sock, 1);
 }
 
+/* peek or read, expecting to get given string. */
+#define READ(str) CALL(read_expect(sock, str, strlen(str)))
+#define PEEK(str) CALL(peek_expect(sock, str, strlen(str)))
+
 /* 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)))
+    DECL(hello, STR);
 
-    CALL(begin(&sock, serve_string, &hello));
+    CALL(begin(&sock, serve_sstring, &hello));
 
     PEEK("Hello");
     PEEK("Hell");
@@ -216,36 +507,362 @@ static int read_and_peek(void)
     PEEK("d."); PEEK("d");
     READ("d.");
 
-    CALL(expect_close(sock));
-
-    ONN("close failed", ne_sock_close(sock));
-    CALL(await_server());
-    return OK;
+    return finish(sock, 1);
 }
 
 /* Read more bytes than were written. */
 static int larger_read(void)
 {
     ne_socket *sock;
-    struct string hello = { STR, strlen(STR) };
     ssize_t nb;
+    DECL(hello, STR);
 
-    CALL(begin(&sock, serve_string, &hello));
+    CALL(begin(&sock, serve_sstring, &hello));
     
     nb = ne_sock_read(sock, buffer, hello.len + 10);
-    ONV(nb != (ssize_t)hello.len, ("read gave too many bytes (%d)", nb));
+    ONV(nb != (ssize_t)hello.len, 
+       ("read gave too many bytes (%" NE_FMT_SSIZE_T ")", nb));
     ONN("read gave wrong data", memcmp(buffer, hello.data, hello.len));
     
-    CALL(expect_close(sock));
+    return finish(sock, 1);
+}
+
+static int line_expect(ne_socket *sock, const char *line)
+{
+    ssize_t ret = ne_sock_readline(sock, buffer, BUFSIZ);
+    size_t len = strlen(line);
+    ONV(ret == NE_SOCK_CLOSED, ("socket closed, expecting `%s'", line));
+    ONV(ret < 0, ("socket error `%s', expecting `%s'", 
+                 ne_sock_error(sock), line));
+    ONV((size_t)ret != len, 
+       ("readline got %" NE_FMT_SSIZE_T ", expecting `%s'", ret, line));
+    ONV(strcmp(line, buffer),
+       ("readline mismatch: `%s' not `%s'", buffer, line));
+    return OK;
+}
+
+#define LINE(x) CALL(line_expect(sock, x))
+
+#define STR2 "Goodbye, cruel world."
+
+static int line_simple(void)
+{
+    ne_socket *sock;
+    DECL(oneline, STR "\n" STR2 "\n");
+
+    CALL(begin(&sock, serve_sstring, &oneline));
+    LINE(STR "\n");
+    LINE(STR2 "\n");
+    
+    return finish(sock, 1);
+}
+
+static int line_closure(void)
+{
+    ne_socket *sock;
+    ssize_t ret;
+    DECL(oneline, STR "\n" "foobar");
+    
+    CALL(begin(&sock, serve_sstring, &oneline));
+    LINE(STR "\n");
+    
+    ret = ne_sock_readline(sock, buffer, BUFSIZ);
+    ONV(ret != NE_SOCK_CLOSED, 
+       ("readline got %" NE_FMT_SSIZE_T " not EOF", ret));
+    
+    return finish(sock, 0);
+}   
+
+/* check that empty lines are handled correctly. */
+static int line_empty(void)
+{
+    ne_socket *sock;
+    DECL(oneline, "\n\na\n\n");
+
+    CALL(begin(&sock, serve_sstring, &oneline));
+    LINE("\n"); LINE("\n");
+    LINE("a\n"); LINE("\n");
+    
+    return finish(sock, 1);
+}
+
+static int line_toolong(void)
+{
+    ne_socket *sock;
+    ssize_t ret;
+    DECL(oneline, "AAAAAA\n");
+
+    CALL(begin(&sock, serve_sstring, &oneline));
+    ret = ne_sock_readline(sock, buffer, 5);
+    ONV(ret != NE_SOCK_ERROR, 
+       ("readline should fail on long line: %" NE_FMT_SSIZE_T, ret));
+    
+    return finish(sock, 0);
+}
 
+/* readline()s mingled with other operations: buffering tests. */
+static int line_mingle(void)
+{
+    ne_socket *sock;
+    DECL(oneline, "alpha\nbeta\ndelta\ngamma\n");
+
+    CALL(begin(&sock, serve_sstring, &oneline));
+    
+    READ("a"); LINE("lpha\n");
+    READ("beta"); LINE("\n");
+    PEEK("d"); PEEK("delt");
+    LINE("delta\n");
+    READ("gam"); LINE("ma\n");
+    
+    return finish(sock, 1);
+}
+
+/* readline which needs multiple read() calls. */
+static int line_chunked(void)
+{
+    ne_socket *sock;
+    DECL(oneline, "this is a line\n");
+
+    CALL(begin(&sock, serve_sstring_slowly, &oneline));
+    
+    LINE("this is a line\n");
+    
+    return finish(sock, 1);
+}
+
+static time_t to_start, to_finish;
+
+static int to_begin(ne_socket **sock)
+{
+    CALL(begin(sock, sleepy_server, NULL));
+    ne_sock_read_timeout(*sock, 1);
+    to_start = time(NULL);
+    return OK;
+}
+
+static int to_end(ne_socket *sock)
+{
+    to_finish = time(NULL);
+    reap_server(); /* hopefully it's hung. */
+    ONN("timeout ignored, or very slow machine", to_finish - to_start > 3);
     ONN("close failed", ne_sock_close(sock));
-    CALL(await_server());
     return OK;
 }
+        
+#define TO_BEGIN ne_socket *sock; CALL(to_begin(&sock))
+#define TO_OP(x) do { int to_ret = (x); \
+ONV(to_ret != NE_SOCK_TIMEOUT, ("operation did not timeout: %d", to_ret)); \
+} while (0)
+#define TO_FINISH return to_end(sock)
+
+static int peek_timeout(void)
+{
+    TO_BEGIN;
+    TO_OP(ne_sock_peek(sock, buffer, 1));
+    TO_FINISH;
+}
+
+static int read_timeout(void)
+{
+    TO_BEGIN;
+    TO_OP(ne_sock_read(sock, buffer, 1));
+    TO_FINISH;
+}
+
+static int readline_timeout(void)
+{
+    TO_BEGIN;
+    TO_OP(ne_sock_readline(sock, buffer, 1));
+    TO_FINISH;
+}
+
+static int fullread_timeout(void)
+{
+    TO_BEGIN;
+    TO_OP(ne_sock_fullread(sock, buffer, 1));
+    TO_FINISH;
+}   
+
+static int serve_expect(ne_socket *sock, void *ud)
+{
+    struct string *str = ud;
+    ssize_t ret;
+
+    while (str->len && 
+          (ret = ne_sock_read(sock, buffer, sizeof(buffer))) > 0) {
+       NE_DEBUG(NE_DBG_SOCKET, "Got %" NE_FMT_SSIZE_T " bytes.\n", ret);
+       ONV(memcmp(str->data, buffer, ret),
+           ("unexpected data: [%.*s] not [%.*s]", 
+            (int)ret, buffer, (int)ret, str->data));
+       str->data += ret;
+       str->len -= ret;
+       NE_DEBUG(NE_DBG_SOCKET, "%" NE_FMT_SIZE_T " bytes left.\n", str->len);
+    }
+
+    NE_DEBUG(NE_DBG_SOCKET, "All data read.\n");
+    return OK;
+}
+
+static int full_write(ne_socket *sock, const char *data, size_t len)
+{
+    int ret = ne_sock_fullwrite(sock, data, len);
+    ONV(ret, ("write failed (%d): %s", ret, ne_sock_error(sock)));
+    return OK;
+}
+
+#define WRITEL(str) CALL(full_write(sock, str, strlen(str))); \
+minisleep()
+
+static int small_writes(void)
+{
+    ne_socket *sock;
+    DECL(str, "This\nIs\nSome\nText.\n");
+    
+    CALL(begin(&sock, serve_expect, &str));
+    
+    WRITEL("This\n"); WRITEL("Is\n"); WRITEL("Some\n"); WRITEL("Text.\n");
+    
+    return finish(sock, 1);
+}
+
+static int large_writes(void)
+{
+#define LARGE_SIZE (123456)
+    struct string str;
+    ne_socket *sock;
+    ssize_t n;
+
+    str.data = ne_malloc(LARGE_SIZE);
+    str.len = LARGE_SIZE;
+
+    for (n = 0; n < LARGE_SIZE; n++)
+       str.data[n] = 41 + n % 130;
+    
+    CALL(begin(&sock, serve_expect, &str));
+    CALL(full_write(sock, str.data, str.len));
+    
+    ne_free(str.data);
+    return finish(sock, 1);    
+}
+
+/* echoes back lines. */
+static int echo_server(ne_socket *sock, void *ud)
+{
+    ssize_t ret;
+
+    while ((ret = ne_sock_readline(sock, buffer, sizeof(buffer))) > 0) {
+       NE_DEBUG(NE_DBG_SOCKET, "Line: %s", buffer);
+       ONN("write failed", ne_sock_fullwrite(sock, buffer, ret));
+       NE_DEBUG(NE_DBG_SOCKET, "Wrote line.\n");
+    }
+
+    ONN("readline failed", ret != NE_SOCK_CLOSED);
+    return 0;
+}
+
+static int echo_expect(ne_socket *sock, const char *line)
+{
+    CALL(full_write(sock, line, strlen(line)));
+    return line_expect(sock, line);
+}
+
+#define ECHO(line) CALL(echo_expect(sock, line))
+
+static int echo_lines(void)
+{
+    ne_socket *sock;
+    CALL(begin(&sock, echo_server, NULL));
+    ECHO("hello,\n");
+    ECHO("\n");
+    ECHO("world\n");
+    return finish(sock, 0);
+}
+
+#ifdef SOCKET_SSL
+/* harder to simulate closure cases for an SSL connection, since it
+ * may be doing multiple read()s or write()s per ne_sock_* call. */
+static int ssl_closure(void)
+{
+    ne_socket *sock;
+    ssize_t ret;
+    CALL(begin(&sock, serve_close, NULL));
+    CALL(full_write(sock, "a", 1));
+    CALL(await_server());
+    do {
+        ret = ne_sock_fullwrite(sock, "a", 1);
+    } while (ret == 0);
+    ONV(ret != NE_SOCK_RESET && ret != NE_SOCK_CLOSED, 
+       ("write got %" NE_FMT_SSIZE_T " not reset or closure", ret));
+    return good_close(sock);
+}
+
+static int serve_truncate(ne_socket *sock, void *userdata)
+{
+    exit(0);
+}
+
+/* when an EOF is received without a clean shutdown (close_notify
+   message). */
+static int ssl_truncate(void)
+{
+    ne_socket *sock; int ret;
+    
+    CALL(begin(&sock, serve_truncate, NULL));
+    ret = ne_sock_read(sock, buffer, 1);
+    ONV(ret != NE_SOCK_TRUNC,
+       ("socket got error %d not truncation: `%s'", ret,
+        ne_sock_error(sock)));
+    return finish(sock, 0);
+}
+
+#else
+/* thanks to W Richard Stevens for the precise repro case for getting
+ * an RST on demand. */
+static int write_reset(void)
+{
+    ne_socket *sock;
+    int ret;
+    CALL(begin(&sock, serve_close, NULL));
+    CALL(full_write(sock, "a", 1));
+    CALL(await_server());
+    ret = ne_sock_fullwrite(sock, "a", 1);
+    if (ret == 0) {
+        ne_sock_close(sock);
+        return SKIP;
+    }
+    ONV(ret != NE_SOCK_RESET, ("write got %d not reset", ret));
+    return good_close(sock);
+}
+
+static int read_reset(void)
+{
+    ne_socket *sock;
+    ssize_t ret;
+    CALL(begin(&sock, serve_close, NULL));
+    CALL(full_write(sock, "a", 1));
+    CALL(await_server());
+    ret = ne_sock_read(sock, buffer, 1);
+    if (ret == NE_SOCK_CLOSED) {
+        ne_sock_close(sock);
+        return SKIP;
+    }
+    ONV(ret != NE_SOCK_RESET, ("read got %" NE_FMT_SSIZE_T " not reset", ret));
+    return good_close(sock);
+}    
+#endif
 
 ne_test tests[] = {
-    T(resolve),
+    T(multi_init),
+    T_LEAKY(resolve),
+    T(resolve_numeric),
+#ifdef SOCKET_SSL
+    T_LEAKY(init_ssl),
+#endif
+    T(addr_make_v4),
+    T(addr_make_v6),
+    T(addr_compare),
     T(just_connect),
+    T(addr_connect),
     T(read_close),
     T(peek_close),
     T(single_read),
@@ -253,5 +870,25 @@ ne_test tests[] = {
     T(small_reads),
     T(read_and_peek),
     T(larger_read),
+    T(line_simple),
+    T(line_closure),
+    T(line_empty),
+    T(line_toolong),
+    T(line_mingle),
+    T(line_chunked),
+    T(small_writes),
+    T(large_writes),
+    T(echo_lines),
+#ifdef SOCKET_SSL
+    T(ssl_closure),
+    T(ssl_truncate),
+#else
+    T(write_reset),
+    T(read_reset),
+#endif
+    T(read_timeout),
+    T(peek_timeout),
+    T(readline_timeout),
+    T(fullread_timeout),
     T(NULL)
 };
index 7d1b222..dba1d4f 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    neon test suite
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2004, 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
@@ -22,6 +22,8 @@
 
 #include <sys/types.h>
 
+#include <sys/stat.h>
+
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -31,6 +33,7 @@
 
 #include "ne_request.h"
 #include "ne_socket.h"
+#include "ne_auth.h"
 
 #include "tests.h"
 #include "child.h"
 
 #define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))
 
-SSL_CTX *server_ctx;
+#define SERVER_CERT "server.cert"
+#define CA_CERT "ca/cert.pem"
+
+#define SERVER_DNAME "Neon QA Dept, Neon Hackers Ltd, " \
+                     "Cambridge, Cambridgeshire, GB"
+#define CACERT_DNAME "Random Dept, Neosign, Oakland, California, US"
+
+static char *srcdir = ".";
+static char *server_key = NULL; 
+
+static ne_ssl_certificate *def_ca_cert = NULL, *def_server_cert;
+static ne_ssl_client_cert *def_cli_cert;
+
+static int check_dname(const ne_ssl_dname *dn, const char *expected,
+                       const char *which);
 
 static int s_strwrite(SSL *s, const char *buf)
 {
@@ -59,73 +76,1475 @@ static int s_strwrite(SSL *s, const char *buf)
     return OK;
 }
 
-static int serve_ssl(nsocket *sock, void *ud)
+/* Arguments for running the SSL server */
+struct ssl_server_args {
+    char *cert; /* the server cert to present. */
+    const char *response; /* the response to send. */
+    int unclean; /* use an unclean shutdown if non-NULL */
+    int numreqs; /* number of request/responses to handle over the SSL connection. */
+
+    /* client cert handling: */
+    int require_cc; /* require a client cert if non-NULL */
+    const char *ca_list; /* file of CA certs to verify client cert against */
+    const char *send_ca; /* file of CA certs to send in client cert request */
+    
+    /* session caching: */
+    int cache; /* use the session cache if non-zero */
+    SSL_SESSION *session; /* use to store copy of cached session. */
+    int count; /* internal use. */
+
+    int use_ssl2; /* force use of SSLv2 only */
+};
+
+/* default response string if args->response is NULL */
+#define DEF_RESP "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"
+
+/* An SSL server inna bun. */
+static int ssl_server(ne_socket *sock, void *userdata)
 {
-    int fd = sock_get_fd(sock);
+    struct ssl_server_args *args = userdata;
+    int fd = ne_sock_fd(sock), ret;
     /* 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];
+    SSL *ssl;
+    static SSL_CTX *ctx = NULL;
+
+    if (ctx == NULL) {
+        ctx = SSL_CTX_new(args->use_ssl2 ? SSLv2_server_method()
+                          : SSLv23_server_method());
+    }
+
+    ONV(ctx == NULL,
+       ("could not create SSL_CTX: %s", ERROR_SSL_STRING));
+    ONV(!SSL_CTX_use_PrivateKey_file(ctx, server_key, SSL_FILETYPE_PEM),
+         ("failed to load private key: %s", ERROR_SSL_STRING));
+
+    NE_DEBUG(NE_DBG_HTTP, "using server cert %s\n", args->cert);
+    ONN("failed to load certificate",
+       !SSL_CTX_use_certificate_file(ctx, args->cert, SSL_FILETYPE_PEM));
+
+    if (args->require_cc) {
+        /* require a client cert. */
+        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | 
+                           SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+        if (args->send_ca) {
+            /* the list of issuer DNs of these CAs is included in the
+            * certificate request message sent to the client */
+            SSL_CTX_set_client_CA_list(ctx, 
+                                       SSL_load_client_CA_file(args->send_ca));
+        }
 
+        /* set default ca_list */
+        if (!args->ca_list) args->ca_list = CA_CERT;
+    }
+    
+    if (args->ca_list) {
+        /* load the CA used to verify the client cert; also sent in the
+         * certificate exchange message. */
+        ONN("failed to load CA cert",
+            SSL_CTX_load_verify_locations(ctx, args->ca_list, NULL) != 1);
+    }
+
+    if (args->cache && args->count == 0) {
+       /* enable OpenSSL's internal session cache, enabling the
+        * negotiation to re-use a session if both sides support it. */
+       SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+    }
+
+    ssl = SSL_new(ctx);
     ONN("SSL_new failed", ssl == NULL);
 
+    args->count++;
+
     SSL_set_bio(ssl, bio, bio);
 
     ONV(SSL_accept(ssl) != 1,
        ("SSL_accept failed: %s", ERROR_SSL_STRING));
 
-    SSL_read(ssl, buf, BUFSIZ);
+    /* loop handling requests: */
+    do {
+        const char *response = args->response ? args->response : DEF_RESP;
+
+        ret = SSL_read(ssl, buf, BUFSIZ - 1);
+        if (ret == 0)
+            return 0; /* connection closed by parent; give up. */
+        ONV(ret < 0, ("SSL_read failed (%d): %s", ret, ERROR_SSL_STRING));
+        
+        buf[ret] = '\0';
+        
+        NE_DEBUG(NE_DBG_HTTP, "Request over SSL was: [%s]\n", buf);
+        
+        if (strstr(buf, "Proxy-Authorization:") != NULL) {
+            NE_DEBUG(NE_DBG_HTTP, "Got Proxy-Auth header over SSL!\n");
+            response = "HTTP/1.1 500 Client Leaks Credentials\r\n"
+                "Content-Length: 0\r\n" "\r\n";
+        }
+        
+        CALL(s_strwrite(ssl, response));
+        
+    } while (--args->numreqs > 0);
+
+    /* copy out the session if requested. */
+    if (args->session) {
+        SSL_SESSION *sess = SSL_get1_session(ssl);
+
+#ifdef NE_DEBUGGING
+        /* dump session to child.log for debugging. */
+        SSL_SESSION_print_fp(ne_debug_stream, sess);
+#endif
 
-    CALL(s_strwrite(ssl, "HTTP/1.0 200 OK\r\n"
-                   "Content-Length: 0\r\n"
-                   "Connection: close\r\n\r\n"));
+        if (args->count == 0) {
+            /* save the session */
+            args->session = sess;
+        } else {
+            /* could just to do this with SSL_CTX_sess_hits really,
+             * but this is a more thorough test. */
+            ONN("cached SSL session not used",
+                SSL_SESSION_cmp(args->session, sess));
+            SSL_SESSION_free(args->session);
+            SSL_SESSION_free(sess);
+        }
+    }  
+    
+    if (!args->unclean) {
+       /* 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);
+    }
 
-    /* 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 0;
+}
+
+/* serve_ssl wrapper which ignores server failure and always succeeds */
+static int fail_serve(ne_socket *sock, void *ud)
+{
+    struct ssl_server_args args = {0};
+    args.cert = ud;
+    ssl_server(sock, &args);
+    return OK;
+}
+
+#define DEFSESS  (ne_session_create("https", "localhost", 7777))
+
+/* Run a request in the given session. */
+static int any_ssl_request(ne_session *sess, server_fn fn, void *server_ud,
+                          char *ca_cert,
+                          ne_ssl_verify_fn verify_fn, void *verify_ud)
+{
+    int ret;
+    
+    if (ca_cert) {
+        ne_ssl_certificate *ca = ne_ssl_cert_read(ca_cert);
+        ONV(ca == NULL, ("could not load CA cert `%s'", ca_cert));
+        ne_ssl_trust_cert(sess, ca);
+        ne_ssl_cert_free(ca);
+    }
+
+    CALL(spawn_server(7777, fn, server_ud));
+
+    if (verify_fn)
+       ne_ssl_set_verify(sess, verify_fn, verify_ud);
+
+    ret = any_request(sess, "/foo");
+
+    CALL(await_server());
+    
+    ONREQ(ret);
 
     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));
+    /* take srcdir as argv[1]. */
+    if (test_argc > 1) {
+       srcdir = test_argv[1];
+       server_key = ne_concat(srcdir, "/server.key", NULL);
+    } else {
+       server_key = "server.key";
+    }
+    
+    if (ne_sock_init()) {
+       t_context("could not initialize socket/SSL library.");
+       return FAILHARD;
+    }
+
+    def_ca_cert = ne_ssl_cert_read(CA_CERT);
+    if (def_ca_cert == NULL) {
+        t_context("couldn't load CA cert %s", CA_CERT);
+        return FAILHARD;
+    }
+
+    def_server_cert = ne_ssl_cert_read(SERVER_CERT);
+    if (def_server_cert == NULL) {
+        t_context("couldn't load server cert %s", SERVER_CERT);
+        return FAILHARD;
+    }
+    
+    /* tests for the encrypted client cert, client.p12 */
+    def_cli_cert = ne_ssl_clicert_read("client.p12");
+    ONN("could not load client.p12", def_cli_cert == NULL);
+
+    ONN("client.p12 is not encrypted!?", 
+        !ne_ssl_clicert_encrypted(def_cli_cert));
+    
+    ONN("failed to decrypt client.p12",
+        ne_ssl_clicert_decrypt(def_cli_cert, "foobar"));
+
+    return OK;
+}
+
+/* just check the result codes of loading server certs. */
+static int load_server_certs(void)
+{
+    ne_ssl_certificate *cert;
+
+    cert = ne_ssl_cert_read("Makefile");
+    ONN("invalid CA cert file loaded successfully", cert != NULL);
+
+    cert = ne_ssl_cert_read("nonesuch.pem");
+    ONN("non-existent 'nonesuch.pem' loaded successfully", cert != NULL);
+
+    cert = ne_ssl_cert_read("ssigned.pem");
+    ONN("could not load ssigned.pem", cert == NULL);
+    ne_ssl_cert_free(cert);
+
+    return OK;
+}
+
+static int trust_default_ca(void)
+{
+    ne_session *sess = DEFSESS;
+    ne_ssl_trust_default_ca(sess);
+    ne_session_destroy(sess);
+    return OK;
+}
+
+#define CC_NAME "Just A Neon Client Cert"
+
+/* Tests for loading client certificates */
+static int load_client_cert(void)
+{
+    ne_ssl_client_cert *cc;
+    const ne_ssl_certificate *cert;
+    const char *name;
+
+    cc = ne_ssl_clicert_read("client.p12");
+    ONN("could not load client.p12", cc == NULL);
+    ONN("client.p12 not encrypted!?", !ne_ssl_clicert_encrypted(cc));
+    name = ne_ssl_clicert_name(cc);
+    ONN("no friendly name given", name == NULL);
+    ONV(strcmp(name, CC_NAME), ("friendly name was %s not %s", name, CC_NAME));
+    ONN("failed to decrypt", ne_ssl_clicert_decrypt(cc, "foobar"));
+    ne_ssl_clicert_free(cc);
+
+    cc = ne_ssl_clicert_read("client.p12");
+    ONN("decrypted client.p12 with incorrect password!?",
+        ne_ssl_clicert_decrypt(cc, "barfoo") == 0);
+    ne_ssl_clicert_free(cc);
+
+    /* tests for the unencrypted client cert, client2.p12 */
+    cc = ne_ssl_clicert_read("unclient.p12");
+    ONN("could not load unencrypted cert unclient.p12", cc == NULL);
+    ONN("unencrypted cert marked encrypted?", ne_ssl_clicert_encrypted(cc));
+    cert = ne_ssl_clicert_owner(cc);
+    ONN("client cert had no certificate", cert == NULL);
+    CALL(check_dname(ne_ssl_cert_subject(cert),
+                     "Neon Client Cert, Neon Hackers Ltd, "
+                     "Cambridge, Cambridgeshire, GB",
+                     "client cert subject"));
+    CALL(check_dname(ne_ssl_cert_issuer(cert), CACERT_DNAME, 
+                     "client cert issuer"));
+    ne_ssl_clicert_free(cc);
+
+    /* test for ccert without a friendly name, noclient.p12 */
+    cc = ne_ssl_clicert_read("noclient.p12");
+    ONN("could not load noclient.p12", cc == NULL);
+    name = ne_ssl_clicert_name(cc);
+    ONV(name != NULL, ("noclient.p12 had friendly name `%s'", name));
+    ne_ssl_clicert_free(cc);
+
+    /* tests for loading bogus files. */
+    cc = ne_ssl_clicert_read("Makefile");
+    ONN("loaded Makefile as client cert!?", cc != NULL);
+
+    /* test for loading nonexistent file. */
+    cc = ne_ssl_clicert_read("nosuch.pem");
+    ONN("loaded nonexistent file as client cert!?", cc != NULL);
+
+    return OK;
+}
+
+/* Test that 'cert', which is signed by CA_CERT, is accepted
+ * unconditionaly. */
+static int accept_signed_cert_for_hostname(char *cert, const char *hostname)
+{
+    ne_session *sess = ne_session_create("https", hostname, 7777);
+    struct ssl_server_args args = {cert, 0};
+    /* no verify callback needed. */
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+    ne_session_destroy(sess);
     return OK;
 }
 
+
+static int accept_signed_cert(char *cert)
+{
+    return accept_signed_cert_for_hostname(cert, "localhost");
+}
+
 static int simple(void)
 {
-    ne_session *sess;
+    return accept_signed_cert(SERVER_CERT);
+}
+
+/* Test for SSL operation when server uses SSLv2 */
+static int simple_sslv2(void)
+{
+    ne_session *sess = ne_session_create("https", "localhost", 7777);
+    struct ssl_server_args args = {SERVER_CERT, 0};
+    args.use_ssl2 = 1;
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+    ne_session_destroy(sess);
+    return OK;
+}
+
+/* Serves using HTTP/1.0 get-till-EOF semantics. */
+static int serve_eof(ne_socket *sock, void *ud)
+{
+    struct ssl_server_args args = {0};
+
+    args.cert = ud;
+    args.response = "HTTP/1.0 200 OK\r\n"
+        "Connection: close\r\n"
+        "\r\n"
+        "This is a response body, like it or not.";
+
+    return ssl_server(sock, &args);
+}
+
+/* Test read-til-EOF behaviour with SSL. */
+static int simple_eof(void)
+{
+    ne_session *sess = DEFSESS;
+
+    CALL(any_ssl_request(sess, serve_eof, SERVER_CERT, CA_CERT, NULL, NULL));
+    ne_session_destroy(sess);
+    return OK;
+}
+
+static int empty_truncated_eof(void)
+{
+    ne_session *sess = DEFSESS;
+    struct ssl_server_args args = {0};
+
+    args.cert = SERVER_CERT;
+    args.response = "HTTP/1.0 200 OK\r\n" "\r\n";
+    
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+
+    ne_session_destroy(sess);
+    return OK;
+}
+
+/* Server function which just sends a string then EOF. */
+static int just_serve_string(ne_socket *sock, void *userdata)
+{
+    const char *str = userdata;
+    server_send(sock, str, strlen(str));
+    return 0;
+}
+
+/* test for the SSL negotiation failing. */
+static int fail_not_ssl(void)
+{
+    ne_session *sess = DEFSESS;
     int ret;
+    
+    CALL(spawn_server(7777, just_serve_string, "Hello, world.\n"));
+    ret = any_request(sess, "/bar");
+    CALL(await_server());
 
-    CALL(make_session(&sess, serve_ssl, NULL));
+    ONN("request did not fail", ret != NE_ERROR);
 
-    ne_set_secure(sess, 1);
+    ne_session_destroy(sess);
+    return OK;
+}
 
-    ret = any_request(sess, "/foo");
+static int wildcard_ok = 0;    
+
+static int wildcard_init(void)
+{
+    struct stat stbuf;
+    
+    t_context("wildcard.cert not found:\n"
+             "This test requires a Linux-like hostname command, see makekeys.sh");
+    PRECOND(stat("wildcard.cert", &stbuf) == 0);
+
+    PRECOND(lookup_hostname() == OK);
 
+    wildcard_ok = 1;
+    return OK;
+}
+
+static int wildcard_match(void)
+{
+    ne_session *sess;
+    struct ssl_server_args args = {"wildcard.cert", 0};
+
+    PRECOND(wildcard_ok);
+    
+    sess = ne_session_create("https", local_hostname, 7777);
+
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+    ne_session_destroy(sess);
+    
+    return OK;
+}
+
+/* Check that hostname comparisons are not cases-sensitive. */
+static int caseless_match(void)
+{
+    return accept_signed_cert("caseless.cert");
+}
+
+/* Test that the subjectAltName extension has precedence over the
+ * commonName attribute */
+static int subject_altname(void)
+{
+    return accept_signed_cert("altname1.cert");
+}
+
+/* tests for multiple altNames. */
+static int two_subject_altname(void)
+{
+    return accept_signed_cert("altname2.cert");
+}
+
+static int two_subject_altname2(void)
+{
+    return accept_signed_cert("altname3.cert");
+}
+
+/* Test that a subject altname with *only* an eMail entry is
+ * ignored, and the commonName is used instead. */
+static int notdns_altname(void)
+{
+    return accept_signed_cert("altname4.cert");
+}
+
+static int ipaddr_altname(void)
+{
+    return accept_signed_cert_for_hostname("altname5.cert", "127.0.0.1");
+}
+
+/* test that the *most specific* commonName attribute is used. */
+static int multi_commonName(void)
+{
+    return accept_signed_cert("twocn.cert");
+}
+
+/* regression test for neon <= 0.23.4 where if commonName was the first
+ * RDN in the subject DN, it was ignored. */
+static int commonName_first(void)
+{
+    return accept_signed_cert("cnfirst.cert");
+}
+
+static int check_dname(const ne_ssl_dname *dn, const char *expected,
+                       const char *which)
+{
+    char *dname;
+
+    ONV(dn == NULL, ("certificate %s dname was NULL", which));
+    
+    dname = ne_ssl_readable_dname(dn);
+
+    NE_DEBUG(NE_DBG_SSL, "Got dname `%s', expecting `%s'\n", dname, expected);
+
+    ONV(strcmp(dname, expected), 
+        ("certificate %s dname was `%s' not `%s'", which, dname, expected));
+
+    ne_free(dname);
+
+    return 0;
+}
+
+/* Check that the readable subject issuer dnames of 'cert' match
+ * 'subject' and 'issuer' (if non-NULL). */
+static int check_cert_dnames(const ne_ssl_certificate *cert,
+                             const char *subject, const char *issuer)
+{
+    ONN("no server certificate presented", cert == NULL);
+    CALL(check_dname(ne_ssl_cert_subject(cert), subject, "subject"));
+    return issuer ? check_dname(ne_ssl_cert_issuer(cert), issuer, "issuer") : OK;
+}
+
+/* Verify callback which checks that the certificate presented has the
+ * predetermined subject and issuer DN (as per makekeys.sh). */
+static int check_cert(void *userdata, int fs, const ne_ssl_certificate *cert)
+{
+    int *ret = userdata;
+
+    if (check_cert_dnames(cert, SERVER_DNAME, CACERT_DNAME) == FAIL)
+        *ret = -1;
+    else
+        *ret = 1;
+
+    return 0;
+}
+
+/* Check that certificate attributes are passed correctly. */
+static int parse_cert(void)
+{
+    ne_session *sess = DEFSESS;
+    int ret = 0;
+    struct ssl_server_args args = {SERVER_CERT, 0};
+
+    /* don't give a CA cert; should force the verify callback to be
+     * used. */
+    CALL(any_ssl_request(sess, ssl_server, &args, NULL, 
+                        check_cert, &ret));
+    ne_session_destroy(sess);
+
+    ONN("cert verification never called", ret == 0);
+
+    if (ret == -1)
+       return FAIL;
+
+    return OK;
+}
+
+/* Check the certificate chain presented against known dnames. */
+static int check_chain(void *userdata, int fs, const ne_ssl_certificate *cert)
+{
+    int *ret = userdata;
+
+    if (check_cert_dnames(cert, SERVER_DNAME, CACERT_DNAME) == FAIL) {
+        *ret = -1;
+        return 0;
+    }
+    
+    cert = ne_ssl_cert_signedby(cert);
+    if (cert == NULL) {
+        t_context("no CA cert in chain");
+        *ret = -1;
+        return 0;
+    }
+    
+    if (check_cert_dnames(cert, CACERT_DNAME, CACERT_DNAME) == FAIL) {
+        *ret = -1;
+        return 0;
+    }
+    
+    *ret = 1;
+    return 0;
+}
+
+/* Check that certificate attributes are passed correctly. */
+static int parse_chain(void)
+{
+    ne_session *sess = DEFSESS;
+    int ret = 0;
+    struct ssl_server_args args = {SERVER_CERT, 0};
+
+    args.ca_list = "ca/cert.pem";    
+
+    /* don't give a CA cert; should force the verify callback to be
+     * used. */
+    CALL(any_ssl_request(sess, ssl_server, &args, NULL, 
+                        check_chain, &ret));
+    ne_session_destroy(sess);
+
+    ONN("cert verification never called", ret == 0);
+
+    if (ret == -1)
+       return FAIL;
+
+    return OK;
+}
+
+
+static int count_vfy(void *userdata, int fs, const ne_ssl_certificate *c)
+{
+    int *count = userdata;
+    (*count)++;
+    return 0;
+}
+
+static int no_verify(void)
+{
+    ne_session *sess = DEFSESS;
+    int count = 0;
+    struct ssl_server_args args = {SERVER_CERT, 0};
+
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, count_vfy,
+                        &count));
+
+    ONN("verify callback called unnecessarily", count != 0);
+
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+static int cache_verify(void)
+{
+    ne_session *sess = DEFSESS;
+    int ret, count = 0;
+    struct ssl_server_args args = {SERVER_CERT, 0};
+    
+    /* force verify cert. */
+    ret = any_ssl_request(sess, ssl_server, &args, NULL, count_vfy,
+                         &count);
+
+    CALL(spawn_server(7777, ssl_server, &args));
+    ret = any_request(sess, "/foo2");
     CALL(await_server());
 
-    ONREQ(ret);
+    ONV(count != 1,
+       ("verify callback result not cached: called %d times", count));
+
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+/* Copy failures into *userdata, and fail verification. */
+static int get_failures(void *userdata, int fs, const ne_ssl_certificate *c)
+{
+    int *out = userdata;
+    *out = fs;
+    return -1;
+}
+
+/* Helper function: run a request using the given self-signed server
+ * certificate, and expect the request to fail with the given
+ * verification failure flags. */
+static int fail_ssl_request(char *cert, char *cacert, 
+                           const char *msg, int failures)
+{
+    ne_session *sess = DEFSESS;
+    int gotf = 0, ret;
+
+    ret = any_ssl_request(sess, fail_serve, cert, cacert,
+                         get_failures, &gotf);
+
+    ONV(gotf == 0,
+       ("no error in verification callback; request failed: %s",
+        ne_get_error(sess)));
+
+    ONV(gotf & ~NE_SSL_FAILMASK,
+       ("verification flags %x outside mask %x", gotf, NE_SSL_FAILMASK));
+
+    /* check the failure flags were as expected. */
+    ONV(failures != gotf,
+       ("verification flags were %d not %d", gotf, failures));
+
+    /* and check that the request was failed too. */
+    ONV(ret == NE_OK, ("%s", msg));
+
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+/* Note that the certs used for fail_* are all self-signed, so the
+ * cert is passed as CA cert and server cert to fail_ssl_request. */
+
+/* Check that a certificate with the incorrect commonName attribute is
+ * flagged as such. */
+static int fail_wrongCN(void)
+{
+    return fail_ssl_request("wrongcn.pem", "wrongcn.pem",
+                           "certificate with incorrect CN was accepted",
+                           NE_SSL_IDMISMATCH);
+}
+
+/* Check that an expired certificate is flagged as such. */
+static int fail_expired(void)
+{
+    char *c = ne_concat(srcdir, "/expired.pem", NULL);
+    CALL(fail_ssl_request(c, c, "expired certificate was accepted",
+                          NE_SSL_EXPIRED));
+    ne_free(c);
+    return OK;
+}
+
+static int fail_notvalid(void)
+{
+    char *c = ne_concat(srcdir, "/notvalid.pem", NULL);
+    CALL(fail_ssl_request(c, c, "not yet valid certificate was accepted",
+                          NE_SSL_NOTYETVALID));
+    ne_free(c);
+    return OK;    
+}
+
+/* Check that a server cert with a random issuer and self-signed cert
+ * fail with UNTRUSTED. */
+static int fail_untrusted_ca(void)
+{
+    return fail_ssl_request("server.cert", NULL, "untrusted CA.",
+                           NE_SSL_UNTRUSTED);
+}
+
+static int fail_self_signed(void)
+{
+    return fail_ssl_request("ssigned.pem", NULL, "self-signed cert", 
+                           NE_SSL_UNTRUSTED);
+}
+
+/* Test for failure when a server cert is presented which has no
+ * commonName (and no alt names either). */
+static int fail_missing_CN(void)
+{
+    ne_session *sess = DEFSESS;
+
+    ONN("accepted server cert with missing commonName",
+        any_ssl_request(sess, fail_serve, "missingcn.cert", SERVER_CERT,
+                        NULL, NULL) == NE_OK);
+    
+    ONV(strstr(ne_get_error(sess), "missing commonName") == NULL,
+        ("unexpected session error `%s'", ne_get_error(sess)));
+
+    ne_session_destroy(sess);
+    return OK;
+}                            
+
+/* Test that the SSL session is cached across connections. */
+static int session_cache(void)
+{
+    struct ssl_server_args args = {0};
+    ne_session *sess = ne_session_create("https", "localhost", 7777);
+    
+    args.cert = SERVER_CERT;
+    args.cache = 1;
+
+    ne_ssl_trust_cert(sess, def_ca_cert);
+
+    /* have spawned server listen for several connections. */
+    CALL(spawn_server_repeat(7777, ssl_server, &args, 4));
+
+    ONREQ(any_request(sess, "/req1"));
+    ONREQ(any_request(sess, "/req2"));
+    ne_session_destroy(sess);
+    /* server should still be waiting for connections: if not,
+     * something went wrong. */
+    ONN("error from child", dead_server());
+    /* now get rid of it. */
+    reap_server();
+
+    return OK;
+}
+
+/* Callback for client_cert_provider; takes a c. cert as userdata and
+ * registers it. */
+static void ccert_provider(void *userdata, ne_session *sess,
+                           const ne_ssl_dname *const *dns, int dncount)
+{
+    const ne_ssl_client_cert *cc = userdata;
+    ne_ssl_set_clicert(sess, cc);
+}
+
+/* Test that the on-demand client cert provider callback is used. */
+static int client_cert_provided(void)
+{
+    ne_session *sess = DEFSESS;
+    ne_ssl_client_cert *cc;
+    struct ssl_server_args args = {SERVER_CERT, NULL};
+
+    args.require_cc = 1;
+
+    cc = ne_ssl_clicert_read("client.p12");
+    ONN("could not load client.p12", cc == NULL);
+    ONN("could not decrypt client.p12", 
+        ne_ssl_clicert_decrypt(cc, "foobar"));
+    
+    ne_ssl_provide_clicert(sess, ccert_provider, cc);
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT,
+                         NULL, NULL));
+
+    ne_session_destroy(sess);
+    ne_ssl_clicert_free(cc);
+    return OK;
+}
+
+static void cc_check_dnames(void *userdata, ne_session *sess,
+                            const ne_ssl_dname *const *dns, int dncount)
+{
+    int n, *ret = userdata;
+    static const char *expected[4] = {
+        "First Random CA, CAs Ltd., Lincoln, Lincolnshire, GB",
+        "Second Random CA, CAs Ltd., Falmouth, Cornwall, GB",
+        "Third Random CA, CAs Ltd., Ipswich, Suffolk, GB",
+        "Fourth Random CA, CAs Ltd., Norwich, Norfolk, GB"
+    };
+
+    ne_ssl_set_clicert(sess, def_cli_cert);
+
+    if (dncount != 4) {
+        t_context("dname count was %d not 4", dncount);
+        *ret = -1;
+        return;
+    }
+    
+    for (n = 0; n < 4; n++) {
+        char which[5];
+
+        sprintf(which, "%d", n);
+
+        if (check_dname(dns[n], expected[n], which) == FAIL) {
+            *ret = -1;
+            return;
+        }
+    }
+
+    *ret = 1;
+}
+
+/* Test for the list of acceptable dnames sent to the client. */
+static int cc_provided_dnames(void)
+{
+    int check = 0;
+    ne_session *sess = DEFSESS;
+    struct ssl_server_args args = {SERVER_CERT, NULL};
+
+    args.require_cc = 1;
+    args.send_ca = "calist.pem";
+
+    PRECOND(def_cli_cert);
+
+    ne_ssl_provide_clicert(sess, cc_check_dnames, &check);
+
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+
+    ne_session_destroy(sess);
+
+    ONN("provider function not called", check == 0);
+
+    return (check == -1) ? FAIL : OK;
+}
+
+/* Tests use of a client certificate. */
+static int client_cert_pkcs12(void)
+{
+    ne_session *sess = DEFSESS;
+    struct ssl_server_args args = {SERVER_CERT, NULL};
+
+    args.require_cc = 1;
+
+    PRECOND(def_cli_cert);
+
+    ne_ssl_set_clicert(sess, def_cli_cert);
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+
+    ne_session_destroy(sess);    
+    return OK;
+}
+
+
+/* Tests use of an unencrypted client certificate. */
+static int ccert_unencrypted(void)
+{
+    ne_session *sess = DEFSESS;
+    ne_ssl_client_cert *ccert;
+    struct ssl_server_args args = {SERVER_CERT, NULL};
+
+    args.require_cc = 1;
+
+    ccert = ne_ssl_clicert_read("unclient.p12");
+    ONN("unclient.p12 was encrypted", ne_ssl_clicert_encrypted(ccert));
+
+    ne_ssl_set_clicert(sess, ccert);
+    CALL(any_ssl_request(sess, ssl_server, &args, CA_CERT, NULL, NULL));
+
+    ne_ssl_clicert_free(ccert);
+    ne_session_destroy(sess);
+    return OK;
+}
+
+/* non-zero if a server auth header was received */
+static int got_server_auth; 
+
+/* Utility function which accepts the 'tunnel' header. */
+static void tunnel_header(char *value)
+{
+    got_server_auth = 1;
+}
+
+/* Server which acts as a proxy accepting a CONNECT request. */
+static int serve_tunnel(ne_socket *sock, void *ud)
+{
+    struct ssl_server_args *args = ud;
+
+    /* check for a server auth function */
+    want_header = "Authorization";
+    got_header = tunnel_header;
+    got_server_auth = 0;
+
+    /* give the plaintext tunnel reply, acting as the proxy */
+    CALL(discard_request(sock));
+
+    if (got_server_auth) {
+        SEND_STRING(sock, "HTTP/1.1 500 Leaked Server Auth Creds\r\n"
+                    "Content-Length: 0\r\n" "Server: serve_tunnel\r\n\r\n");
+        return 0;
+    } else {
+        SEND_STRING(sock, "HTTP/1.1 200 OK\r\nServer: serve_tunnel\r\n\r\n");
+        return ssl_server(sock, args);
+    }
+}
+
+/* neon versions <= 0.21.2 segfault here because ne_sock_close would
+ * be called twice on the socket after the server cert verification
+ * fails. */
+static int fail_tunnel(void)
+{
+    ne_session *sess = ne_session_create("https", "example.com", 443);
+    struct ssl_server_args args = {SERVER_CERT, NULL};
+
+    ne_session_proxy(sess, "localhost", 7777);
+
+    ONN("server cert verification didn't fail",
+       any_ssl_request(sess, serve_tunnel, &args, CA_CERT,
+                       NULL, NULL) != NE_ERROR);
+    
+    ne_session_destroy(sess);
+    return OK;
+}
+
+static int proxy_tunnel(void)
+{
+    ne_session *sess = ne_session_create("https", "localhost", 443);
+    struct ssl_server_args args = {SERVER_CERT, NULL};
+
+    ne_session_proxy(sess, "localhost", 7777);
+    
+    /* CA cert is trusted, so no verify callback should be needed. */
+    CALL(any_ssl_request(sess, serve_tunnel, &args, CA_CERT,
+                        NULL, NULL));
+
+    ne_session_destroy(sess);
+    return OK;
+}
+
+#define RESP_0LENGTH "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"
+
+/* a tricky test which requires spawning a second server process in
+ * time for a new connection after a 407. */
+static int apt_post_send(ne_request *req, void *ud, const ne_status *st)
+{
+    int *code = ud;
+    if (st->code == *code) {
+        struct ssl_server_args args = {SERVER_CERT, NULL};
+    
+        if (*code == 407) args.numreqs = 2;
+        args.response = RESP_0LENGTH;
+
+        NE_DEBUG(NE_DBG_HTTP, "Got challenge, awaiting server...\n");
+        CALL(await_server());
+        NE_DEBUG(NE_DBG_HTTP, "Spawning proper tunnel server...\n");
+        /* serve *two* 200 OK responses. */
+        CALL(spawn_server(7777, serve_tunnel, &args));
+        NE_DEBUG(NE_DBG_HTTP, "Spawned.\n");
+    }
+    return OK;
+}
+
+static int apt_creds(void *userdata, const char *realm, int attempt,
+                     char *username, char *password)
+{
+    strcpy(username, "foo");
+    strcpy(password, "bar");
+    return attempt;
+}
+
+/* Test for using SSL over a CONNECT tunnel via a proxy server which
+ * requires authentication.  Broke briefly between 0.23.x and
+ * 0.24.0. */
+static int auth_proxy_tunnel(void)
+{
+    ne_session *sess = ne_session_create("https", "localhost", 443);
+    int ret, code = 407;
+    
+    ne_session_proxy(sess, "localhost", 7777);
+    ne_hook_post_send(sess, apt_post_send, &code);
+    ne_set_proxy_auth(sess, apt_creds, NULL);
+    ne_ssl_trust_cert(sess, def_ca_cert);
+    
+    CALL(spawn_server(7777, single_serve_string,
+                      "HTTP/1.0 407 I WANT MORE BISCUITS\r\n"
+                      "Proxy-Authenticate: Basic realm=\"bigbluesea\"\r\n"
+                      "Connection: close\r\n" "\r\n"));
+    
+    /* run two requests over the tunnel. */
+    ret = any_2xx_request(sess, "/foobar");
+    if (!ret) ret = any_2xx_request(sess, "/foobar2");
+    CALL(await_server());
+    CALL(ret);
+
+    ne_session_destroy(sess);
+    return 0;
+}
+
+/* Regression test to check that server credentials aren't sent to the
+ * proxy in a CONNECT request. */
+static int auth_tunnel_creds(void)
+{
+    ne_session *sess = ne_session_create("https", "localhost", 443);
+    int ret, code = 401;
+    struct ssl_server_args args = {SERVER_CERT, 0};
+    
+    ne_session_proxy(sess, "localhost", 7777);
+    ne_hook_post_send(sess, apt_post_send, &code);
+    ne_set_server_auth(sess, apt_creds, NULL);
+    ne_ssl_trust_cert(sess, def_ca_cert);
+    
+    args.response = "HTTP/1.1 401 I want a Shrubbery\r\n"
+        "WWW-Authenticate: Basic realm=\"bigredocean\"\r\n"
+        "Server: Python\r\n" "Content-Length: 0\r\n" "\r\n";
+    
+    CALL(spawn_server(7777, serve_tunnel, &args));
+    ret = any_2xx_request(sess, "/foobar");
+    CALL(await_server());
+    CALL(ret);
 
     ne_session_destroy(sess);
+    return OK;    
+}
+
+/* compare against known digest of notvalid.pem.  Via:
+ *   $ openssl x509 -fingerprint -sha1 -noout -in notvalid.pem */
+#define THE_DIGEST "cf:5c:95:93:76:c6:3c:01:8b:62:" \
+                   "b1:6f:f7:7f:42:32:ac:e6:69:1b"
+
+static int cert_fingerprint(void)
+{
+    char *fn = ne_concat(srcdir, "/notvalid.pem", NULL);
+    ne_ssl_certificate *cert = ne_ssl_cert_read(fn);
+    char digest[60];
+    
+    ne_free(fn);
+
+    ONN("could not load notvalid.pem", cert == NULL);
+
+    ONN("failed to digest", ne_ssl_cert_digest(cert, digest));
+    ne_ssl_cert_free(cert);
+
+    ONV(strcmp(digest, THE_DIGEST),
+        ("digest was %s not %s", digest, THE_DIGEST));
+
+    return OK;
+}
+
+/* verify that identity of certificate in filename 'fname' is 'identity' */
+static int check_identity(const char *fname, const char *identity)
+{
+    ne_ssl_certificate *cert = ne_ssl_cert_read(fname);
+    const char *id;
+
+    ONV(cert == NULL, ("could not read cert `%s'", fname));
+
+    id = ne_ssl_cert_identity(cert);
+
+    if (identity) {
+        ONV(id == NULL, ("certificate `%s' had no identity", fname));
+        ONV(strcmp(id, identity), 
+            ("certificate `%s' had identity `%s' not `%s'", fname, 
+             id, identity));
+    } else {
+        ONV(id != NULL, ("certificate `%s' had identity `%s' (expected none)",
+                         fname, id));
+    }            
+    
+    ne_ssl_cert_free(cert);
+    return OK;
+}
+
+/* check certificate identities. */
+static int cert_identities(void)
+{
+    static const struct {
+        const char *fname, *identity;
+    } certs[] = {
+        { "twocn.cert", "localhost" },
+        { "altname1.cert", "localhost" },
+        { "altname2.cert", "nohost.example.com" },
+        { "altname4.cert", "localhost" },
+        { "ca4.pem", "fourth.example.com" },
+        { NULL, NULL }
+    };
+    int n;
+
+    for (n = 0; certs[n].fname != NULL; n++)
+        CALL(check_identity(certs[n].fname, certs[n].identity));
+
+    return OK;
+}
+
+static int check_validity(const char *fname,
+                          const char *from, const char *until)
+{
+    char actfrom[NE_SSL_VDATELEN], actuntil[NE_SSL_VDATELEN];
+    ne_ssl_certificate *cert;
+
+    cert = ne_ssl_cert_read(fname);
+    ONV(cert == NULL, ("could not load cert `%s'", fname));
+
+    /* cover all calling combos for nice coverage analysis */
+    ne_ssl_cert_validity(cert, NULL, NULL);
+    ne_ssl_cert_validity(cert, actfrom, NULL);
+    ne_ssl_cert_validity(cert, NULL, actuntil);
+    ne_ssl_cert_validity(cert, actfrom, actuntil);
+
+    ONV(strcmp(actfrom, from), 
+        ("%s: start time was `%s' not `%s'", fname, actfrom, from));
+
+    ONV(strcmp(actuntil, until), 
+        ("%s: end time was `%s' not `%s'", fname, actuntil, until));
+
+    ne_ssl_cert_free(cert);
+    return OK;
+}
+
+/* ceritificate validity times. */
+static int cert_validity(void)
+{
+    char *cert = ne_concat(srcdir, "/expired.pem", NULL);
+    CALL(check_validity(cert, "Jan 21 20:39:04 2002 GMT", "Jan 31 20:39:04 2002 GMT"));
+    ne_free(cert);
+    cert = ne_concat(srcdir, "/notvalid.pem", NULL);
+    CALL(check_validity(cert, "Dec 27 20:40:29 2023 GMT", "Dec 28 20:40:29 2023 GMT"));
+    ne_free(cert);
+    return OK;
+}
+
+/* dname comparisons. */
+static int dname_compare(void)
+{
+    ne_ssl_certificate *ssigned;
+    const ne_ssl_dname *dn1, *dn2;
+    
+    dn1 = ne_ssl_cert_subject(def_server_cert);
+    dn2 = ne_ssl_cert_subject(def_server_cert);
+    ONN("identical subject names not equal", ne_ssl_dname_cmp(dn1, dn2) != 0);
+
+    dn2 = ne_ssl_cert_issuer(def_server_cert);
+    ONN("issuer and subject names equal for signed cert",
+        ne_ssl_dname_cmp(dn1, dn2) == 0);
+    
+    dn1 = ne_ssl_cert_subject(def_ca_cert);
+    ONN("issuer of signed cert not equal to subject of CA cert",
+        ne_ssl_dname_cmp(dn1, dn2) != 0);
+
+    ssigned = ne_ssl_cert_read("ssigned.pem");
+    ONN("could not load ssigned.pem", ssigned == NULL);
+
+    dn1 = ne_ssl_cert_subject(ssigned);
+    dn2 = ne_ssl_cert_issuer(ssigned);
+    ONN("issuer and subject names not equal for self-signed cert",
+        ne_ssl_dname_cmp(dn1, dn2));
+    ne_ssl_cert_free(ssigned);
+
+    return OK;
+}
+
+/* The dname with the UTF-8 encoding of the Unicode string: 
+ * "H<LATIN SMALL LETTER E WITH GRAVE>llo World". */
+#define I18N_DNAME "H\xc3\xa8llo World, Neon Hackers Ltd, Cambridge, Cambridgeshire, GB"
+
+/* N.B. t61subj.cert encodes an ISO-8859-1 string in a T61String
+ * field, which is strictly wrong but the common usage. */
+
+/* tests for ne_ssl_readable_dname */
+static int dname_readable(void)
+{
+    struct {
+        const char *cert;
+        const char *subjdn, *issuerdn;
+    } ts[] = {
+        { "justmail.cert", "blah@example.com", NULL },
+        { "t61subj.cert", I18N_DNAME, NULL },
+        { "bmpsubj.cert", I18N_DNAME, NULL },
+        { "utf8subj.cert", I18N_DNAME, NULL }
+    };
+    size_t n;
+
+    for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
+        ne_ssl_certificate *cert = ne_ssl_cert_read(ts[n].cert);
+        ONV(cert == NULL, ("could not load cert %s", ts[n].cert));
+        CALL(check_cert_dnames(cert, ts[n].subjdn, ts[n].issuerdn));
+        ne_ssl_cert_free(cert);
+    }
+
+    return OK;
+}
+
+/* test cert comparisons */
+static int cert_compare(void)
+{
+    ne_ssl_certificate *c1, *c2;
+
+    c1 = ne_ssl_cert_read("server.cert");
+    c2 = ne_ssl_cert_read("server.cert");
+    ONN("identical certs don't compare equal", ne_ssl_cert_cmp(c1, c2) != 0);
+    ONN("identical certs don't compare equal", ne_ssl_cert_cmp(c2, c1) != 0);
+    ne_ssl_cert_free(c2);
+
+    c2 = ne_ssl_cert_read("ssigned.pem");
+    ONN("different certs don't compare different",
+        ne_ssl_cert_cmp(c1, c2) == 0);
+    ONN("different certs don't compare different",
+        ne_ssl_cert_cmp(c2, c1) == 0);
+    ne_ssl_cert_free(c2);
+    ne_ssl_cert_free(c1);
+
+    return OK;
+}
+
+/* Extract raw base64 string from a PEM file */
+static int flatten_pem(const char *fname, char **out)
+{
+    FILE *fp = fopen(fname, "r");
+    char buf[80];
+    size_t outlen = 0;
+    int ignore = 1;
+
+    ONV(fp == NULL, ("could not open %s", fname));
+
+    *out = NULL;
+
+    while (fgets(buf, sizeof buf, fp) != NULL) {
+        size_t len = strlen(buf) - 1;
+        
+        if (len < 1) continue;
+
+        /* look for the wrapper lines. */
+        if (strncmp(buf, "-----", 5) == 0) {
+            ignore = !ignore;
+            continue;
+        }
+
+        /* ignore until the first wrapper line */
+        if (ignore) continue;
+        
+        *out = realloc(*out, outlen + len + 1);
+        memcpy(*out + outlen, buf, len);
+        outlen += len;
+    }
+
+    (*out)[outlen] = '\0';
+    fclose(fp);
+
+    return OK;
+}
+
+/* check export cert data 'actual' against expected data 'expected */
+static int check_exported_data(const char *actual, const char *expected)
+{
+    ONN("could not export cert", actual == NULL);
+
+    ONN("export data contained newline",
+        strchr(actual, '\r') || strchr(actual, '\n'));        
+
+    ONV(strcmp(actual, expected), ("exported cert differed from expected:\n"
+                                   "actual: %s\nexpected: %s", 
+                                   actual, expected));
+    return OK;
+}
+
+/* Test import and export of certificates.  The export format is PEM
+ * without the line feeds and wrapping; compare against . */
+static int import_export(void)
+{
+    char *expected, *actual;
+    ne_ssl_certificate *cert, *imp;
+
+    CALL(flatten_pem("server.cert", &expected));
+    
+    cert = ne_ssl_cert_read("server.cert");
+    ONN("could not load server.cert", cert == NULL);
+
+    /* export the cert to and compare it with the PEM file */
+    actual = ne_ssl_cert_export(cert);
+    CALL(check_exported_data(actual, expected));
+
+    /* import the exported cert data, check it looks the same */
+    imp = ne_ssl_cert_import(actual);
+    ONN("failed to import exported cert", imp == NULL);
+    ONN("imported cert was different to original", 
+        ne_ssl_cert_cmp(imp, cert));
+
+    /* re-export the imported cert and check that looks the same */
+    ne_free(actual);
+    actual = ne_ssl_cert_export(imp);
+    CALL(check_exported_data(actual, expected));
+    ne_ssl_cert_free(imp);
+
+    /* try importing from bogus data */
+    imp = ne_ssl_cert_import("!!");
+    ONN("imported bogus cert from bogus base64", imp != NULL);
+    imp = ne_ssl_cert_import("aaaa");
+    ONN("imported bogus cert from valid base64", imp != NULL);
+
+    ne_ssl_cert_free(cert);
+    ne_free(actual);
+    ne_free(expected);
     return OK;
 }
 
+/* Test write/read */
+static int read_write(void)
+{
+    ne_ssl_certificate *c1, *c2;
+
+    c1 = ne_ssl_cert_read("server.cert");
+    ONN("could not load server.cert", c1 == NULL);
+
+    ONN("could not write output.pem", ne_ssl_cert_write(c1, "output.pem"));
+    
+    ONN("wrote to nonexistent directory",
+        ne_ssl_cert_write(c1, "nonesuch/output.pem") == 0);
+
+    c2 = ne_ssl_cert_read("output.pem");
+    ONN("could not read output.pem", c2 == NULL);
+    
+    ONN("read of output.pem differs from original",
+        ne_ssl_cert_cmp(c2, c1));
+
+    ne_ssl_cert_free(c1);
+    ne_ssl_cert_free(c2);
+
+    return OK;
+}
+
+/* A verification callback which caches the passed cert. */
+static int verify_cache(void *userdata, int fs,
+                        const ne_ssl_certificate *cert)
+{
+    char **cache = userdata;
+    
+    if (*cache == NULL) {
+        *cache = ne_ssl_cert_export(cert);
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+/* Test a common use of the SSL API; cache the server cert across
+ * sessions. */
+static int cache_cert(void)
+{
+    ne_session *sess = DEFSESS;
+    char *cache = NULL;
+    ne_ssl_certificate *cert;
+    struct ssl_server_args args = {0};
+
+    args.cert = "ssigned.pem";
+    args.cache = 1;
+
+    ONREQ(any_ssl_request(sess, ssl_server, &args, CA_CERT,
+                          verify_cache, &cache));
+    ne_session_destroy(sess);
+
+    ONN("no cert was cached", cache == NULL);
+    
+    /* make a real cert */
+    cert = ne_ssl_cert_import(cache);
+    ONN("could not import cached cert", cert == NULL);
+    ne_free(cache);
+
+    /* create a new session */
+    sess = DEFSESS;
+    /* trust the cert */
+    ne_ssl_trust_cert(sess, cert);
+    ne_ssl_cert_free(cert);
+    /* now, the request should succeed without manual verification */
+    ONREQ(any_ssl_request(sess, ssl_server, &args, CA_CERT,
+                          NULL, NULL));
+    ne_session_destroy(sess);
+    return OK;
+}
+
+/* TODO: code paths still to test in cert verification:
+ * - server cert changes between connections: Mozilla gives
+ * a "bad MAC decode" error for this; can do better?
+ * - server presents no certificate (using ADH ciphers)... can
+ * only really happen if they mess with the SSL_CTX and enable
+ * ADH cipher manually; but good to check the failure case is 
+ * safe.
+ * From the SSL book:
+ * - an early FIN should be returned as a possible truncation attack,
+ * NOT just an NE_SOCK_CLOSED.
+ * - unexpected close_notify is an error but not an attack.
+ * - never attempt session resumption after any aborted connection.
+ */
+
 ne_test tests[] = {
-    T(init),
+    T_LEAKY(init),
+
+    T(load_server_certs),
+    T(trust_default_ca),
+
+    T(cert_fingerprint),
+    T(cert_identities),
+    T(cert_validity),
+    T(cert_compare),
+    T(dname_compare),
+    T(dname_readable),
+    T(import_export),
+    T(read_write),
+
+    T(load_client_cert),
 
     T(simple),
+    T(simple_sslv2),
+    T(simple_eof),
+    T(empty_truncated_eof),
+    T(fail_not_ssl),
+    T(cache_cert),
+
+    T(client_cert_pkcs12),
+    T(ccert_unencrypted),
+    T(client_cert_provided),
+    T(cc_provided_dnames),
+
+    T(parse_cert),
+    T(parse_chain),
+
+    T(no_verify),
+    T(cache_verify),
+    T_LEAKY(wildcard_init),
+    T(wildcard_match),
+    T(caseless_match),
+
+    T(subject_altname),
+    T(two_subject_altname),
+    T(two_subject_altname2),
+    T(notdns_altname),
+    T(ipaddr_altname),
+
+    T(multi_commonName),
+    T(commonName_first),
+
+    T(fail_wrongCN),
+    T(fail_expired),
+    T(fail_notvalid),
+    T(fail_untrusted_ca),
+    T(fail_self_signed),
+    T(fail_missing_CN),
+
+    T(session_cache),
+       
+    T(fail_tunnel),
+    T(proxy_tunnel),
+    T(auth_proxy_tunnel),
+    T(auth_tunnel_creds),
 
     T(NULL) 
 };
index c288d1e..3d0fac1 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    String handling tests
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2003, 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
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h> /* for the ENOENT definitions in str_errors */
+#endif
 
-#include "string_utils.h"
+#include "ne_string.h"
 
 #include "tests.h"
 
+#undef ONCMP
+#define ONCMP(a,b) ONV(strcmp(a, b), \
+                      ("result was [%s] not [%s]", a, b))
+
 static int simple(void) {
-    sbuffer s = sbuffer_create();
+    ne_buffer *s = ne_buffer_create();
+    ON(s == NULL);
+    ne_buffer_zappend(s, "abcde");
+    ONCMP(s->data, "abcde");
+    ON(ne_buffer_size(s) != 5);
+    ne_buffer_destroy(s);
+    return OK;
+}
+
+static int buf_concat(void)
+{
+    ne_buffer *s = ne_buffer_create();
     ON(s == NULL);
-    ON(sbuffer_zappend(s, "abcde"));
-    ON(strcmp(sbuffer_data(s), "abcde"));
-    ON(sbuffer_size(s) != 5);
-    sbuffer_destroy(s);
+    ne_buffer_concat(s, "a", "b", "c", "d", "e", "f", "g", NULL);
+    ONCMP(s->data, "abcdefg");
+    ON(ne_buffer_size(s) != 7);
+    ne_buffer_destroy(s);
     return OK;
 }
 
-static int concat(void) {
-    sbuffer s = sbuffer_create();
+static int buf_concat2(void)
+{
+#define RES "alphabetagammadeltaepsilonetatheta"
+    ne_buffer *s = ne_buffer_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);
+    ne_buffer_concat(s, "alpha", "beta", "gamma", "delta", "epsilon", 
+                    "eta", "theta", NULL);
+    ONCMP(s->data, RES);
+    ON(ne_buffer_size(s) != strlen(RES));
+    ne_buffer_destroy(s);
+    return OK;
+}
+
+static int buf_concat3(void)
+{
+    ne_buffer *s = ne_buffer_create();
+    ON(s == NULL);
+    ne_buffer_zappend(s, "foobar");
+    ne_buffer_concat(s, "norman", NULL);
+    ONCMP(s->data, "foobarnorman");
+    ON(ne_buffer_size(s) != 12);
+    ne_buffer_destroy(s);
     return OK;
 }
 
 static int append(void) {
-    sbuffer s = sbuffer_create();
+    ne_buffer *s = ne_buffer_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);
+    ne_buffer_append(s, "a", 1);
+    ne_buffer_append(s, "b", 1);
+    ne_buffer_append(s, "c", 1);
+    ONCMP(s->data, "abc");
+    ON(ne_buffer_size(s) != 3);
+    ne_buffer_destroy(s);
     return OK;
 }    
 
+static int grow(void)
+{
+    ne_buffer *s = ne_buffer_ncreate(2);
+    ON(s == NULL);
+    ne_buffer_append(s, "a", 1);
+    ne_buffer_grow(s, 4);
+    ONCMP(s->data, "a");
+    ne_buffer_destroy(s);
+    return OK;
+}
+
 static int alter(void) {
-    sbuffer s = sbuffer_create();
+    ne_buffer *s = ne_buffer_create();
     char *d;
     ON(s == NULL);
-    ON(sbuffer_zappend(s, "abcdefg"));
-    d = sbuffer_data(s);
+    ne_buffer_zappend(s, "abcdefg");
+    d = s->data;
     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"));
+    ne_buffer_altered(s);
+    ONCMP(s->data, "ab");
+    ON(ne_buffer_size(s) != 2);
+    ne_buffer_zappend(s, "hijkl");
+    ONCMP(s->data, "abhijkl");
+    ne_buffer_destroy(s);
+    return OK;
+}
+
+/* Macros for testing ne_token. */
+
+#define TEST(res) do { \
+  char *tok = ne_token(&pnt, ','); \
+  ONN(res ": return", tok == NULL); \
+  ONN(res ": compare", strcmp(tok, (res))); \
+  ONN(res ": modify", pnt == NULL); \
+} while (0)
+
+#define LASTTEST(res) do { \
+  char *tok = ne_token(&pnt, ','); \
+  ONN(res ": last return", tok == NULL); \
+  ONN(res ": last compare", strcmp(tok, (res))); \
+  ONN(res ": last modify", pnt != NULL); \
+} while (0)
+
+#define QTEST(res) do { \
+  char *tok = ne_qtoken(&pnt, ',', QUOTES); \
+  ONN(res ": return", tok == NULL); \
+  ONN(res ": compare", strcmp(tok, (res))); \
+  ONN(res ": modify", pnt == NULL); \
+} while (0)
+
+#define QLASTTEST(res) do { \
+  char *tok = ne_qtoken(&pnt, ',', QUOTES); \
+  ONN(res ": last return", tok == NULL); \
+  ONN(res ": last compare", strcmp(tok, (res))); \
+  ONN(res ": last modify", pnt != NULL); \
+} while (0)
+
+static int token1(void)
+{
+    char *str = ne_strdup("a,b,c,d"), *pnt = str;
+
+    TEST("a"); TEST("b"); TEST("c"); LASTTEST("d");
+    
+    ne_free(str);
+    return OK;
+}
+
+static int token2(void)
+{
+    char *str = ne_strdup("norman,fishing, elsewhere"), *pnt = str;
+    
+    TEST("norman"); TEST("fishing"); LASTTEST(" elsewhere");
+
+    ne_free(str);
+    return OK;
+}
+
+static int nulls(void)
+{
+    char *str = ne_strdup("alpha,,gamma"), *pnt = str;
+
+    TEST("alpha"); TEST(""); LASTTEST("gamma");
+    ne_free(str);
+    
+    pnt = str = ne_strdup(",,,wooo");
+    TEST(""); TEST(""); TEST(""); LASTTEST("wooo");
+    ne_free(str);
+
+    pnt = str = ne_strdup("wooo,,,");
+    TEST("wooo"); TEST(""); TEST(""); LASTTEST("");
+    ne_free(str);    
+
+    return OK;
+}
+
+static int empty(void)
+{
+    char *str = ne_strdup(""), *pnt = str;
+
+    LASTTEST("");
+    ne_free(str);
+
+    return OK;
+}
+
+#undef QUOTES
+#define QUOTES "'"
+
+static int quoted(void)
+{
+    char *str = 
+       ne_strdup("alpha,'beta, a fish called HELLO!?',sandwiches");
+    char *pnt = str;
+    
+    QTEST("alpha");
+    QTEST("'beta, a fish called HELLO!?'");
+    QLASTTEST("sandwiches");
+    
+    ne_free(str);
+    return OK;
+}
+
+static int badquotes(void)
+{
+    char *str = ne_strdup("alpha,'blah"), *pnt = str;
+    
+    QTEST("alpha");
+    ON(ne_qtoken(&pnt, ',', QUOTES) != NULL);
+    
+    ne_free(str);
+    return OK;
+}
+
+/* for testing ne_shave. */
+#undef TEST
+#define TEST(str, ws, res) do { \
+  char *s = ne_strdup((str)); \
+  char *r = ne_shave(s, (ws)); \
+  ONN("[" str "]", strcmp(r, (res))); \
+  ne_free(s); \
+} while (0)
+
+static int shave(void)
+{
+    TEST(" b ", " ", "b");
+    TEST("b", " ", "b");
+    TEST("   b    ", " ", "b");
+    TEST("--bbb-----", "-", "bbb");
+    TEST("hello, world    ", " ", "hello, world");
+    TEST("<<><<<><<this is foo<<><<<<><<", "<>", "this is foo");
+    TEST("09809812342347I once saw an helicopter0012312312398", "0123456789",
+        "I once saw an helicopter");
+    return OK;
+}
+
+/* Regression test for ne_shave call which should produce an empty
+ * string. */
+static int shave_regress(void)
+{
+    TEST("\"\"", "\"", "");
+    return OK;
+}
+
+/* Test the ne_token/ne_shave combination. */
+
+#undef TEST
+#undef LASTTEST
+
+#define TEST(res) do { \
+  char *tok = ne_token(&pnt, ','); \
+  ONN(res ": return", tok == NULL); \
+  tok = ne_shave(tok, " "); \
+  ONN(res ": shave", tok == NULL); \
+  ONN(res ": compare", strcmp(tok, (res))); \
+  ONN(res ": modify", pnt == NULL); \
+} while (0)
+
+
+#define LASTTEST(res) do { \
+  char *tok = ne_token(&pnt, ','); \
+  ONN(res ": last return", tok == NULL); \
+  tok = ne_shave(tok, " "); \
+  ONN(res ": last shave", tok == NULL); \
+  ONN(res ": last compare", strcmp(tok, (res))); \
+  ONN(res ": last modify", pnt != NULL); \
+} while (0)
+
+/* traditional use of ne_token/ne_shave. */
+static int combo(void)
+{
+    char *str = ne_strdup("  fred , mary, jim , alice, david"), *pnt = str;
+    
+    TEST("fred"); TEST("mary"); TEST("jim"); TEST("alice");
+    LASTTEST("david");
+    
+    ne_free(str);
+    return 0;
+}
+
+static int concat(void)
+{
+#define CAT(res, args) do { char *str = ne_concat args; \
+ONCMP(str, res); \
+ne_free(str); } while (0)
+    CAT("alphabeta", ("alpha", "beta", NULL));
+    CAT("alpha", ("alpha", "", "", NULL));
+    CAT("", ("", NULL));
+    CAT("", ("", "", "", NULL));
+    CAT("alpha", ("", "a", "lph", "", "a", NULL));
+    return OK;    
+}
+
+static int str_errors(void)
+{
+    char expect[200], actual[200];
+    
+    strncpy(expect, strerror(ENOENT), sizeof(expect));
+    ONN("ne_strerror did not return passed-in buffer",
+       ne_strerror(ENOENT, actual, sizeof(actual)) != actual);
+    
+    ONV(strcmp(expect, actual), 
+       ("error from ENOENT was `%s' not `%s'", actual, expect));
+
+    /* Test truncated error string is still NUL-terminated. */
+    ne_strerror(ENOENT, actual, 6);
+    ONN("truncated string had wrong length", strlen(actual) != 5);
+
+    return OK;
+}
+
+static int strnzcpy(void)
+{
+    char buf[5];
+
+    ne_strnzcpy(buf, "abcdefghi", sizeof buf);
+    ONV(strcmp(buf, "abcd"), ("result was `%s' not `abcd'", buf));
+    
+    ne_strnzcpy(buf, "ab", sizeof buf);
+    ONV(strcmp(buf, "ab"), ("result was `%s' not `ab'", buf));    
+
+    return OK;    
+}
+
+#define FOX_STRING "The quick brown fox jumped over the lazy dog"
+#define PUNC_STRING "<>,.;'#:@~[]{}!\"$%^&*()_+-="
+
+static int cleaner(void)
+{
+    static const char *strings[] = {
+        "alpha", "alpha",
+        "pretty\033[41mcolours", "pretty [41mcolours",
+        "beta\n", "beta ",
+        "del\rt\na", "del t a",
+        FOX_STRING, FOX_STRING,
+        "0123456789", "0123456789",
+        PUNC_STRING, PUNC_STRING,
+        "\01blah blee\05bloo", " blah blee bloo",
+        NULL,
+    };
+    unsigned int n;
+    
+    for (n = 0; strings[n]; n+=2) {
+        char *act = ne_strclean(ne_strdup(strings[n]));
+        
+        ONV(strcmp(act, strings[n+1]), 
+            ("cleansed to `%s' not `%s'", act, strings[n+1]));
+        
+        ne_free(act);
+    }
+
+    return OK;
+}
+
+/* Check that raw data 'raw', of length 'len', has base64 encoding
+ * of 'expected'. */
+static int b64_check(const unsigned char *raw, size_t len,
+                     const char *expected)
+{
+    char *encoded = ne_base64(raw, len);
+    unsigned char *decoded;
+    size_t dlen;
+    
+    ONV(strcmp(encoded, expected), 
+        ("base64(\"%s\") gave \"%s\" not \"%s\"", raw, encoded, expected));
+    
+    dlen = ne_unbase64(encoded, &decoded);
+    ONV(dlen != len, 
+        ("decoded `%s' length was %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T,
+         expected, dlen, len));
+
+    ONV(memcmp(raw, decoded, dlen), 
+        ("decoded `%s' as `%.*s' not `%.*s'",
+         expected, (int)dlen, decoded, (int)dlen, raw));
+
+    ne_free(decoded);
+    ne_free(encoded);
+    return OK;
+}
+
+/* ALLBITS: base64 encoding of "\0..\377" */
+#define ALLBITS \
+"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss" \
+"LS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZ" \
+"WltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWG" \
+"h4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz" \
+"tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g" \
+"4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
+
+static int base64(void)
+{
+    unsigned char bits[256];
+    size_t n;
+
+#define B64B(x, l, y) CALL(b64_check(x, l, y))
+#define B64(x, y) B64B(x, strlen(x), y)
+
+    /* invent these with 
+     *  $ printf "string" | uuencode -m blah
+     */
+    B64("a", "YQ==");
+    B64("bb", "YmI=");
+    B64("ccc", "Y2Nj");
+    B64("Hello, world", "SGVsbG8sIHdvcmxk");
+    B64("Aladdin:open sesame", "QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
+    B64("I once saw a dog called norman.\n", 
+       "SSBvbmNlIHNhdyBhIGRvZyBjYWxsZWQgbm9ybWFuLgo=");
+    B64("The quick brown fox jumped over the lazy dog", 
+       "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2c=");
+
+    /* binary data..
+     *   $ printf "string" | wc -c # get the length
+     *   $ printf "string" | uuencode -m blah # get the base64
+     */
+    B64B("\0\0\0\0\0\n", 6, "AAAAAAAK");
+    B64B("I once wished \0 upon a \0 fish.", 30, 
+        "SSBvbmNlIHdpc2hlZCAAIHVwb24gYSAAIGZpc2gu");
+    B64B("\201\202\203\204", 4, "gYKDhA==");
+
+    for (n = 0; n < sizeof bits; n++)
+        bits[n] = (unsigned char)n;
+    CALL(b64_check(bits, sizeof bits, ALLBITS));
+
+#undef B64
+#undef B64B
+    return OK;
+}
+
+static int unbase64(void)
+{
+    static const char *ts[] = {
+        "", "a", "ab", "abc", 
+        "}bcd", "a}cd", "ab}d", "abc}", "    ",
+        "^bcd", "a^cd", "ab^d", "abc^",
+        "====", "=bcd", "a=cd", "ab=d", "a==d", "a=c=",
+        NULL
+    };
+    size_t n;
+
+    for (n = 0; ts[n]; n++) {
+        unsigned char *tmp;
+        ONV(ne_unbase64(ts[n], &tmp) != 0,
+            ("invalid string `%s' was decoded", ts[n]));
+    }
+
     return OK;
 }
 
-test_func tests[] = {
-    simple,
-    concat,
-    append,
-    alter,
-    NULL
+ne_test tests[] = {
+    T(simple),
+    T(buf_concat),
+    T(buf_concat2),
+    T(buf_concat3),
+    T(append),
+    T(grow),
+    T(alter),
+    T(token1),
+    T(token2),
+    T(nulls),
+    T(empty),
+    T(quoted),
+    T(badquotes),
+    T(shave),
+    T(shave_regress),
+    T(combo),
+    T(concat),
+    T(str_errors),
+    T(strnzcpy),
+    T(cleaner),
+    T(base64),
+    T(unbase64),
+    T(NULL)
 };
 
index 5c61513..494f8f0 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    neon test suite
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
@@ -94,13 +94,58 @@ static int stub_decompress(void)
 static int stub_ssl(void)
 {
     ne_session *sess = ne_session_create("https", "localhost", 7777);
-    
+    ne_ssl_certificate *cert;
+    ne_ssl_client_cert *cc;
+
     /* 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);
-    
+    cert = ne_ssl_cert_read("Makefile");
+    if (cert) {
+        char *dn, digest[60], date[NE_SSL_VDATELEN];
+        const ne_ssl_certificate *issuer;
+
+        /* This branch should never be executed, but lets pretend it
+         * will to prevent the compiler optimising this code away if
+         * it's placed after the cert != NULL test.  And all that
+         * needs to be tested is that these functions link OK. */
+        dn = ne_ssl_readable_dname(ne_ssl_cert_subject(cert));
+        ONN("this code shouldn't run", dn != NULL);
+        dn = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
+        ONN("this code shouldn't run", dn != NULL);
+        issuer = ne_ssl_cert_signedby(cert);
+        ONN("this code shouldn't run", issuer != NULL);
+        ONN("this code shouldn't run", ne_ssl_cert_digest(cert, digest));
+        ne_ssl_cert_validity(cert, date, date);
+        ONN("this code shouldn't run",
+            ne_ssl_dname_cmp(ne_ssl_cert_subject(cert),
+                             ne_ssl_cert_issuer(cert)));
+        ONN("this code shouldn't run", ne_ssl_cert_identity(issuer) != NULL);
+        ONN("this code shouldn't run", ne_ssl_cert_export(cert) != NULL);
+    }
+
+    ONN("this code shouldn't run", ne_ssl_cert_import("foo") != NULL);
+    ONN("this code shouldn't run", ne_ssl_cert_read("Makefile") != NULL);
+    ONN("this code shouldn't succeed", ne_ssl_cert_cmp(NULL, NULL) == 0);
+
+    ONN("certificate load succeeded", cert != NULL);
+    ne_ssl_cert_free(cert);
+
+    cc = ne_ssl_clicert_read("Makefile");
+    if (cc) {
+        const char *name;
+        /* dead branch as above. */
+        cert = (void *)ne_ssl_clicert_owner(cc);
+        ONN("this code shouldn't run", cert != NULL);
+        name = ne_ssl_clicert_name(cc);
+        ONN("this code shouldn't run", name != NULL);
+        ONN("this code shouldn't run", ne_ssl_clicert_decrypt(cc, "fubar"));
+        ne_ssl_set_clicert(sess, cc);
+    }
+
+    ONN("client certificate load succeeded", cc != NULL);
+    ne_ssl_clicert_free(cc);
+
+    ne_ssl_trust_default_ca(sess);
+
     ne_session_destroy(sess);
     return OK;
 }
index 761c477..753c376 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    URI tests
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2003, 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
 #include <string.h>
 #endif
 
-#include <uri.h>
+#include "ne_uri.h"
+#include "ne_alloc.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));
+    ne_uri p = {0};
+    ON(ne_uri_parse("http://www.webdav.org/foo", &p));
     ON(strcmp(p.host, "www.webdav.org"));
     ON(strcmp(p.path, "/foo"));
     ON(strcmp(p.scheme, "http"));
-    ON(p.port != -1);
+    ON(p.port);
     ON(p.authinfo != NULL);
+    ne_uri_free(&p);
     return 0;
 }
 
 static int simple_ssl(void)
 {
-    struct uri p = {0};
-    ON(uri_parse("https://webdav.org/", &p, NULL));
+    ne_uri p = {0};
+    ON(ne_uri_parse("https://webdav.org/", &p));
     ON(strcmp(p.scheme, "https"));
-    ON(p.port != -1);
+    ON(p.port);
+    ne_uri_free(&p);
     return OK;
 }
 
 static int no_path(void)
 {
-    struct uri p = {0};
-    ON(uri_parse("https://webdav.org", &p, NULL));
+    ne_uri p = {0};
+    ON(ne_uri_parse("https://webdav.org", &p));
     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"));
+    ne_uri_free(&p);
     return OK;
 }
 
@@ -85,104 +68,310 @@ static int authinfo(void)
 static int escapes(void)
 {
     char *un, *esc;
-    esc = uri_abspath_escape(STR);
+    esc = ne_path_escape(STR);
     ON(esc == NULL);
-    un = uri_unescape(esc);
+    un = ne_path_unescape(esc);
     ON(un == NULL);
     ON(strcmp(un, STR));
-    free(un);
-    free(esc);
+    ne_free(un);
+    ne_free(esc);
+    ONN("unescape accepted invalid URI", 
+        ne_path_unescape("/foo%zzbar") != NULL);
+    /* no-escape path */
+    esc = ne_path_escape("/foobar");
+    ON(strcmp(esc, "/foobar"));
+    ne_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);
+    static const struct {
+       const char *path, *parent;
+    } ps[] = {
+       { "/a/b/c", "/a/b/" },
+       { "/a/b/c/", "/a/b/" },
+       { "/alpha/beta", "/alpha/" },
+       { "/foo", "/" },
+       { "norman", NULL },
+       { "/", NULL },
+       { "", NULL },
+       { NULL, NULL }
+    };
+    int n;
+    
+    for (n = 0; ps[n].path != NULL; n++) {
+       char *p = ne_path_parent(ps[n].path);
+       if (ps[n].parent == NULL) {
+           ONV(p != NULL, ("parent of `%s' was `%s' not NULL", 
+                           ps[n].path, p));
+       } else {
+           ONV(p == NULL, ("parent of `%s' was NULL", ps[n].path));
+           ONV(strcmp(p, ps[n].parent),
+               ("parent of `%s' was `%s' not `%s'",
+                ps[n].path, p, ps[n].parent));
+           ne_free(p);
+       }
+    }
+
     return OK;
 }
 
-static int abspath(void)
+static int compares(void)
 {
-    const char *p = uri_abspath("http://norman:1234/a/b");
-    ON(p == NULL);
-    ON(strcmp(p, "/a/b"));
+    const char *alpha = "/alpha";
+
+    ON(ne_path_compare("/a", "/a/") != 0);
+    ON(ne_path_compare("/a/", "/a") != 0);
+    ON(ne_path_compare("/ab", "/a/") == 0);
+    ON(ne_path_compare("/a/", "/ab") == 0);
+    ON(ne_path_compare("/a/", "/a/") != 0);
+    ON(ne_path_compare("/alpha/", "/beta/") == 0);
+    ON(ne_path_compare("/alpha", "/b") == 0);
+    ON(ne_path_compare("/alpha/", "/alphash") == 0);
+    ON(ne_path_compare("/fish/", "/food") == 0);
+    ON(ne_path_compare(alpha, alpha) != 0);
+    ON(ne_path_compare("/a/b/c/d", "/a/b/c/") == 0);
     return OK;
 }
 
-static int compares(void)
+/* Checks that a URI comparison of 'u1' and 'u2', which have differing
+ * 'field', doesn't compare to equal; and that they are ordered
+ * correctly. */
+static int cmp_differ(const char *field,
+                     const ne_uri *u1, const ne_uri *u2)
 {
-    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);
+    ONV(ne_uri_cmp(u1, u2) == 0,
+       ("URIs with different %s were equal", field));
+
+    ONV(ne_uri_cmp(u2, u1) == 0,
+       ("URIs with different %s were equal (reversed)", field));
+
+    /* relies on strcmp return value being of equal magnitude when
+     * arguments are reversed; not sure if this is portable
+     * assumption. */
+    ONV(ne_uri_cmp(u1, u2) + ne_uri_cmp(u2, u1) != 0,
+       ("relative ordering of URIs with different %s incorrect", field));
+
+    return OK;
+}
+
+static int cmp(void)
+{
+    ne_uri alpha, beta;
+    
+    alpha.path = "/alpha";
+    alpha.scheme = "http";
+    alpha.host = "example.com";
+    alpha.port = 80;
+
+    beta = alpha; /* structure copy. */
+
+    ONN("equal URIs not equal", ne_uri_cmp(&alpha, &beta) != 0);
+
+    beta.path = "/beta";
+    CALL(cmp_differ("path", &alpha, &beta));
+
+    beta = alpha; beta.scheme = "https";
+    CALL(cmp_differ("scheme", &alpha, &beta));
+    
+    beta = alpha; beta.port = 433;
+    CALL(cmp_differ("port", &alpha, &beta));
+
+    beta = alpha; beta.host = "fish.com";
+    CALL(cmp_differ("host", &alpha, &beta));
+
+    beta = alpha; beta.host = "EXAMPLE.CoM";
+    ONN("hostname comparison not case-insensitive",
+       ne_uri_cmp(&alpha, &beta) != 0);
+    
+    beta = alpha; beta.scheme = "HtTp";
+    ONN("scheme comparison not case-insensitive",
+       ne_uri_cmp(&alpha, &beta) != 0);
+    
+    beta = alpha; beta.path = ""; alpha.path = "/";
+    ONN("empty abspath doesn't match '/'",
+       ne_uri_cmp(&alpha, &beta) != 0);
+    ONN("'/' doesn't match empty abspath",
+       ne_uri_cmp(&beta, &alpha) != 0);
+
+    beta = alpha; alpha.path = ""; beta.path = "/foo";
+    ONN("empty abspath matched '/foo'", ne_uri_cmp(&alpha, &beta) == 0);
+    ONN("'/foo' matched empty abspath ", ne_uri_cmp(&beta, &alpha) == 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);
+    ON(ne_path_childof("/a", "/a/b") == 0);
+    ON(ne_path_childof("/a/", "/a/b") == 0);
+    ON(ne_path_childof("/aa/b/c", "/a/b/c/d/e") != 0);
+    ON(ne_path_childof("////", "/a") != 0);
     return OK;
 }
 
 static int slash(void)
 {
-    ON(uri_has_trailing_slash("/a/") == 0);
-    ON(uri_has_trailing_slash("/a") != 0);
+    ON(ne_path_has_trailing_slash("/a/") == 0);
+    ON(ne_path_has_trailing_slash("/a") != 0);
     {
        /* check the uri == "" case. */
        char *foo = "/";
-       ON(uri_has_trailing_slash(&foo[1]));
+       ON(ne_path_has_trailing_slash(&foo[1]));
     }
     return OK;
 }
 
 static int just_hostname(void)
 {
-    struct uri p = {0};
-    ON(uri_parse("host.name.com", &p, NULL));
+    ne_uri p = {0};
+    ON(ne_uri_parse("host.name.com", &p));
     ON(strcmp(p.host, "host.name.com"));
+    ne_uri_free(&p);
     return 0;
 }
 
 static int just_path(void)
 {
-    struct uri p = {0};
-    ON(uri_parse("/argh", &p, NULL));
+    ne_uri p = {0};
+    ON(ne_uri_parse("/argh", &p));
     ON(strcmp(p.path, "/argh"));
+    ne_uri_free(&p);
     return 0;
 }
 
-static int null_uri(void) {
-    struct uri p = {0};
-    ON(uri_parse("", &p, NULL) == 0);
+static int default_port(void)
+{
+    ONN("default http: port incorrect", ne_uri_defaultport("http") != 80);
+    ONN("default https: port incorrect", ne_uri_defaultport("https") != 443);
+    ONN("unspecified scheme: port incorrect", ne_uri_defaultport("ldap") != 0);
+    return OK;
+}
+
+static int parse(void)
+{
+    static const struct test_uri {
+       const char *uri, *scheme, *host;
+       unsigned int port;
+       const char *path, *authinfo;
+    } uritests[] = {
+       { "http://webdav.org/norman", "http", "webdav.org", 0, "/norman", NULL },
+       { "http://webdav.org:/norman", "http", "webdav.org", 0, "/norman", NULL },
+       { "https://webdav.org/foo", "https", "webdav.org", 0, "/foo", NULL },
+       { "http://webdav.org:8080/bar", "http", "webdav.org", 8080, "/bar", NULL },
+       { "http://a/b", "http", "a", 0, "/b", NULL },
+       { "http://webdav.org/bar:fish", "http", "webdav.org", 0, "/bar:fish", NULL },
+       { "http://webdav.org", "http", "webdav.org", 0, "/", NULL },
+        { "http://webdav.org/fish@food", "http", "webdav.org", 0, "/fish@food", NULL },
+        /* authinfo */
+        { "ftp://jim:bob@jim.com", "ftp", "jim.com", 0, "/", "jim:bob" },
+        { "ldap://fred:bloggs@fish.com/foobar", "ldap", "fish.com", 0, 
+          "/foobar", "fred:bloggs" },
+       /* relativeURIs accepted for dubious legacy reasons. */
+       { "a/b", NULL, "a", 0, "/b", NULL },
+       { "a:8080/b", NULL, "a", 8080, "/b", NULL },
+       { "/fish", NULL, NULL, 0, "/fish", NULL },
+       { "webdav.org:8080", NULL, "webdav.org", 8080, "/", NULL },
+       /* IPv6 hex strings allowed even if IPv6 not supported. */
+       { "http://[::1]/foo", "http", "[::1]", 0, "/foo", NULL },
+       { "http://[a:a:a:a::0]/foo", "http", "[a:a:a:a::0]", 0, "/foo", NULL },
+       { "http://[::1]:8080/bar", "http", "[::1]", 8080, "/bar", NULL },
+       { "ftp://[feed::cafe]:555", "ftp", "[feed::cafe]", 555, "/", NULL },
+       { "http://fish/[foo]/bar", "http", "fish", 0, "/[foo]/bar", NULL },
+       /* and some dubious ones: */
+       { "[::1]/foo", NULL, "[::1]", 0, "/foo", NULL },
+       { "[::1]:8000/foo", NULL, "[::1]", 8000, "/foo", NULL },
+       { NULL }
+    };
+    int n;
+
+    for (n = 0; uritests[n].uri != NULL; n++) {
+       ne_uri res;
+       const struct test_uri *e = &uritests[n];
+       ONV(ne_uri_parse(e->uri, &res) != 0,
+           ("%s: parse failed", e->uri));
+       ONV(res.port != e->port,
+           ("%s: parsed port was %d not %d", e->uri, res.port, e->port));
+       ONCMP(e->scheme, res.scheme, e->uri, "scheme");
+       ONCMP(e->host, res.host, e->uri, "host");
+       ONV(strcmp(res.path, e->path),
+           ("%s: parsed path was %s not %s", e->uri, res.path, e->path));
+        ONCMP(e->authinfo, res.authinfo, e->uri, "authinfo");
+       ne_uri_free(&res);
+    }
+
+    return OK;
+}
+
+static int failparse(void)
+{
+    static const char *uris[] = {
+       "",
+       "http://[::1/",
+       NULL
+    };
+    int n;
+    
+    for (n = 0; uris[n] != NULL; n++) {
+       ne_uri p;
+       ONV(ne_uri_parse(uris[n], &p) == 0,
+           ("`%s' did not fail to parse", uris[n]));
+       ne_uri_free(&p);
+    }
+
     return 0;
 }
 
-test_func tests[] = {
-    simple,
-    simple_ssl,
-    authinfo,
-    no_path,
-    escapes,
-    parents,
-    abspath,
-    compares,
-    children,
-    slash,
-    just_hostname,
-    just_path,
-    null_uri,
-    NULL
+static int unparse(void)
+{
+    const char *uris[] = {
+       "http://foo.com/bar",
+       "https://bar.com/foo/wishbone",
+       "http://www.random.com:8000/",
+       "http://[::1]:8080/",
+       "ftp://ftp.foo.bar/abc/def",
+       NULL
+    };
+    int n;
+
+    for (n = 0; uris[n] != NULL; n++) {
+       ne_uri parsed;
+       char *unp;
+
+       ONV(ne_uri_parse(uris[n], &parsed),
+           ("failed to parse %s", uris[n]));
+       
+        if (parsed.port == 0)
+            parsed.port = ne_uri_defaultport(parsed.scheme);
+
+       unp = ne_uri_unparse(&parsed);
+
+       ONV(strcmp(unp, uris[n]),
+           ("unparse got %s from %s", unp, uris[n]));
+       
+        ne_uri_free(&parsed);
+       ne_free(unp);
+    }
+
+    return OK;    
+}
+
+ne_test tests[] = {
+    T(simple),
+    T(simple_ssl),
+    T(no_path),
+    T(escapes),
+    T(parents),
+    T(compares),
+    T(cmp),
+    T(children),
+    T(slash),
+    T(just_hostname),
+    T(just_path),
+    T(default_port),
+    T(parse),
+    T(failparse),
+    T(unparse),
+    T(NULL)
 };
index 284f46b..464f338 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    utils tests
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2004, 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
 #include <string.h>
 #endif
 
-#include "http_utils.h"
-#include "neon_md5.h"
-#include "base64.h"
+#include "ne_utils.h"
+#include "ne_md5.h"
+#include "ne_alloc.h"
+#include "ne_dates.h"
+#include "ne_string.h"
+
 #include "tests.h"
 
 static const struct {
@@ -44,8 +47,12 @@ static const struct {
     { "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" },
+    { "HTTP/1.1 100 ", 1, 1, 100, "" },
+
     /* these aren't really valid but we should be able to parse them. */
+    { "HTTP/1.1 100", 1, 1, 100, "" },
     { "HTTP/1.1   200   OK", 1, 1, 200, "OK" },
+    { "HTTP/1.1   200 \t  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 }
@@ -55,85 +62,214 @@ static const char *bad_sl[] = {
     "",
     "HTTP/1.1 1000 OK",
     "HTTP/1.1 1000",
-    "HTTP/1.1 100",
+    "HTTP/-1.1 100 OK",
+    "HTTP/1.1 -100 OK",
     "HTTP/ 200 OK",
+    "HTTP/",
+    "HTTP/1.1A 100 OK",
+    "HTTP/1.",
+    "HTTP/1.1 1",
+    "Fish/1.1 100 OK",
+    "HTTP/1.1 10",
+    "HTTP",
+    "H\0TP/1.1 100 OK",
     NULL
 };  
 
 static int status_lines(void)
 {
-    http_status s;
+    ne_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));
+       ONV(ne_parse_statusline(accept_sl[n].status, &s),
+           ("valid #%d: parse", n));
+       ONV(accept_sl[n].major != s.major_version, ("valid #%d: major", n));
+       ONV(accept_sl[n].minor != s.minor_version, ("valid #%d: minor", n));
+       ONV(accept_sl[n].code != s.code, ("valid #%d: code", n));
+       ONV(strcmp(accept_sl[n].rp, s.reason_phrase), 
+           ("valid #%d: reason phrase", n));
+        ne_free(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);
+       ONV(ne_parse_statusline(bad_sl[n], &s) == 0, 
+           ("invalid #%d", n));
     }
 
     return OK;
 }
 
+/* Write MD5 of 'len' bytes of 'str' to 'digest' */
+static unsigned char *digest_md5(const char *data, size_t len, unsigned char digest[16])
+{
+    struct ne_md5_ctx ctx;
+
+#define CHUNK 100
+    ne_md5_init_ctx(&ctx);
+    /* exercise the buffering interface */
+    while (len > CHUNK) {
+        ne_md5_process_bytes(data, CHUNK, &ctx);
+        len -= CHUNK;
+        data += CHUNK;
+    }
+    ne_md5_process_bytes(data, len, &ctx);
+    ne_md5_finish_ctx(&ctx, digest);
+    
+    return digest;
+}
+
 static int md5(void)
 {
     unsigned char buf[17] = {0}, buf2[17] = {0};
     char ascii[33] = {0};
+    char zzzs[500];
 
-    ne_md5_to_ascii(ne_md5_buffer("", 0, buf), ascii);
+    ne_md5_to_ascii(digest_md5("", 0, buf), ascii);
     ONN("MD5(null)", strcmp(ascii, "d41d8cd98f00b204e9800998ecf8427e"));
     
-    ne_md5_to_ascii(ne_md5_buffer("foobar", 7, buf), ascii);
+    ne_md5_to_ascii(digest_md5("foobar", 7, buf), ascii);
     ONN("MD5(foobar)", strcmp(ascii, "b4258860eea29e875e2ee4019763b2bb"));
 
-    ne_ascii_to_md5(ascii, buf2);
+    /* $ perl -e 'printf "z"x500' | md5sum
+     * 8b9323bd72250ea7f1b2b3fb5046391a  - */
+    memset(zzzs, 'z', sizeof zzzs);
+    ne_md5_to_ascii(digest_md5(zzzs, sizeof zzzs, buf), ascii);
+    ONN("MD5(\"z\"x512)", strcmp(ascii, "8b9323bd72250ea7f1b2b3fb5046391a"));
 
+    ne_ascii_to_md5(ascii, buf2);
     ON(memcmp(buf, buf2, 16));
+    
+    return OK;
+}
+
+static int md5_alignment(void)
+{
+    char *bb = ne_malloc(66);
+    struct ne_md5_ctx ctx;
+
+    /* regression test for a bug in md5.c in <0.15.0 on SPARC, where
+     * the process_bytes function would SIGBUS if the buffer argument
+     * isn't 32-bit aligned. Won't trigger on x86 though. */
+    ne_md5_init_ctx(&ctx);
+    ne_md5_process_bytes(bb + 1, 65, &ctx);
+    ne_free(bb);
+
+    return OK;
+}
+
+static const struct {
+    const char *str;
+    time_t time;
+    enum { d_rfc1123, d_iso8601, d_rfc1036 } type;
+} good_dates[] = {
+    { "Fri, 08 Jun 2001 22:59:46 GMT", 992041186, d_rfc1123 },
+    { "Friday, 08-Jun-01 22:59:46 GMT", 992041186, d_rfc1036 },
+    { "Wednesday, 06-Jun-01 22:59:46 GMT", 991868386, d_rfc1036 },
+    /* some different types of ISO8601 dates. */
+    { "2001-06-08T22:59:46Z", 992041186, d_iso8601 },
+    { "2001-06-08T22:59:46.9Z", 992041186, d_iso8601 },
+    { "2001-06-08T26:00:46+03:01", 992041186, d_iso8601 },
+    { "2001-06-08T20:58:46-02:01", 992041186, d_iso8601 },
+    { NULL }
+};
+
+static int parse_dates(void)
+{
+    int n;
+
+    for (n = 0; good_dates[n].str != NULL; n++) {
+       time_t res;
+       const char *str = good_dates[n].str;
+
+       switch (good_dates[n].type) {
+       case d_rfc1036: res = ne_rfc1036_parse(str); break;
+       case d_iso8601: res = ne_iso8601_parse(str); break;
+       case d_rfc1123: res = ne_rfc1123_parse(str); break;
+       default: res = -1; break;
+       }
+       
+       ONV(res == -1, ("date %d parse", n));
+       
+#define FT "%" NE_FMT_TIME_T
+       ONV(res != good_dates[n].time, (
+           "date %d incorrect (" FT " not " FT ")", n,
+           res, good_dates[n].time));
+    }
 
-    return 0;
+    return OK;
 }
 
-static int base64(void)
+/* trigger segfaults in ne_rfc1036_parse() in <=0.24.5. */
+static int regress_dates(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");
+    static const char *dates[] = {
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    };
+    size_t n;
+    
+    for (n = 0; n < sizeof(dates)/sizeof(dates[0]); n++) {
+        ne_rfc1036_parse(dates[n]);
+        ne_iso8601_parse(dates[n]);
+        ne_rfc1123_parse(dates[n]);
+    }
+
+    return OK;
+}
+
+static int versioning(void)
+{
+#define GOOD(n,m,msg) ONV(ne_version_match(n,m), \
+("match of " msg " failed (%d.%d)", n, m))
+#define BAD(n,m,msg) ONV(ne_version_match(n,m) == 0, \
+("match of " msg " succeeded (%d.%d)", n, m))
+    GOOD(NEON_VERSION_MAJOR, NEON_VERSION_MINOR, "current version");
+    BAD(NEON_VERSION_MAJOR, NEON_VERSION_MINOR + 1, "later minor");
+    BAD(NEON_VERSION_MAJOR + 1, 0, "later major");
+#if NEON_VERSION_MINOR > 0
+    GOOD(NEON_VERSION_MAJOR, NEON_VERSION_MINOR - 1, "earlier minor");
+#endif
+#if NEON_VERSION_MAJOR > 0
+    BAD(NEON_VERSION_MAJOR - 1, 0, "earlier major");
 #endif
+#undef GOOD
+#undef BAD
+    return OK;
+}
+
+/* basic ne_version_string() sanity tests */
+static int version_string(void)
+{
+    char buf[1024];
+    
+    ne_snprintf(buf, sizeof buf, "%s", ne_version_string());
+    
+    NE_DEBUG(NE_DBG_HTTP, "Version string: %s\n", buf);
 
-#undef B64
+    ONN("version string too long", strlen(buf) > 200);
+    ONN("version string contained newline", strchr(buf, '\n') != NULL);
+
+    return OK;    
+}
+
+static int support(void)
+{
+#ifdef NEON_SSL
+    ONN("SSL support not advertised", !ne_supports_ssl());
+#else
+    ONN("SSL support advertised", ne_supports_ssl());
+#endif
     return OK;
 }
 
-test_func tests[] = {
-    status_lines,
-    md5,
-    base64,
-    NULL
+ne_test tests[] = {
+    T(status_lines),
+    T(md5),
+    T(md5_alignment),
+    T(parse_dates),
+    T(regress_dates),
+    T(versioning),
+    T(version_string),
+    T(support),
+    T(NULL)
 };
index 50dbdbe..c23860a 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    Utility functions for HTTP client tests
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2004, 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
 
 */
 
+#include "config.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for sleep() */
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_session.h"
+
 #include "child.h"
 #include "tests.h"
 #include "utils.h"
 
-int single_serve_string(nsocket *s, void *userdata)
+int make_session(ne_session **sess, server_fn fn, void *ud)
+{
+    *sess = ne_session_create("http", "localhost", 7777);
+    return spawn_server(7777, fn, ud);
+}
+
+static int serve_response(ne_socket *s, const char *response)
+{
+    CALL(discard_request(s));
+    CALL(discard_body(s));
+    ONN("failed to send response", SEND_STRING(s, response));
+    return OK;
+}    
+
+int single_serve_string(ne_socket *s, void *userdata)
 {
     const char *str = userdata;
-    char buf[1024];
+    return serve_response(s, str);
+}
 
-    ON(discard_request(s));
+int sleepy_server(ne_socket *sock, void *userdata)
+{
+    sleep(10);
+    return 0;
+}
+
+int many_serve_string(ne_socket *s, void *userdata)
+{
+    int n;
+    struct many_serve_args *args = userdata;
     
-    while (clength > 0) {
-       clength -= sock_read(s, buf, clength);
+    for (n = 0; n < args->count; n++) {
+       NE_DEBUG(NE_DBG_HTTP, "Serving response %d\n", n);
+       CALL(serve_response(s, args->str));
     }
 
-    ON(sock_send_string(s, str));
-
     return OK;
 }
 
+int any_request(ne_session *sess, const char *uri)
+{
+    ne_request *req = ne_request_create(sess, "GET", uri);
+    int ret = ne_request_dispatch(req);
+    ne_request_destroy(req);
+    return ret;
+}
+
+int any_2xx_request(ne_session *sess, const char *uri)
+{
+    ne_request *req = ne_request_create(sess, "GET", uri);
+    int ret = ne_request_dispatch(req);
+    ONV(ret != NE_OK || ne_get_status(req)->klass != 2,
+       ("request failed: %s\n", ne_get_error(sess)));
+    ne_request_destroy(req);
+    return ret;
+}
+
+int any_2xx_request_body(ne_session *sess, const char *uri)
+{
+    ne_request *req = ne_request_create(sess, "GET", uri);
+#define BSIZE 5000
+    char *body = memset(ne_malloc(BSIZE), 'A', BSIZE);
+    int ret;
+    ne_set_request_body_buffer(req, body, BSIZE);
+    ret = ne_request_dispatch(req);
+    ne_free(body);
+    ONV(ret != NE_OK || ne_get_status(req)->klass != 2,
+       ("request failed: %s\n", ne_get_error(sess)));
+    ne_request_destroy(req);
+    return ret;
+}
+
+int serve_sstring(ne_socket *sock, void *ud)
+{
+    struct string *str = ud;
+
+    NE_DEBUG(NE_DBG_SOCKET, "Serving string: [[[%.*s]]]\n",
+            (int)str->len, str->data);
+
+    ONN("write failed", ne_sock_fullwrite(sock, str->data, str->len));
+    
+    return 0;
+}
+
+int serve_sstring_slowly(ne_socket *sock, void *ud)
+{
+    struct string *str = ud;
+    size_t n;
+
+    NE_DEBUG(NE_DBG_SOCKET, "Slowly serving string: [[[%.*s]]]\n",
+            (int)str->len, str->data);
+    
+    for (n = 0; n < str->len; n++) {
+       ONN("write failed", ne_sock_fullwrite(sock, &str->data[n], 1));
+       minisleep();
+    }
+    
+    return 0;
+}
index 6484cf1..2b466b6 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    neon-specific test utils
-   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2001-2004, 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
 
 #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);
+#define ONREQ(x) do { int _ret = (x); if (_ret) { t_context("line %d: HTTP error:\n%s", __LINE__, ne_get_error(sess)); return FAIL; } } while (0);
 
+int single_serve_string(ne_socket *s, void *userdata);
+
+struct many_serve_args {
+    int count;
+    const char *str;
+};
+
+/* Serves args->str response args->count times down a single
+ * connection. */
+int many_serve_string(ne_socket *s, void *userdata);
+
+/* Run a request using URI on the session. */
+int any_request(ne_session *sess, const char *uri);
+
+/* Run a request using URI on the session; fail on a non-2xx response.
+ */
+int any_2xx_request(ne_session *sess, const char *uri);
+
+/* As above but with a request body. */
+int any_2xx_request_body(ne_session *sess, const char *uri);
+
+/* makes *session, spawns server which will run 'fn(userdata,
+ * socket)'.  sets error context if returns non-zero, i.e use like:
+ * CALL(make_session(...)); */
+int make_session(ne_session **sess, server_fn fn, void *userdata);
+
+/* Server which sleeps for 10 seconds then closes the socket. */
+int sleepy_server(ne_socket *sock, void *userdata);
+
+struct string {
+    char *data;
+    size_t len;
+};
+
+/* Serve a struct string. */
+int serve_sstring(ne_socket *sock, void *ud);
+
+/* Serve a struct string slowly. */
+int serve_sstring_slowly(ne_socket *sock, void *ud);
 
 #endif /* UTILS_H */
index 10a2d88..9cea6c7 100644 (file)
@@ -1,6 +1,6 @@
 /* 
    neon test suite
-   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+   Copyright (C) 2002-2003, 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
 #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,
+/* A set of SAX handlers which serialize SAX events back into a 
+ * pseudo-XML-like string. */
+static int startelm(void *userdata, int state,
+                    const char *nspace, const char *name,
                    const char **atts)
 {
     ne_buffer *buf = userdata;
     int n;
 
-    ne_buffer_concat(buf, "<", "{", s->nspace, "}", s->name, NULL);
+    if (strcmp(name, "decline") == 0)
+        return NE_XML_DECLINE;
+
+    ne_buffer_concat(buf, "<", "{", nspace, "}", 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 state + 1;
+}
+
+static int chardata(void *userdata, int state, const char *cdata, size_t len)
+{
+    ne_buffer *buf = userdata;
+    ne_buffer_append(buf, cdata, len);
+    return !strncmp(cdata, "!ABORT!", len);
+}
+
+static int endelm(void *userdata, int state,
+                  const char *nspace, const char *name)
+{
+    ne_buffer *buf = userdata;
+    ne_buffer_concat(buf, "</{", nspace, "}", name, ">", NULL);
     return 0;
 }
 
-static int endelm(void *userdata, const struct ne_xml_elm *s,
-                 const char *cdata)
+/* A set of SAX handlers which do as above, but change some element
+ * names; used to check nested SAX handling is working properly.  */
+static int startelm_xform(void *userdata, int state,
+                          const char *nspace, const char *name,
+                          const char **atts)
+{
+    if (strcmp(nspace, "two") == 0)
+        return startelm(userdata, state, nspace, "xform", atts);
+    else
+        return NE_XML_DECLINE;
+}
+
+static int endelm_xform(void *userdata, int state,
+                        const char *nspace, const char *name)
+{
+    if (strcmp(nspace, "two") == 0)
+        return endelm(userdata, state, nspace, "xform");
+    else
+        return NE_XML_DECLINE;
+}
+
+/* A set of SAX handlers which verify that state handling is working
+ * correctly. */
+static int startelm_state(void *userdata, int parent,
+                          const char *nspace, const char *name,
+                          const char **atts)
+{
+    int n;
+
+    if (strcmp(nspace, "state") != 0)
+        return NE_XML_DECLINE;
+
+    for (n = 0; atts[n]; n += 2) {
+        if (strcmp(atts[n], "parent") == 0) {
+            int expected = atoi(atts[n+1]);
+            
+            if (expected != parent) {
+                char err[50];
+                sprintf(err, "parent state of %s was %d not %d", name, parent, 
+                    expected);
+                ne_buffer_zappend(userdata, err);
+            }
+        }
+    }
+
+    return atoi(name+1);    
+}
+
+static int endelm_state(void *userdata, int state,
+                        const char *nspace, const char *name)
 {
+    int expected = atoi(name + 1);
     ne_buffer *buf = userdata;
-    ne_buffer_concat(buf, cdata?cdata:"NOCDATA", "</{", s->nspace, "}",
-                    s->name, ">", NULL);
+    
+    if (state != expected)
+        ne_buffer_concat(buf, "wrong state in endelm of ", name, NULL);
+    
     return 0;
 }
 
-static int parse_match(struct ne_xml_elm *elms,
-                      const char *doc, const char *result)
+/* A set of SAX handlers which verify that abort handling is working
+ * correctly. */
+static int startelm_abort(void *buf, int parent,
+                          const char *nspace, const char *name,
+                          const char **atts)
+{
+    if (strcmp(name, "abort-start") == 0) {
+        ne_buffer_zappend(buf, "ABORT");
+        return NE_XML_ABORT;
+    } else
+        return startelm(buf, parent, nspace, name, atts);
+}
+
+static int endelm_abort(void *buf, int state,
+                        const char *nspace, const char *name)
+{
+    if (strcmp(name, "abort-end") == 0) {
+        ne_buffer_zappend(buf, "ABORT");
+        return -1;
+    } else
+        return 0;
+}
+
+enum match_type {
+    match_valid = 0,
+    match_invalid,
+    match_nohands,
+    match_encoding
+};
+
+static int parse_match(const char *doc, const char *result, enum match_type t)
 {
     ne_xml_parser *p = ne_xml_create();
     ne_buffer *buf = ne_buffer_create();
 
-    ne_xml_push_handler(p, elms, validelm, startelm, endelm, buf);
+    if (t == match_invalid)
+        ne_xml_push_handler(p, startelm_abort, chardata, endelm_abort, buf);
+    if (t != match_encoding && t != match_nohands) {
+        ne_xml_push_handler(p, startelm_state, NULL, endelm_state, buf);
+        ne_xml_push_handler(p, startelm, chardata, endelm, buf);
+        ne_xml_push_handler(p, startelm_xform, chardata, endelm_xform, buf);
+    }
 
     ne_xml_parse(p, doc, strlen(doc));
+    ne_xml_parse(p, "", 0);
     
-    ONN("parse failed", !ne_xml_valid(p));
+    if (t == match_invalid)
+        ONV(ne_xml_valid(p), ("parse did not fail: %s", buf->data));
+    else
+        ONV(!ne_xml_valid(p), ("parse failed: %s", ne_xml_get_error(p)));
     
-    ONV(strcmp(result, buf->data),
-       ("result mismatch: %s not %s", buf->data, result));
-
+    if (t == match_encoding) {
+        const char *enc = ne_xml_doc_encoding(p);
+        ONV(strcmp(enc, result), ("encoding was `%s' not `%s'", enc, result));
+    } else if (t == match_valid)
+        ONV(strcmp(result, buf->data),
+            ("result mismatch: %s not %s", buf->data, result));
+        
     ne_xml_destroy(p);
     ne_buffer_destroy(buf);
 
@@ -89,49 +197,265 @@ static int parse_match(struct ne_xml_elm *elms,
 
 static int matches(void)
 {
-#define PFX "<?xml version='1.0'?>\r\n"
+#define PFX "<?xml version='1.0' encoding='utf-8'?>\r\n"
 #define E(ns, n) "<{" ns "}" n "></{" ns "}" n ">"
     static const struct {
        const char *in, *out;
-       int flags;
+        enum match_type invalid;
     } ms[] = {
-       { PFX "<hello/>", "<{}hello></{}hello>", 0 },
+        
+        /*** Simplest tests ***/
+       { PFX "<hello/>", "<{}hello></{}hello>"},
        { 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. */
+         "<{}hello foo='bar'></{}hello>"},
+
+       /*** Tests for character data handling. ***/
+       { PFX "<hello> world</hello>", "<{}hello> world</{}hello>"},
+       /* test for cdata between elements. */
        { PFX "<hello>\r\n<wide>  world</wide></hello>",
-         "<{}hello><{}wide>  world</{}wide></{}hello>", 0 },
-       /*** namespace handling. */
+         "<{}hello>\n<{}wide>  world</{}wide></{}hello>"},
+
+       /*** Tests for 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 }
+         "<{}widget " NSA ">" E("bar", "norman") "</{}widget>"},
+       { PFX "<widget " NSA " xmlns:abc='def' xmlns:g='z'>"
+          "<foo:norman/></widget>",
+         "<{}widget " NSA " xmlns:abc='def' xmlns:g='z'>" 
+          E("bar", "norman") "</{}widget>"},
+       /* empty namespace default takes precedence. */
+       { PFX "<widget xmlns='foo'><smidgen xmlns=''><norman/>"
+         "</smidgen></widget>",
+         "<{foo}widget xmlns='foo'><{}smidgen xmlns=''>" 
+         E("", "norman") 
+         "</{}smidgen></{foo}widget>" },
+        /* inherited empty namespace default */
+        { PFX "<bar xmlns='foo'><grok xmlns=''><fish/></grok></bar>",
+          "<{foo}bar xmlns='foo'><{}grok xmlns=''>" 
+          E("", "fish") "</{}grok></{foo}bar>" },
+
+       /* regression test for neon <= 0.23.5 with libxml2, where the
+        * "dereference entities" flag was not set by default. */
+       { PFX "<widget foo=\"no&amp;body\"/>",
+         "<{}widget foo='no&body'></{}widget>" },
+       { PFX "<widget foo=\"no&#x20;body\"/>",
+         "<{}widget foo='no body'></{}widget>" },
+
+        /* tests for declined branches */
+        { PFX "<hello><decline>fish</decline>"
+          "<world><decline/>yes</world>goodbye<decline/></hello>",
+          "<{}hello><{}world>yes</{}world>goodbye</{}hello>" },
+        { PFX 
+          "<hello><decline><nested>fish</nested>bar</decline><fish/></hello>",
+          "<{}hello>" E("", "fish") "</{}hello>" },
+        /* tests for nested SAX handlers */
+        { PFX "<hello xmlns='two'><decline/></hello>",
+          "<{two}hello xmlns='two'>" E("two", "xform") "</{two}hello>"},
+
+        /* tests for state handling */
+        { PFX "<a55 xmlns='state'/>", "" },
+        { PFX "<a777 xmlns='state' parent='0'/>", "" },
+        { PFX "<a99 xmlns='state'><f77 parent='99'/>blah</a99>", "" },
+
+        /* tests for abort handling */
+        { PFX "<hello><merry><abort-start/></merry></hello>",
+          "<{}hello><{}merry>ABORT", match_invalid },
+        { PFX "<hello><merry><abort-end/>fish</merry></hello>",
+          "<{}hello><{}merry><{}abort-end>ABORT", match_invalid },
+        { PFX "<hello>!ABORT!</hello>", "<{}hello>!ABORT!", match_invalid },
+        { PFX "<hello>!ABORT!<foo/></hello>", "<{}hello>!ABORT!", match_invalid },
+        { PFX "<hello>!ABORT!</fish>", "<{}hello>!ABORT!", match_invalid },
+
+        /* tests for encodings */
+        { "<?xml version='1.0' encoding='ISO-8859-1'?><hello/>",
+          "ISO-8859-1", match_encoding },
+
+        { "<?xml version='1.0' encoding='UTF-8'?><hello/>",
+          "UTF-8", match_encoding },
+
+        /* test that parse is valid even with no handlers registered. */
+        { PFX "<hello><old>world</old></hello>", "", match_nohands },
+
+        /* regression test for prefix matching bug fixed in 0.18.0 */
+#define THENS "xmlns:d='foo' xmlns:dd='bar'"
+        { PFX "<d:hello " THENS "/>",
+          "<{foo}hello " THENS "></{foo}hello>" },
+
+        /**** end of list ****/ { NULL, 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));
+       CALL(parse_match(ms[n].in, ms[n].out, ms[n].invalid));
     }
 
     return OK;
 }
-       
+
+static int mapping(void)
+{
+    static const struct ne_xml_idmap map[] = {
+        { "fee", "bar", 1 },
+        { "foo", "bar", 2 },
+        { "bar", "foo", 3 },
+        { "", "bob", 4 },
+        { "balloon", "buffoon", 5},
+        { NULL, NULL, 0}
+    };
+    int n;
+
+    for (n = 0; map[n].id; n++) {
+        int id = ne_xml_mapid(map, NE_XML_MAPLEN(map) - 1,
+                              map[n].nspace, map[n].name);
+        ONV(id != map[n].id, ("mapped to id %d not %d", id, map[n].id));
+    }
+
+    n = ne_xml_mapid(map, NE_XML_MAPLEN(map) - 1, "no-such", "element");
+    ONV(n != 0, ("unknown element got id %d not zero", n));
+
+    return OK;
+}
+
+/* Test for some parse failures */
+static int fail_parse(void)
+{
+    static const char *docs[] = {
+        "foo",
+        PFX "<bar:foo/>",
+        /* malformed namespace declarations */
+        PFX "<foo xmlns:D=''/>",
+        PFX "<foo xmlns:='fish'/>",
+        PFX "<foo: xmlns:foo='bar'/>",
+#if 0
+        /* 2-byte encoding of '.': */
+        PFX "<foo>" "\x2F\xC0\xAE\x2E\x2F" "</foo>",
+        /* 3-byte encoding of '.': */
+        PFX "<foo>" "\x2F\xE0\x80\xAE\x2E\x2F" "</foo>",
+        /* 4-byte encoding of '.': */
+        PFX "<foo>" "\x2F\xF0\x80\x80\xAE\x2E\x2F" "</foo>",
+        /* 5-byte encoding of '.': */
+        PFX "<foo>" "\x2F\xF8\x80\x80\x80\xAE\x2E\x2F" "</foo>",
+        /* 6-byte encoding of '.': */
+        PFX "<foo>" "\x2F\xFC\x80\x80\x80\x80\xAE\x2E\x2F" "</foo>",
+        /* two-byte encoding of '<' must not be parsed as a '<': */
+        PFX "\xC0\xBC" "foo></foo>",
+#endif
+        NULL
+    };
+    int n;
+
+    for (n = 0; docs[n]; n++) {
+        ne_xml_parser *p = ne_xml_create();
+        const char *err;
+
+        ne_xml_parse(p, docs[n], strlen(docs[n]));
+        ne_xml_parse(p, "", 0);
+        ONV(ne_xml_valid(p), ("`%s' was valid", docs[n]));
+
+        err = ne_xml_get_error(p);
+        ONV(strstr(err, "parse error") == NULL,
+            ("bad error %s", err));
+        
+        ne_xml_destroy(p);
+    }
+
+    return OK;
+}
+
+static int check_attrib(ne_xml_parser *p, const char **atts, 
+                        const char *nspace, const char *name,
+                        const char *value)
+{
+    const char *act = ne_xml_get_attr(p, atts, nspace, name);
+    char err[50];
+    int ret = 0;
+
+    if (value == NULL) {
+        if (act != NULL) {
+            sprintf(err, "attribute %s was set to %s", name, act);
+            ret = NE_XML_ABORT;
+        }
+    } else {
+        if (act == NULL) {
+            sprintf(err, "attribute %s not found", name);
+            ret = NE_XML_ABORT;
+        } else if (strcmp(act, value) != 0) {
+            sprintf(err, "attribute %s was %s not %s", name, act, value);
+            ret = NE_XML_ABORT;
+        }
+    }
+    if (ret == NE_XML_ABORT) ne_xml_set_error(p, err);
+    return ret;
+}
+
+static int startelm_attrib(void *userdata, int state,
+                           const char *nspace, const char *name,
+                           const char **atts)
+{
+    ne_xml_parser *p = userdata;
+    
+    if (strcmp(name, "hello") == 0) {
+        CALL(check_attrib(p, atts, NULL, "first", "second"));
+        CALL(check_attrib(p, atts, NULL, "third", ""));
+        CALL(check_attrib(p, atts, "garth", "bar", "asda"));
+        CALL(check_attrib(p, atts, "giraffe", "bar", NULL));
+        CALL(check_attrib(p, atts, "hot", "dog", NULL));
+        CALL(check_attrib(p, atts, NULL, "nonesuch", NULL));
+    } else if (strcmp(name, "goodbye") == 0) {
+        if (atts[0] != NULL) {
+            ne_xml_set_error(p, "non-empty attrib array");
+            return 1;
+        }
+    }
+
+    return 1;
+}
+
+static int attributes(void)
+{
+    ne_xml_parser *p = ne_xml_create();
+    static const char doc[] = PFX
+        "<hello xmlns:foo='garth' first='second' third='' "
+        "foo:bar='asda' goof:bar='jar'><goodbye/></hello>";
+
+    ne_xml_push_handler(p, startelm_attrib, NULL, NULL, p);
+
+    ne_xml_parse_v(p, doc, strlen(doc));
+
+    ONV(!ne_xml_valid(p), ("parse error: %s", ne_xml_get_error(p)));
+
+    ne_xml_destroy(p);
+    return OK;
+}
+
+/* Test for the get/set error interface */
+static int errors(void)
+{
+    ne_xml_parser *p = ne_xml_create();
+    const char *err;
+    
+    ONV(strcmp(ne_xml_get_error(p), "Unknown error") != 0,
+        ("initial error string unspecified"));
+
+    ne_xml_set_error(p, "Fish food");
+    err = ne_xml_get_error(p);
+    
+    ONV(strcmp(err, "Fish food"), ("wrong error %s!", err));
+
+    ne_xml_destroy(p);
+    return 0;        
+}
+
 ne_test tests[] = {
     T(matches),
-
+    T(mapping),
+    T(fail_parse),
+    T(attributes),
+    T(errors),
     T(NULL)
 };