summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2004-10-02 18:47:02 +0000
committerjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2004-10-02 18:47:02 +0000
commit0294ff3d3282d1b1c5497f00ea25e5e55e6f4338 (patch)
tree978af6f81c7b7715597871b1e89a9ad083907f1a
downloadneon-0294ff3d3282d1b1c5497f00ea25e5e55e6f4338.tar.gz
Import neon 0.24.0 to begin 0.24.x branch.
git-svn-id: http://svn.webdav.org/repos/projects/neon/branches/0.24.x@243 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845
-rw-r--r--.cvsignore22
-rw-r--r--.package1
-rwxr-xr-x.release.sh29
-rw-r--r--AUTHORS5
-rw-r--r--BUGS37
-rw-r--r--ChangeLog666
-rw-r--r--INSTALL.win32130
-rw-r--r--Makefile.in158
-rw-r--r--NEWS873
-rw-r--r--README45
-rw-r--r--THANKS45
-rw-r--r--TODO121
-rwxr-xr-xautogen.sh24
-rw-r--r--config.hw.in59
-rw-r--r--configure.in152
-rw-r--r--doc/.cvsignore13
-rw-r--r--doc/TODO156
-rw-r--r--doc/fdl.sgml466
-rw-r--r--doc/feat.xml78
-rw-r--r--doc/html.xsl70
-rw-r--r--doc/manual.css42
-rw-r--r--doc/manual.xml167
-rw-r--r--doc/parsing-xml.txt170
-rw-r--r--doc/ref/alloc.xml88
-rw-r--r--doc/ref/auth.xml114
-rw-r--r--doc/ref/buf.xml53
-rw-r--r--doc/ref/bufapp.xml89
-rw-r--r--doc/ref/bufcr.xml60
-rw-r--r--doc/ref/bufdest.xml81
-rw-r--r--doc/ref/bufutil.xml62
-rw-r--r--doc/ref/clicert.xml153
-rw-r--r--doc/ref/config.xml124
-rw-r--r--doc/ref/err.xml66
-rw-r--r--doc/ref/getst.xml63
-rw-r--r--doc/ref/iaddr.xml124
-rw-r--r--doc/ref/init.xml55
-rw-r--r--doc/ref/neon.xml145
-rw-r--r--doc/ref/opts.xml126
-rw-r--r--doc/ref/req.xml169
-rw-r--r--doc/ref/reqbody.xml69
-rw-r--r--doc/ref/reqhdr.xml63
-rw-r--r--doc/ref/resolve.xml145
-rw-r--r--doc/ref/sess.xml123
-rw-r--r--doc/ref/shave.xml51
-rw-r--r--doc/ref/sslca.xml81
-rw-r--r--doc/ref/sslcert.xml66
-rw-r--r--doc/ref/ssldname.xml66
-rw-r--r--doc/ref/sslvfy.xml140
-rw-r--r--doc/ref/status.xml74
-rw-r--r--doc/ref/tok.xml76
-rw-r--r--doc/ref/vers.xml63
-rw-r--r--doc/refentry.xml56
-rw-r--r--doc/using-neon.txt166
-rw-r--r--doc/using.xml119
-rw-r--r--doc/xml.xml207
-rwxr-xr-xinstall-sh250
-rw-r--r--macros/ChangeLog929
-rw-r--r--macros/neon-test.m443
-rw-r--r--macros/neon-xml-parser.m4144
-rw-r--r--macros/neon.m4850
-rw-r--r--macros/socklen-arg-type.m443
-rw-r--r--neon-config.in105
-rw-r--r--neon.mak186
-rw-r--r--neon.pc.in10
-rw-r--r--src/.cvsignore15
-rw-r--r--src/COPYING.LIB482
-rw-r--r--src/ChangeLog4929
-rw-r--r--src/Makefile.in147
-rw-r--r--src/README15
-rw-r--r--src/memleak.h55
-rw-r--r--src/ne_207.c345
-rw-r--r--src/ne_207.h93
-rw-r--r--src/ne_acl.c130
-rw-r--r--src/ne_acl.h56
-rw-r--r--src/ne_alloc.c208
-rw-r--r--src/ne_alloc.h58
-rw-r--r--src/ne_auth.c1026
-rw-r--r--src/ne_auth.h62
-rw-r--r--src/ne_basic.c554
-rw-r--r--src/ne_basic.h140
-rw-r--r--src/ne_compress.c414
-rw-r--r--src/ne_compress.h44
-rw-r--r--src/ne_cookies.c135
-rw-r--r--src/ne_cookies.h51
-rw-r--r--src/ne_dates.c251
-rw-r--r--src/ne_dates.h54
-rw-r--r--src/ne_defs.h10
-rw-r--r--src/ne_i18n.c32
-rw-r--r--src/ne_i18n.h37
-rw-r--r--src/ne_locks.c819
-rw-r--r--src/ne_locks.h166
-rw-r--r--src/ne_md5.c432
-rw-r--r--src/ne_md5.h144
-rw-r--r--src/ne_openssl.c790
-rw-r--r--src/ne_private.h118
-rw-r--r--src/ne_privssl.h48
-rw-r--r--src/ne_props.c620
-rw-r--r--src/ne_props.h244
-rw-r--r--src/ne_redirect.c142
-rw-r--r--src/ne_redirect.h41
-rw-r--r--src/ne_request.c1389
-rw-r--r--src/ne_request.h275
-rw-r--r--src/ne_session.c272
-rw-r--r--src/ne_session.h195
-rw-r--r--src/ne_socket.c1040
-rw-r--r--src/ne_socket.h197
-rw-r--r--src/ne_ssl.h150
-rw-r--r--src/ne_string.c519
-rw-r--r--src/ne_string.h137
-rw-r--r--src/ne_stubssl.c135
-rw-r--r--src/ne_uri.c334
-rw-r--r--src/ne_uri.h85
-rw-r--r--src/ne_utils.c185
-rw-r--r--src/ne_utils.h127
-rw-r--r--src/ne_xml.c605
-rw-r--r--src/ne_xml.h140
-rw-r--r--test/.cvsignore41
-rw-r--r--test/COPYING339
-rw-r--r--test/ChangeLog1329
-rw-r--r--test/Makefile.in171
-rw-r--r--test/README39
-rw-r--r--test/STATUS74
-rw-r--r--test/acl.c115
-rw-r--r--test/auth.c315
-rw-r--r--test/basic.c228
-rw-r--r--test/common/ChangeLog222
-rw-r--r--test/common/README4
-rw-r--r--test/common/child.c454
-rw-r--r--test/common/child.h113
-rwxr-xr-xtest/common/run.sh19
-rw-r--r--test/common/tests.c318
-rw-r--r--test/common/tests.h125
-rw-r--r--test/compress.c216
-rw-r--r--test/cookies.c140
-rw-r--r--test/expired.pem20
-rw-r--r--test/htdocs/plain1
-rw-r--r--test/lock.c540
-rwxr-xr-xtest/makekeys.sh151
-rw-r--r--test/notvalid.pem20
-rw-r--r--test/openssl.conf77
-rw-r--r--test/props.c508
-rw-r--r--test/redirect.c189
-rw-r--r--test/request.c1649
-rw-r--r--test/resolve.c59
-rw-r--r--test/run.sh24
-rw-r--r--test/server.key9
-rw-r--r--test/session.c158
-rw-r--r--test/skeleton.c51
-rw-r--r--test/socket.c918
-rw-r--r--test/ssl.c1476
-rw-r--r--test/string-tests.c492
-rw-r--r--test/stubs.c171
-rw-r--r--test/uri-tests.c377
-rw-r--r--test/util-tests.c256
-rw-r--r--test/utils.c107
-rw-r--r--test/utils.h57
-rw-r--r--test/xml.c444
157 files changed, 39934 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..0522756
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,22 @@
+config.h.in
+config.h
+configure
+config.status
+config.log
+conftest.c
+Makefile
+aclocal.m4
+*.cache
+libtool
+neon-config
+reconf*
+confdefs.h
+ltmain.sh
+ltconfig
+config.sub
+config.guess
+*.out
+*.log
+.version
+config.hw
+neon.pc
diff --git a/.package b/.package
new file mode 100644
index 0000000..493f404
--- /dev/null
+++ b/.package
@@ -0,0 +1 @@
+announce-list=neon@webdav.org
diff --git a/.release.sh b/.release.sh
new file mode 100755
index 0000000..2a540ae
--- /dev/null
+++ b/.release.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+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
+
+for f in config.hw; do
+in=$f.in
+out=$f
+sed -e "s/@VERSION@/$version/g" \
+ -e "s/@MAJOR@/$major/g" \
+ -e "s/@MINOR@/$minor/g" \
+ -e "s/@RELEASE@/$release/g" < $in > $out
+done
+
+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
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..4f3c725
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+neon is Copyright (C) 1999-2003 Joe Orton <joe@manyfish.co.uk>
+Portions are:
+Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+Copyright (C) 1999-2000 Peter Boos <pedib@colorfullife.com>
+Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc.
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..f4c4750
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,37 @@
+
+Known problems/bugs in neon -*- text -*-
+---------------------------
+
+* 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.
+
+* test failures in `socket' on some non-Linux platforms:
+
+19. write_reset........... FAIL (write got 0 not reset)
+20. read_reset............ FAIL (read got -3 not reset)
+
+* server auth should not run on a CONNECT request; once a connection
+ has been CONNECT tunneled, subsequent requests through the tunnel
+ should not give proxy auth.
+
+* 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.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..27a656c
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,666 @@
+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.
+
+Wed May 10 19:16:43 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * example/nget.c (main): Allow output to stdout.
+
+Wed May 10 19:15:56 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * Makefile.in: Added shared, install* targets
+
+Wed May 10 17:47:30 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * example/nget.c (pretty_progress_bar): New function, from
+ cadaver.
+
+Wed May 10 14:42:45 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * example/nget.c: New file.
+
+Wed May 10 14:41:39 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * configure.in, Makefile.in, .cvsignore, install-sh: New files.
diff --git a/INSTALL.win32 b/INSTALL.win32
new file mode 100644
index 0000000..9fc5bdd
--- /dev/null
+++ b/INSTALL.win32
@@ -0,0 +1,130 @@
+Building neon on Windows uses a single Nmake neon.mak file. By
+placing various parameters on nmake's command line, you can specify
+exactly the features and behavior of the Neon libraries. The
+parameters are additive, so to add more features, add the command line
+options specified in the particular section below.
+
+All the builds described below should work with Microsoft VC++ 5 and
+6.
+
+Build neon
+__________
+
+This is the most basic version of the Neon library you can build. It
+does not require any third party libraries, but you do not get the
+full capabilities of Neon.
+
+Compile Neon with no parameters
+
+ nmake /f neon.mak
+
+After compiling the library, the directory contains libneon.lib,
+against which you can link your program.
+
+
+Build neon with WebDAV support
+______________________________
+
+To compile Neon with WebDAV support, Neon must compile and link
+against a third-party XML parser, either expat, expat-lite, libxml or
+libxml2. This Windows neon.mak file is designed to compile and link
+against the pre-built Expat Windows libraries version 1.95.X or newer.
+This library is available for download from
+
+ http://sourceforge.net/projects/expat/
+
+Download the latest expat_win32bin package named
+
+ expat_win32bin_X_YY_Z.exe
+
+and install it on your system. It wants to install itself into
+Q:\some\dir\Expat-X.Y.ZZ. Choose your installation location for expat
+and then compile Neon with
+
+ nmake /f neon.mak EXPAT_SRC=\path\to\Expat-X.YY.Z
+
+NOTE: When you run your program make sure the LIBEXPAT.DLL from expat
+is accessible, i.e. is in your PATH.
+
+This should work with Microsoft VC++ 5 and 6.
+
+
+Build neon with dynamically linked SSL support
+______________________________________________
+
+To build neon on Windows with SSL support you need OpenSSL already
+installed on your system (I used OpenSSL 0.9.6g). It can be
+downloaded from
+
+ http://www.openssl.org/source/openssl-0.9.6g.tar.gz
+
+After compiling OpenSSL, now simply point make to the OpenSSL sources:
+
+ nmake /f neon.mak OPENSSL_SRC=\path\to\openssl
+
+NOTE: The include files for OpenSSL reside in inc32/ directory
+("../openssl-0.9.6g/inc32").
+
+NOTE: Make sure that your program is linked against libeay32.lib and
+ssleay32.lib (normally in "../openssl-0.9.6g/out32dll") and that
+libeay32.dll and ssleay32.dll is accessible, i.e. is in your PATH.
+
+
+Build neon with statically linked OpenSSL support
+_________________________________________________
+
+If you want to statically link against OpenSSL, then add the
+OPENSSL_STATIC parameter.
+
+ nmake /f neon.mak OPENSSL_SRC=\path\to\openssl OPENSSL_STATIC=yes
+
+
+Build neon with statically linked Zlib support
+______________________________________________
+
+If you want to build Neon with the capability to decompress compressed
+content, then you need to compile against the Zlib library.
+
+Currently, the Neon's neon.mak file expects to compile and link a self
+compiled version of Zlib. You need Zlib 1.1.4 or greater. Zlib 1.1.3
+and older has a serious security issue.
+
+Here's how to compile Zlib.
+
+ 1) Get one of the Zlib source file packages in Zip format from
+ http://www.gzip.org/zlib/
+ 2) Unzip it.
+ 3) Get the package
+ http://www.gzip.org/zlib/contrib/zlib113-win32.zip
+ 4) Unzip it and copy the Makefile from this package to the Zlib
+ 1.1.4 or greater package.
+ 5) Run nmake in the Zlib 1.1.4 or greater directory.
+
+Now add the ZLIB_SRC parameter to Neon's neon.mak pointing to your
+newly compiled zlib.
+
+ nmake /f neon.mak ZLIB_SRC=\path\to\zlib
+
+
+Build neon with dynamically linked Zlib support
+_______________________________________________
+
+To build Neon with dynamically linked Zlib support, use the
+instructions for the statically linked Zlib support above and add the
+ZLIB_DLL parameter
+
+ nmake /f neon.mak ZLIB_SRC=\path\to\zlib ZLIB_DLL=yes
+
+
+Build neon with debugging support
+_________________________________
+
+Set the DEBUG_BUILD parameter
+
+ nmake /f neon.mak DEBUG_BUILD=yes
+
+It does not matter what value DEBUG_BUILD is set to, as long as it is
+not set to "".
+
+After compiling the library, the directory contains libneonD.lib,
+against which you can link your program.
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..1d07c7f
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,158 @@
+# Copyright (C) 2001-2002 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.
+
+# 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@
+mandir = @mandir@
+man1dir = $(mandir)/man1
+man3dir = $(mandir)/man3
+datadir = $(prefix)/share
+docdir = $(datadir)/doc/neon-@NEON_VERSION@
+includedir = @includedir@
+neonincludes = $(includedir)/neon
+pkgconfigdir = $(libdir)/pkgconfig
+
+top_srcdir = @top_srcdir@
+top_builddir = .
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+@SET_MAKE@
+
+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 --skip-validation
+
+# 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
+
+all: subdirs
+
+check: subdirs
+ cd test && $(MAKE) check
+
+# 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)
+
+subdirs:
+ cd src && $(MAKE)
+ @echo
+ @echo " Compilation complete. Run '$(MAKE) install' (as root?) to install neon."
+ @echo
+
+# 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
+
+docs-man:
+ rm -rf doc/man; mkdir doc/man
+ $(XMLTO) -o `pwd`/doc/man man doc/manual.xml
+
+docs-pdf:
+ $(XMLTO) -o `pwd`/doc pdf doc/manual.xml
+
+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 src/Makefile libtool config.log config.cache neon-config autom4te.cache test/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 $(DESTDIR)$(neonincludes)
+ @for h in $(DIST_HEADERS); do \
+ echo Installing $$h into $(DESTDIR)$(neonincludes); \
+ $(INSTALL_DATA) $(srcdir)/src/$$h $(DESTDIR)$(neonincludes)/$$h \
+ || exit 1; \
+ done
+
+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
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..03ea3d2
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,873 @@
+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.
diff --git a/README b/README
new file mode 100644
index 0000000..8097e1c
--- /dev/null
+++ b/README
@@ -0,0 +1,45 @@
+
+neon is an HTTP and WebDAV client library, with a C language API.
+Bindings for other languages may also be available, see the web site
+for more details.
+
+Mailing list: neon@webdav.org || Web site: http://www.webdav.org/neon/
+
+WARNING: THE NEON API IS NOT YET STABLE.
+
+Current features:
+
+ - High-level interface to HTTP and WebDAV methods.
+ - Low-level interface to HTTP request handling, to allow implementing
+ new methods easily.
+ - Persistent connection support (HTTP/1.1 and HTTP/1.0 aware)
+ - Basic and digest authentication (RFC2617) (including auth-int, md5-sess)
+ - Proxy support (including basic/digest authentication)
+ - SSL/TLS support using OpenSSL (including client certificate support)
+ - Generic WebDAV 207 XML response handling mechanism
+ - XML parsing using expat or libxml (1.x or 2.x) parser
+ - Easy generation of error messages from 207 error responses
+ - Basic HTTP/1.1 methods: GET, PUT, HEAD, OPTIONS, conditional PUT
+ - WebDAV resource manipulation: MOVE, COPY, DELETE, MKCOL.
+ - WebDAV metadata support: set and remove properties (PROPPATCH), query
+ any set of properties (PROPFIND).
+ - WebDAV locking support
+ - Autoconf macros supplied for easily embedding neon directly inside
+ an application source tree.
+
+Provides lower-level interfaces to directly implement new HTTP
+methods, and higher-level interfaces so that you don't have to worry
+about the lower-level stuff.
+
+neon is licensed under the GNU Library GPL; see src/COPYING.LIB for
+full details. The manual is licensed under the terms of the GNU FDL;
+see doc/fdl.sgml or the generated documentation. The autoconf macros
+in the "macros" directory are under a more liberal license, see each
+file for details. The test suite is licensed under the GNU GPL; see
+test/COPYING for full details.
+
+neon is Copyright (C) 1999-2003 Joe Orton
+Portions are:
+Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com>
+Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc.
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..43925d5
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,45 @@
+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>
+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>
+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>
+Rodney Dawes <dobey@ximian.com>
+Sam TH <sam@uchicago.edu>
+Sander Alberink <sander.alberink@cmg.nl>
+Sander Striker <striker@apache.org>
+Shane Mayer <shanemayer42@yahoo.com>
+Taisuke Yamada <tai@iij.ad.jp>
+Tom Bednarz <tombednarz@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>
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..e31a595
--- /dev/null
+++ b/TODO
@@ -0,0 +1,121 @@
+
+To Do List for neon -*- text -*-
+===================
+
+Please submit feature requests to <mailto:neon@webdav.org>
+
+For one-point-oh
+----------------
+
+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.
+
+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.
+
+44. Finer-grained connection status feedback, i.e., "Sent Request",
+ "Got response status-line"... "Reading response body"
+
+58. 2616 is quite strict about when to retry non-idempotent requests
+ and when not to. neon may not be compliant here.
+
+61. Make everything namespace-safe:
+ remove split_string/pair_string.
+
+62. Select which auth mechanisms are allowed, e.g. JUST SAY NO to
+ basic might very well be useful to some apps.
+
+63. Unconditionally turn off Nagle algorithm.
+
+Longer term
+-----------
+
+
+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.
+
+2. Add proper domain support to authentication code. (requires full
+ URI parsing support). Need to tell the auth layer the server
+ details.
+
+4. Better cnonce generation for authentication: use /dev/{u}random or
+ whatever like mod_auth_digest.
+
+6. PUT with ranges... ne_put_range
+
+9. DeltaV support (http://www.webdav.org/deltav/). See also the
+ subversion project (http://subversion.tigris.org/) who might build
+ a versioning system over DAV.
+
+10. ACL support (http://www.webdav.org/acl/)
+
+11. DASL support (http://www.webdav.org/dasl/). Xythos have server
+ support for this (www.sharemation.com). The UI is probably the
+ hardest problem here.
+ => Jim Whitehead's UCI postgrad team is working on this and
+ has written a DASL implementation.
+
+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
+ 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.
+
+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
+ 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.
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..cc95493
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+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
diff --git a/config.hw.in b/config.hw.in
new file mode 100644
index 0000000..1f09719
--- /dev/null
+++ b/config.hw.in
@@ -0,0 +1,59 @@
+/*
+ Win32 config.h
+ Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com>
+ Copyright (C) 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
+ 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
+
+*/
+#if defined(_WIN32) && !defined(WIN32)
+#define WIN32
+#endif
+
+#ifdef WIN32
+
+#define NEON_VERSION "@VERSION@"
+#define NEON_VERSION_MAJOR (@MAJOR@)
+#define NEON_VERSION_MINOR (@MINOR@)
+
+#define HAVE_ERRNO_H
+#define HAVE_LIMITS_H
+#define HAVE_STDLIB_H
+#define HAVE_STRING_H
+
+#define HAVE_MEMCPY
+
+#ifndef NEON_NODAV
+#define USE_DAV_LOCKS
+#endif
+
+#define NE_FMT_SIZE_T "u"
+#define NE_FMT_SSIZE_T "d"
+#define NE_FMT_OFF_T "ld"
+
+/* Win32 uses a underscore, so we use a macro to eliminate that. */
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strcasecmp strcmpi
+#define strncasecmp strnicmp
+#define ssize_t int
+#define inline __inline
+#define off_t _off_t
+
+#include <io.h>
+#define read _read
+
+#endif
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..c7a2f3d
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,152 @@
+dnl Require autoconf 2.53 for an AC_C_BIGENDIAN which supports
+dnl cross-compiling.
+AC_PREREQ(2.53)
+
+dnl Extract the version (sans LF) from .version, created at release-time.
+m4_define(ne_version, [esyscmd([echo -n `cat .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
+
+# 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_INSTALL
+
+AC_DISABLE_SHARED
+AC_PROG_LIBTOOL
+
+AC_EXEEXT
+
+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}"
+
+LIBNEON_SOURCE_CHECKS
+
+# Use the libtool-type build.
+NEON_LIBTOOL_BUILD
+# Checks to compile test suite
+NEON_TEST
+# 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])
+
+if test "$enable_memleak" = "yes"; then
+ CPPFLAGS="$CPPFLAGS -DNEON_MEMLEAK -I\$(top_srcdir)/src"
+ # disable 'make install'
+ ALLOW_INSTALL=memleak
+fi
+
+# 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.
+])
+
+case $ALLOW_INSTALL in
+memleak)
+ AC_MSG_NOTICE([Configured with development-only flags:
+
+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/doc/.cvsignore b/doc/.cvsignore
new file mode 100644
index 0000000..30791a1
--- /dev/null
+++ b/doc/.cvsignore
@@ -0,0 +1,13 @@
+*.?
+*.html
+*.pdf
+*.ps
+*.tex
+*.sgml
+*.junk
+html
+man
+man3
+man1
+version.xml
+date.xml
diff --git a/doc/TODO b/doc/TODO
new file mode 100644
index 0000000..febed30
--- /dev/null
+++ b/doc/TODO
@@ -0,0 +1,156 @@
+/* List of interfaces needing reference documentation. -*- c -*- */
+
+/* ne_session.h */
+
+### DONE: basics
+ne_session *ne_session_create(const char *scheme, const char *hostname, int port);
+void ne_session_destroy(ne_session *sess);
+void ne_close_connection(ne_session *sess);
+void ne_session_proxy(ne_session *sess, const char *hostname, int port);
+
+### DONE: error handling
+void ne_set_error(ne_session *sess, const char *format, ...);
+const char *ne_get_error(ne_session *sess);
+
+### DONE: options
+void ne_set_useragent(ne_session *sess, const char *product);
+void ne_set_expect100(ne_session *sess, int use_expect100);
+void ne_set_persist(ne_session *sess, int persist);
+void ne_set_read_timeout(ne_session *sess, int timeout);
+
+### TODO: progress + status callbcacks
+void ne_set_progress(ne_session *sess, ne_progress progress, void *userdata);
+
+### TODO: status callback
+typedef enum ne_conn_status;
+typedef void (*ne_notify_status)(void *userdata, ne_conn_status status, const char *info);
+void ne_set_status(ne_session *sess, ne_notify_status status, void *userdata);
+
+### DONE: SSL verification
+
+typedef struct ne_ssl_dname;
+char *ne_ssl_readable_dname(const ne_ssl_dname *dn);
+typedef struct ne_ssl_certificate;
+#define NE_SSL_*
+typedef int (*ne_ssl_verify_fn)(void *userdata, int failures,
+ const ne_ssl_certificate *cert);
+void ne_ssl_set_verify(ne_session *sess, ne_ssl_verify_fn fn, void *userdata);
+
+### DONE: SSL server certs
+int ne_ssl_load_ca(ne_session *sess, const char *file);
+int ne_ssl_load_default_ca(ne_session *sess);
+
+### TODO: SSL client certs
+typedef int (*ne_ssl_keypw_fn)(void *userdata, char *pwbuf, size_t len);
+void ne_ssl_keypw_prompt(ne_session *sess, ne_ssl_keypw_fn fn, void *ud);
+int ne_ssl_load_pkcs12(ne_session *sess, const char *fn);
+int ne_ssl_load_pem(ne_session *sess, const char *certfn, const char *keyfn);
+typedef void (*ne_ssl_provide_fn)(void *userdata, ne_session *sess,
+ const ne_ssl_dname *server);
+void ne_ssl_provide_ccert(ne_session *sess, ne_ssl_provide_fn fn, void *userdata);
+
+#ifdef NEON_SSL
+SSL_CTX *ne_ssl_get_context(ne_session *sess);
+X509 *ne_ssl_server_cert(ne_session *req);
+#endif
+
+### TODO: utility functions
+int ne_version_pre_http11(ne_session *sess);
+const char *ne_get_server_hostport(ne_session *sess);
+const char *ne_get_scheme(ne_session *sess);
+void ne_fill_server_uri(ne_session *sess, ne_uri *uri);
+
+/* end of ne_session.h *****************************************/
+
+/* ne_request.h */
+
+### DONE: request basics
+ne_request *ne_request_create(ne_session *sess, const char *method, const char *path);
+int ne_request_dispatch(ne_request *req);
+void ne_request_destroy(ne_request *req);
+
+### DONE: request status
+const ne_status *ne_get_status(ne_request *req);
+
+### TODO: request bodies
+void ne_set_request_body_buffer(ne_request *req, const char *buf, size_t count);
+int ne_set_request_body_fd(ne_request *req, int fd, size_t count);
+
+typedef ssize_t (*ne_provide_body)(void *userdata,
+ char *buffer, size_t buflen);
+void ne_set_request_body_provider(ne_request *req, size_t size,
+ ne_provide_body provider, void *userdata);
+
+### TODO: response bodies
+typedef int (*ne_accept_response)(void *userdata, ne_request *req, ne_status *st);
+int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st);
+int ne_accept_always(void *userdata, ne_request *req, ne_status *st);
+void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
+ ne_block_reader reader, void *userdata);
+
+### TODO: response headers
+typedef void (*ne_header_handler)(void *userdata, const char *value);
+void ne_add_response_header_handler(ne_request *req, const char *name,
+ ne_header_handler hdl, void *userdata);
+void ne_add_response_header_catcher(ne_request *req,
+ ne_header_handler hdl, void *userdata);
+
+void ne_duplicate_header(void *userdata, const char *value);
+void ne_handle_numeric_header(void *userdata, const char *value);
+
+### DONE: request headers
+void ne_add_request_header(ne_request *req, const char *name, const char *value);
+void ne_print_request_header(ne_request *req, const char *name, const char *format, ...);
+
+### TODO: misc
+ne_session *ne_get_session(ne_request *req);
+
+### TODO: caller-pulls request interface
+int ne_begin_request(ne_request *req);
+int ne_end_request(ne_request *req);
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);
+
+### TODO: hooks
+typedef void (*ne_free_hooks)(void *cookie);
+typedef void (*ne_create_request_fn)(void *userdata, ne_request *req,
+ const char *method, const char *path);
+void ne_hook_create_request(ne_session *sess, ne_create_request_fn fn, void *userdata);
+
+typedef void (*ne_pre_send_fn)(void *userdata, ne_buffer *header);
+void ne_hook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);
+
+typedef int (*ne_post_send_fn)(void *userdata, const ne_status *status);
+void ne_hook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata);
+
+typedef void (*ne_destroy_fn)(void *userdata);
+void ne_hook_destroy_request(ne_session *sess, ne_destroy_fn fn, void *userdata);
+
+void ne_hook_destroy_session(ne_session *sess, ne_destroy_fn fn, void *userdata);
+
+typedef void *(*ne_accessor_fn)(void *userdata);
+void ne_hook_session_accessor(ne_session *sess, const char *id, ne_accessor_fn, void *userdata);
+void ne_hook_request_accessor(ne_request *req, const char *id, ne_accessor_fn, void *userdata);
+
+void *ne_null_accessor(void *userdata);
+
+void *ne_session_hook_private(ne_session *sess, const char *id);
+
+void *ne_request_hook_private(ne_request *req, const char *id);
+
+/* ne_207.h */
+/* ne_acl.h */
+/* DONE: ne_alloc.h */
+/* DONE: ne_auth.h */
+/* ne_basic.h */
+/* ne_compress.h */
+/* ne_cookies.h */
+/* ne_dates.h */
+/* ne_locks.h */
+/* ne_props.h */
+/* ne_redirect.h */
+/* ne_socket.h */
+/* MOSTLY DONE: ne_string.h */
+/* ne_uri.h */
+/* ne_utils.h */
+/* ne_xml.h */
+
diff --git a/doc/fdl.sgml b/doc/fdl.sgml
new file mode 100644
index 0000000..37470db
--- /dev/null
+++ b/doc/fdl.sgml
@@ -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/doc/feat.xml b/doc/feat.xml
new file mode 100644
index 0000000..547ab9f
--- /dev/null
+++ b/doc/feat.xml
@@ -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>
diff --git a/doc/html.xsl b/doc/html.xsl
new file mode 100644
index 0000000..a96867d
--- /dev/null
+++ b/doc/html.xsl
@@ -0,0 +1,70 @@
+<?xml version='1.0'?>
+
+<!-- This file wraps around the DocBook HTML XSL stylesheet to customise
+ - some parameters; add the CSS stylesheet, etc.
+ -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
+
+<xsl:variable name="html.stylesheet">../manual.css</xsl:variable>
+
+<!-- for sections with id attributes, use the id in the filename.
+ This produces meaningful (and persistent) URLs for the sections. -->
+<xsl:variable name="use.id.as.filename" select="1"/>
+
+<!-- enable the shaded verbatim (programlisting etc) blocks. -->
+<!-- <xsl:variable name="shade.verbatim" select="1"/> -->
+
+<!-- add class="programlisting" attribute on the <pre>s from
+ <programlisting>s so that the CSS can style them nicely. -->
+<xsl:attribute-set name="shade.verbatim.style">
+ <xsl:attribute name="class">programlisting</xsl:attribute>
+</xsl:attribute-set>
+
+<!-- use sane ANSI C function prototypes -->
+<xsl:variable name="funcsynopsis.style" 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/doc/manual.css b/doc/manual.css
new file mode 100644
index 0000000..621feb5
--- /dev/null
+++ b/doc/manual.css
@@ -0,0 +1,42 @@
+
+p, pre.funcsynopsisinfo { margin-left: 0.4em; margin-right: 0.4em; }
+
+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.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: thin solid #777777;
+}
+
+h1.title { border-bottom: thick solid #bbf2bb; padding-bottom: 0.1em; }
+
+div.toc {
+ border-left: thick solid #bbf2bb; padding-left: 0.5em;
+ }
+
+h2, h3 { padding-left: 0.2em; padding-top: -0.1em; }
+
+h2 { background-color: #bbf2bb; font-size: 110%;
+ padding-bottom: 0.3em; padding-top: 0.2em; spacing-top: 0.1em; }
+
+h3 { border-bottom: thin solid #bbf2bb; }
diff --git a/doc/manual.xml b/doc/manual.xml
new file mode 100644
index 0000000..893f007
--- /dev/null
+++ b/doc/manual.xml
@@ -0,0 +1,167 @@
+<?xml version='1.0'?> <!-- -*- text -*- -->
+
+<!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.xml SYSTEM "xml.xml">
+<!ENTITY section.features SYSTEM "feat.xml">
+<!ENTITY section.using SYSTEM "using.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 refssldname SYSTEM "ref/ssldname.xml">
+<!ENTITY refsslca SYSTEM "ref/sslca.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>
+ <bookinfo>
+ <title>neon HTTP/WebDAV client library</title>
+ <author>
+ <firstname>Joe</firstname><surname>Orton</surname>
+ <affiliation>
+ <address><email>neon@webdav.org</email></address>
+ </affiliation>
+ </author>
+ <copyright><year>2001-2003</year><holder>Joe Orton</holder></copyright>
+
+ <legalnotice>
+ <para>Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.1
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, with no Front-Cover Texts,
+ and with no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".</para>
+ </legalnotice>
+
+ </bookinfo>
+
+ <chapter 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>
+
+ <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 id="api">
+ <title>The neon API for the C language</title>
+
+ &section.xml;
+
+ </chapter>
+
+ <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>
+
+ &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_certificate -->
+ &refssldname; <!-- ne_ssl_dname -->
+ &refsslca; <!-- 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>
+
+&fdl;
+
+</book>
diff --git a/doc/parsing-xml.txt b/doc/parsing-xml.txt
new file mode 100644
index 0000000..dc3e467
--- /dev/null
+++ b/doc/parsing-xml.txt
@@ -0,0 +1,170 @@
+
+Requirements for XML parsing in neon
+------------------------------------
+
+Before describing the interface given in neon for parsing XML, here
+are the requirements which it must satisfy:
+
+ 1. to support using either libxml or expat as the underlying parser
+ 2. to allow "independent" sections to handle parsing one XML
+ document
+ 3. to map element namespaces/names to an integer for easier
+ comparison.
+
+A description of requirement (2) is useful since it is the "hard"
+requirement, and adds most of the complexity of interface: WebDAV
+PROPFIND responses are made up of a large boilerplate XML
+
+ <multistatus><response><propstat><prop>...</prop></propstat> etc.
+
+neon should handle the parsing of these standard elements, and expose
+the meaning of the response using a convenient interface. But, within
+the <prop> elements, there may also be fragments of XML: neon can
+never know how to parse these, since they are property- and hence
+application-specific. The simplest example of this is the
+DAV:resourcetype property.
+
+So there is requirement (2) that two "independent" sections of code
+can handle the parsing of one XML document.
+
+Callback-based XML parsing
+--------------------------
+
+There are two ways of parsing XML documents commonly used:
+
+ 1. Build an in-memory tree of the document
+ 2. Use callbacks
+
+Where practical, using callbacks is more efficient than building a
+tree, so this is what neon uses. The standard interface for
+callback-based XML parsing is called SAX, so understanding the SAX
+interface is useful to understanding the XML parsing interface
+provided by neon.
+
+The SAX interface works by registering callbacks which are called *as
+the XML is parsed*. The most important callbacks are for 'start
+element' and 'end element'. For instance, if the XML document below is
+parsed by a SAX-like interface:
+
+ <hello>
+ <foobar></foobar>
+ </hello>
+
+Say we have registered callbacks "startelm" for 'start element' and
+"endelm" for 'end element'. Simplified somewhat, the callbacks will
+be called in this order, with these arguments:
+
+ 1. startelm("hello")
+ 2. startelm("foobar")
+ 3. endelm("foobar")
+ 4. endelm("hello")
+
+See the expat 'xmlparse.h' header for a more complete definition of a
+SAX-like interface.
+
+The hip_xml interface
+---------------------
+
+The hip_xml interface satisfies requirement (2) by introducing the
+"handler" concept. A handler is made up of these things:
+
+ - a set of XML elements
+ - a callback to validate an element
+ - a callback which is called when an element is opened
+ - a callback which is called when an element is closed
+ - (optionally, a callback which is called for CDATA)
+
+Registering a handler essentially says:
+
+ "If you encounter any of this set of elements, I want these
+ callbacks to be called."
+
+Handlers are kept in a STACK inside hip_xml. The first handler
+registered becomes the BASE of the stack, subsequent handlers are
+PUSHed on top.
+
+During XML parsing, the handler which is used for an XML element is
+recorded. When a new element is started, the search for a handler for
+this element begins at the handler used for the parent element, and
+carries on up the stack. For the root element, the search always
+starts at the BASE of the stack.
+
+A user's guide to hip_xml
+-------------------------
+
+The first thing to do when using hip_xml is to know what set of XML
+elements you are going to be parsing. This can usually be done by
+looking at the DTD provided for the documents you are going to be
+parsing. The DTD is also very useful in writing the 'validate'
+callback function, since it can tell you what parent/child pairs are
+valid, and which aren't.
+
+In this example, we'll parse XML documents which look like:
+
+<T:list-of-things xmlns:T="http://things.org/">
+ <T:a-thing>foo</T:a-thing>
+ <T:a-thing>bar</T:a-thing>
+</T:list-of-things>
+
+So, given the set of elements, declare the element id's and the
+element array:
+
+#define ELM_listofthings (HIP_ELM_UNUSED)
+#define ELM_a_thing (HIP_ELM_UNUSED + 1)
+
+const static struct my_elms[] = {
+ { "http://things.org/", "list-of-things", ELM_listofthings, 0 },
+ { "http://things.org/", "a-thing", ELM_a_thing, HIP_XML_CDATA },
+ { NULL }
+};
+
+This declares we know about two elements: list-of-things, and a-thing,
+and that the 'a-thing' element contains character data.
+
+The definition of the validation callback is very simple:
+
+static int validate(hip_xml_elmid parent, hip_xml_elmid child)
+{
+ /* Only allow 'list-of-things' as the root element. */
+ if (parent == HIP_ELM_root && child == ELM_listofthings ||
+ parent = ELM_listofthings && child == ELM_a_thing) {
+ return HIP_XML_VALID;
+ } else {
+ return HIP_XML_INVALID;
+ }
+}
+
+For this example, we can ignore the start-element callback, and just
+use the end-element callback:
+
+static int endelm(void *userdata, const struct hip_xml_elm *s,
+ const char *cdata)
+{
+ printf("Got a thing: %s\n", cdata);
+ return 0;
+}
+
+This endelm callback just prints the cdata which was contained in the
+"a-thing" element.
+
+Now, on to parsing. A new parser object is created for parsing each
+XML document. Creating a new parser object is as simple as:
+
+ hip_xml_parser *parser;
+
+ parser = hip_xml_create();
+
+Next register the handler, passing NULL as the start-element callback,
+and also as userdata, which we don't use here.
+
+ hip_xml_push_handler(parser, my_elms,
+ validate, NULL, endelm,
+ NULL);
+
+Finally, call hip_xml_parse, passing the chunks of XML document to the
+hip_xml as you get them. The output should be:
+
+ Got a thing: foo
+ Got a thing: bar
+
+for the XML document.
diff --git a/doc/ref/alloc.xml b/doc/ref/alloc.xml
new file mode 100644
index 0000000..cc0ac22
--- /dev/null
+++ b/doc/ref/alloc.xml
@@ -0,0 +1,88 @@
+ <refentry id="refalloc">
+
+ <refmeta>
+ <refentrytitle>ne_malloc</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_malloc">ne_malloc</refname>
+ <refname id="ne_calloc">ne_calloc</refname>
+ <refname id="ne_realloc">ne_realloc</refname>
+ <refname id="ne_strdup">ne_strdup</refname>
+ <refname id="ne_strndup">ne_strndup</refname>
+ <refname id="ne_oom_callback">ne_oom_callback</refname>
+ <refpurpose>memory allocation wrappers</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_alloc.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void *<function>ne_malloc</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void *<function>ne_calloc</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void *<function>ne_realloc</function></funcdef>
+ <paramdef>void *<parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_strdup</function></funcdef>
+ <paramdef>const char *<parameter>s</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_strndup</function></funcdef>
+ <paramdef>const char *<parameter>s</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_oom_callback</function></funcdef>
+ <paramdef>void (*<parameter>callback</parameter>)(void)</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The functions <function>ne_malloc</function>,
+<function>ne_calloc</function>, <function>ne_realloc</function>,
+<function>ne_strdup</function> and <function>ne_strdnup</function>
+provide wrappers for the equivalent functions in the standard C
+library. The wrappers provide the extra guarantee that if the C
+library equivalent returns &null; when no memory is available, an
+optional callback will be called, and the library will then call
+<function>abort</function>().</para>
+
+ <para><function>ne_oom_callback</function> registers a callback
+which will be invoked if an out of memory error is detected.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>If the operating system uses optimistic memory
+allocation, the C library memory allocation routines will not return
+&null;, so it is not possible to gracefully handle memory allocation
+failures.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/auth.xml b/doc/ref/auth.xml
new file mode 100644
index 0000000..9b20201
--- /dev/null
+++ b/doc/ref/auth.xml
@@ -0,0 +1,114 @@
+ <refentry id="refauth">
+
+ <refmeta>
+ <refentrytitle>ne_set_server_auth</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_set_server_auth">ne_set_server_auth</refname>
+ <refname id="ne_set_proxy_auth">ne_set_proxy_auth</refname>
+ <refname id="ne_forget_auth">ne_forget_auth</refname>
+ <refpurpose>register authentication callbacks</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_auth.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>typedef int (*<function>ne_request_auth</function>)</funcdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ <paramdef>const char *<parameter>realm</parameter></paramdef>
+ <paramdef>int <parameter>attempt</parameter></paramdef>
+ <paramdef>char *<parameter>username</parameter></paramdef>
+ <paramdef>char *<parameter>password</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_server_auth</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>ne_request_auth <parameter>callback</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_proxy_auth</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>ne_request_auth <parameter>callback</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_forget_auth</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <type>ne_request_auth</type> function type defines a
+callback which is invoked when a server or proxy server requires user
+authentication for a particular request. The
+<parameter>realm</parameter> string is supplied by the server. <!--
+FIXME --> The <parameter>attempt</parameter> is a counter giving the
+number of times the request has been retried with different
+authentication credentials. The first time the callback is invoked
+for a particular request, <parameter>attempt</parameter> will be zero.</para>
+
+ <para>To retry the request using new authentication
+credentials, the callback should return zero, and the
+<parameter>username</parameter> and <parameter>password</parameter>
+buffers must contain &nul;-terminated strings. The
+<literal>NE_ABUFSIZ</literal> constant gives the size of these
+buffers.</para>
+
+ <tip>
+ <para>If you only wish to allow the user one attempt to enter
+credentials, use the value of the <parameter>attempt</parameter>
+parameter as the return value of the callback.</para>
+ </tip>
+
+ <para>To abort the request, the callback should return a
+non-zero value; in which case the contents of the
+<parameter>username</parameter> and <parameter>password</parameter>
+buffers are ignored.</para>
+
+ <para>The <function>ne_forget_auth</function> function can be
+used to discard the cached authentication credentials.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <programlisting>
+/* Function which prompts for a line of user input: */
+extern char *prompt_for(const char *prompt);
+
+static int
+my_auth(void *userdata, const char *realm, int attempts,
+ char *username, char *password)
+{
+ strncpy(username, prompt_for("Username: "), NE_ABUFSIZ);
+ strncpy(password, prompt_for("Password: "), NE_ABUFSIZ);
+ return attempts;
+}
+
+int main(...)
+{
+ &egsess;
+
+ ne_set_server_auth(sess, my_auth, NULL);
+
+ /* ... */
+}</programlisting>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/buf.xml b/doc/ref/buf.xml
new file mode 100644
index 0000000..68b515c
--- /dev/null
+++ b/doc/ref/buf.xml
@@ -0,0 +1,53 @@
+ <refentry id="refbuf">
+
+ <refmeta>
+ <refentrytitle>ne_buffer</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer">ne_buffer</refname>
+ <refpurpose>string buffer handling</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis><funcsynopsisinfo>#include &lt;ne_string.h&gt;
+
+typedef struct {
+ char *data;
+ size_t used;
+ size_t length;
+} <type>ne_buffer</type>;</funcsynopsisinfo></funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <type>ne_buffer</type> type represents an expandable
+memory buffer for holding &nul;-terminated strings. The
+<structfield>data</structfield> field points to the beginnning of the
+string, the length of which is given by the
+<structfield>used</structfield> field. The current size of memory
+allocated is given by the <structfield>length</structfield> field. It
+is not recommended that the fields of a buffer are manipulated
+directly. The <structfield>data</structfield> pointer may change when
+the buffer is modified.</para>
+
+ <para>A buffer is created using <xref
+linkend="ne_buffer_create"/> or <xref
+linkend="ne_buffer_create_sized"/>, and destroyed using <xref
+linkend="ne_buffer_destroy"/> or <xref linkend="ne_buffer_finish"/>.
+The functions <xref linkend="ne_buffer_append"/>, <xref
+linkend="ne_buffer_zappend"/> and <xref linkend="ne_buffer_concat"/> are
+used to append data to a buffer.</para>
+
+ <para>If the string referenced by the
+<structfield>data</structfield> pointer is modified directly (rather
+than using one of the functions listed above),
+<function>ne_buffer_altered</function> must be called.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/bufapp.xml b/doc/ref/bufapp.xml
new file mode 100644
index 0000000..a75ce81
--- /dev/null
+++ b/doc/ref/bufapp.xml
@@ -0,0 +1,89 @@
+ <refentry id="refbufapp">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_append</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_append">ne_buffer_append</refname>
+ <refname id="ne_buffer_zappend">ne_buffer_zappend</refname>
+ <refname id="ne_buffer_concat">ne_buffer_concat</refname>
+ <refpurpose>append data to a string buffer</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_append</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>const char *<parameter>string</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_zappend</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>const char *<parameter>string</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_concat</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>const char *<parameter>str</parameter></paramdef>
+ <paramdef>...</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_buffer_append</function> and
+<function>ne_buffer_zappend</function> functions append a string to
+the end of a buffer; extending the buffer as necessary. The
+<parameter>len</parameter> passed to
+<function>ne_buffer_append</function> specifies the length of the
+string to append; there must be no &nul; terminator in the first
+<parameter>len</parameter> bytes of the string.
+<function>ne_buffer_zappend</function> must be passed a
+&nul;-terminated string.</para>
+
+ <para>The <function>ne_buffer_concat</function> function takes
+a variable-length argument list following <parameter>str</parameter>;
+each argument must be a <type>char *</type> pointer to a
+&nul;-terminated string. A &null; pointer must be given as the last
+argument to mark the end of the list. The strings (including
+<parameter>str</parameter>) are appended to the buffer in the order
+given. None of the strings passed to
+<function>ne_buffer_concat</function> are modified.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The following code will output "<literal>Hello, world.
+And goodbye.</literal>".</para>
+
+ <programlisting>ne_buffer *buf = ne_buffer_create();
+ne_buffer_zappend(buf, "Hello");
+ne_buffer_concat(buf, ", world. ", "And ", "goodbye.", NULL);
+puts(buf->data);
+ne_buffer_destroy(buf);</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_buffer"/>, <xref linkend="ne_buffer_create"/>,
+<xref linkend="ne_buffer_destroy"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/bufcr.xml b/doc/ref/bufcr.xml
new file mode 100644
index 0000000..0c572a6
--- /dev/null
+++ b/doc/ref/bufcr.xml
@@ -0,0 +1,60 @@
+ <refentry id="refbufcr">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_create">ne_buffer_create</refname>
+ <refname id="ne_buffer_create_sized">ne_buffer_ncreate</refname>
+ <refpurpose>general purpose of group of functions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_alloc.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>ne_buffer *<function>ne_buffer_create</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>ne_buffer *<function>ne_buffer_ncreate</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>ne_buffer_create</function> creates a new
+buffer object, with an implementation-defined initial size.
+<function>ne_buffer_ncreate</function> creates an
+<type>ne_buffer</type> where the minimum initial size is given in the
+<parameter>size</parameter> parameter. The buffer created will
+contain the empty string (<literal>""</literal>).</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>Both functions return a pointer to a new buffer object,
+and never &null;.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_buffer"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/bufdest.xml b/doc/ref/bufdest.xml
new file mode 100644
index 0000000..5dc4dfc
--- /dev/null
+++ b/doc/ref/bufdest.xml
@@ -0,0 +1,81 @@
+ <refentry id="refbufdest">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_destroy</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_destroy">ne_buffer_destroy</refname>
+ <refname id="ne_buffer_finish">ne_buffer_finish</refname>
+ <refpurpose>destroy a buffer object</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_destroy</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_buffer_finish</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>ne_buffer_destroy</function> frees all memory
+associated with the buffer. <function>ne_buffer_finish</function>
+frees the buffer structure, but not the actual string stored in the
+buffer, which is returned and must be <function>free</function>()d by
+the caller.</para>
+
+ <para>Any use of the buffer object after calling either of these
+functions gives undefined behaviour.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>ne_buffer_finish</function> returns the
+<function>malloc</function>-allocated string stored in the buffer.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>An example use of <function>ne_buffer_finish</function>;
+the <function>duplicate</function> function returns a string made up of
+<parameter>n</parameter> copies of <parameter>str</parameter>:</para>
+
+ <programlisting>static char *duplicate(int n, const char *str)
+{
+ ne_buffer *buf = ne_buffer_create();
+ while (n--) {
+ ne_buffer_zappend(buf, str);
+ }
+ return ne_buffer_finish(buf);
+}</programlisting>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_buffer"/>, <xref linkend="ne_buffer_create"/>,
+<xref linkend="ne_buffer_zappend"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/bufutil.xml b/doc/ref/bufutil.xml
new file mode 100644
index 0000000..3f3f3c5
--- /dev/null
+++ b/doc/ref/bufutil.xml
@@ -0,0 +1,62 @@
+ <refentry id="refbufutil">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_clear</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_clear">ne_buffer_clear</refname>
+ <refname id="ne_buffer_grow">ne_buffer_grow</refname>
+ <refname id="ne_buffer_altered">ne_buffer_altered</refname>
+ <refpurpose>general purpose of group of functions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_clear</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_altered</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_grow</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_buffer_clear</function> function sets
+the string stored in <parameter>buf</parameter> to be the empty string
+(<literal>""</literal>).</para>
+
+ <para>The <function>ne_buffer_altered</function> function must
+be used after the string stored in the buffer
+<parameter>buf</parameter> is modified by directly rather than using
+<xref linkend="ne_buffer_append"/>, <xref linkend="ne_buffer_zappend"/>
+or <xref linkend="ne_buffer_concat"/>.</para>
+
+ <para>The <function>ne_buffer_grow</function> function
+ensures that at least <parameter>size</parameter> bytes are allocated
+for the string; this can be used if a large amount of data is going to
+be appended to the buffer and may result in more efficient memory
+allocation.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/clicert.xml b/doc/ref/clicert.xml
new file mode 100644
index 0000000..47a2ce0
--- /dev/null
+++ b/doc/ref/clicert.xml
@@ -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_owner</function> will return
+ this name (or &null; if no friendly name was specified).
+ <function>ne_ssl_clicert_owner</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_certificate"/></para>
+ </refsect1>
+
+</refentry>
+
diff --git a/doc/ref/config.xml b/doc/ref/config.xml
new file mode 100644
index 0000000..4823cca
--- /dev/null
+++ b/doc/ref/config.xml
@@ -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>
diff --git a/doc/ref/err.xml b/doc/ref/err.xml
new file mode 100644
index 0000000..50551b5
--- /dev/null
+++ b/doc/ref/err.xml
@@ -0,0 +1,66 @@
+ <refentry id="referr">
+
+ <refmeta>
+ <refentrytitle>ne_get_error</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_get_error">ne_get_error</refname>
+ <refname id="ne_set_error">ne_set_error</refname>
+ <refpurpose>error handling for HTTP sessions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_error</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_error</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>...</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The session error string is used to store any
+human-readable error information associated with any errors which
+occur whilst using the HTTP session.</para>
+
+ <para>The <function>ne_get_error</function> function returns the current
+session error string. This string persists only until it is changed by a
+subsequent operation on the session.</para>
+
+ <para>The <function>ne_set_error</function> function can be
+used to set a new session error string, using a
+<function>printf</function>-style format string interface.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <para>Retrieve the current error string:</para>
+ <programlisting>&egsess;
+...
+printf("Error was: %s\n", ne_get_error(sess));</programlisting>
+
+ <para>Set a new error string:</para>
+ <programlisting>&egsess;
+...
+ne_set_error(sess, "Response missing header %s", "somestring");</programlisting>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/getst.xml b/doc/ref/getst.xml
new file mode 100644
index 0000000..5a419c4
--- /dev/null
+++ b/doc/ref/getst.xml
@@ -0,0 +1,63 @@
+ <refentry id="refgetst">
+
+ <refmeta>
+ <refentrytitle>ne_get_status</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_get_status">ne_get_status</refname>
+ <refpurpose>retrieve HTTP response status for request</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>const ne_status *<function>ne_get_status</function></funcdef>
+ <paramdef>const ne_request *<parameter>request</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_get_status</function> function returns
+a pointer to the HTTP status object giving the result of a request.
+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_status"/>, <xref
+ linkend="ne_request_create"/></para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>Display the response status code of applying the
+<literal>HEAD</literal> method to some resource.</para>
+
+ <programlisting>ne_request *req = ne_request_create(sess, "HEAD", "/foo/bar");
+if (ne_request_dispatch(req))
+ /* handle errors... */
+else
+ printf("Response status code was %d\n", ne_get_status(req)->code);
+ne_request_destroy(req);</programlisting>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/iaddr.xml b/doc/ref/iaddr.xml
new file mode 100644
index 0000000..cf64a13
--- /dev/null
+++ b/doc/ref/iaddr.xml
@@ -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>
+
diff --git a/doc/ref/init.xml b/doc/ref/init.xml
new file mode 100644
index 0000000..32dcda5
--- /dev/null
+++ b/doc/ref/init.xml
@@ -0,0 +1,55 @@
+<refentry id="refsockinit">
+
+ <refmeta>
+ <refentrytitle>ne_sock_init</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_sock_init">ne_sock_init</refname>
+ <refpurpose>perform library initialization</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_socket.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>ne_sock_init</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>In some platforms and configurations, &neon; may be using
+ some socket or SSL libraries which require global initialization
+ before use. To perform this initialization, the
+ <function>ne_sock_init</function> function must be called once
+ before any other library functions are used.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>ne_sock_init</function> returns zero on success,
+ or non-zero on error. If an error occurs, no further use of the
+ &neon; library should be attempted.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="refneon"/></para>
+ </refsect1>
+
+</refentry>
+
diff --git a/doc/ref/neon.xml b/doc/ref/neon.xml
new file mode 100644
index 0000000..9857c63
--- /dev/null
+++ b/doc/ref/neon.xml
@@ -0,0 +1,145 @@
+<refentry id="refneon">
+
+ <refmeta>
+ <refentrytitle>neon</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>neon</refname>
+ <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 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
+ linkend="ne_set_server_auth"/>; complete SSL/TLS support is also
+ included, see <xref linkend="ne_ssl_set_verify"/>.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Conventions</title>
+
+ <para>Some conventions are used throughout the neon API, to
+ provide a consistent and simple interface; these are documented
+ below.</para>
+
+ <refsect2>
+ <title>Thread-safeness and global initialization</title>
+
+ <para>&neon; itself is implemented to be thread-safe (avoiding
+ any use of global state), but in some configurations makes use of
+ other libraries which require global initialization. The
+ <xref linkend="ne_sock_init"/> function should be called before
+ any other use of the &neon; library interface.</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 arguments 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 in
+ <emphasis>undefined behaviour</emphasis>, by definition.</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 internally. WebDAV property
+ names and values must be valid UTF-8 encoded Unicode
+ strings.</para>
+
+ </refsect2>
+
+ <refsect2>
+ <title>Memory handling</title>
+
+ <para>neon does not attempt to cope gracefully with an
+ 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>
+
+ <refsect2>
+ <title>Callbacks and userdata</title>
+
+ <para>Whenever a callback is registered, a
+ <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="ne_oom_callback"/></para>
+ </refsect1>
+
+</refentry>
+
diff --git a/doc/ref/opts.xml b/doc/ref/opts.xml
new file mode 100644
index 0000000..fe8f368
--- /dev/null
+++ b/doc/ref/opts.xml
@@ -0,0 +1,126 @@
+ <refentry id="refopts">
+
+ <refmeta>
+ <refentrytitle>ne_set_useragent</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_set_useragent">ne_set_useragent</refname>
+ <refname id="ne_set_persist">ne_set_persist</refname>
+ <refname id="ne_set_read_timeout">ne_set_read_timeout</refname>
+ <refname id="ne_set_expect100">ne_set_expect100</refname>
+ <refpurpose>common settings for HTTP sessions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_useragent</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>product</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_persist</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_read_timeout</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>timeout</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_expect100</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ </funcprototype>
+
+<!--
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_scheme</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_server_hostport</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+-->
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <!-- TODO: intro para? -->
+
+ <para>The <literal>User-Agent</literal> request header is used
+to identify the software which generated the request for statistical
+or debugging purposes. neon does not send a
+<literal>User-Agent</literal> header unless a call is made to the
+<function>ne_set_useragent</function>.
+<function>ne_set_useragent</function> must be passed a product string
+conforming to RFC2616's product token grammar; of the form
+<literal>"Product/Version"</literal>.</para>
+
+ <para>By default neon will use a persistent connection
+whenever possible. For specific applications, or for debugging
+purposes, it is sometimes useful to disable persistent connections.
+The <function>ne_set_persist</function> function will disable
+persistent connections if passed a <parameter>flag</parameter>
+parameter of <literal>0</literal>, and will enable them
+otherwise.</para>
+
+ <para>When neon reads from a socket, by default the read
+operation will time out after 60 seconds, and the request will fail
+giving an <errorcode>NE_TIMEOUT</errorcode> error. To configure this
+timeout interval, call <function>ne_set_read_timeout</function> giving
+the desired number of seconds as the <parameter>timeout</parameter>
+parameter.</para>
+
+ <para>An extension introduced in the HTTP/1.1 specification
+was the use of the <literal>Expect: 100-continue</literal> header.
+This header allows an HTTP client to be informed of the expected
+response status before the request message body is sent: a useful
+optimisation for situations where a large message body is to be sent.
+The <function>ne_set_expect100</function> function can be used to
+enable this feature by passing the <parameter>flag</parameter>
+parameter as any non-zero integer.</para>
+
+<warning><para>Unfortunately, if this header is sent to a server which
+is not fully compliant with the HTTP/1.1 specification, a deadlock
+occurs resulting in a temporarily "hung" connection. neon will
+recover gracefully from this situation, but only after a 15 second
+timeout. It is highly recommended that this option is not enabled
+unless it is known that the server in use correctly implements
+<literal>Expect: 100-continue</literal> support.</para></warning>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <para>Set a user-agent string:</para>
+ <programlisting>&egsess;
+ne_set_useragent(sess, "MyApplication/2.1");</programlisting>
+
+ <para>Disable use of persistent connections:</para>
+ <programlisting>ne_session *sess = ne_session_create(...);
+ne_set_persist(sess, 0);</programlisting>
+
+ <para>Set a 30 second read timeout:</para>
+ <programlisting>&egsess;
+ne_set_read_timeout(sess, 30);</programlisting>
+
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/req.xml b/doc/ref/req.xml
new file mode 100644
index 0000000..7c36d5c
--- /dev/null
+++ b/doc/ref/req.xml
@@ -0,0 +1,169 @@
+ <refentry id="refreq">
+
+ <refmeta>
+ <refentrytitle>ne_request_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_request_create">ne_request_create</refname>
+ <refname id="ne_request_dispatch">ne_request_dispatch</refname>
+ <refname id="ne_request_destroy">ne_request_destroy</refname>
+ <refpurpose>low-level HTTP request handling</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>ne_request *<function>ne_request_create</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>method</parameter></paramdef>
+ <paramdef>const char *<parameter>path</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>ne_request_dispatch</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_request_destroy</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>An HTTP request, represented by the
+<type>ne_request</type> type, specifies that some operation is to be
+performed on some resource. The
+<function>ne_request_create</function> function creates a request
+object, specifying the operation in the <parameter>method</parameter>
+parameter. The location of the resource is determined by the server in
+use for the session given by the <parameter>sess</parameter>
+parameter, combined with the <parameter>path</parameter> parameter.</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
+<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
+<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, <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 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>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>The <function>ne_request_create</function> function
+returns a pointer to a request object (and never &null;).</para>
+
+ <para>The <function>ne_request_dispatch</function> function
+returns zero if the request was dispatched successfully, and a
+non-zero error code otherwise.</para>
+
+ </refsect1>
+
+<!-- TODO: abs_path description in a NOTES section -->
+
+ <refsect1>
+ <title>Errors</title>
+
+ <variablelist>
+ <varlistentry><term><errorcode>NE_ERROR</errorcode></term>
+ <listitem>
+ <simpara>Request failed (see session error string)</simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_LOOKUP</errorcode></term>
+ <listitem>
+ <simpara>The DNS lookup for the server (or proxy server) failed.</simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_AUTH</errorcode></term>
+ <listitem>
+ <simpara>Authentication failed on the server.</simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_PROXYAUTH</errorcode></term>
+ <listitem>
+ <simpara>Authentication failed on the proxy server.</simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_CONNECT</errorcode></term>
+ <listitem>
+ <simpara>A connection to the server could not be established.</simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_TIMEOUT</errorcode></term>
+ <listitem>
+ <simpara>A timeout occurred while waiting for the server to respond.</simpara>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>An example of applying a <literal>MKCOL</literal>
+ operation to the resource at the location
+ <literal>http://www.example.com/foo/bar/</literal>:</para>
+
+ <programlisting>ne_session *sess = ne_session_create("http", "www.example.com", 80);
+ne_request *req = ne_request_create(sess, "MKCOL", "/foo/bar/");
+if (ne_request_dispatch(req)) {
+ printf("Request failed: %s\n", ne_get_error(sess));
+}
+ne_request_destroy(req);</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_get_error"/>, <xref
+linkend="ne_set_error"/>, <xref linkend="ne_get_status"/>, <xref
+linkend="ne_add_request_header"/>, <xref
+linkend="ne_set_request_body_buffer"/>.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/reqbody.xml b/doc/ref/reqbody.xml
new file mode 100644
index 0000000..e2c8eb3
--- /dev/null
+++ b/doc/ref/reqbody.xml
@@ -0,0 +1,69 @@
+ <refentry id="refreqbody">
+
+ <refmeta>
+ <refentrytitle>ne_set_request_body_buffer</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_set_request_body_buffer">ne_set_request_body_buffer</refname>
+ <refname id="ne_set_request_body_fd">ne_set_request_body_fd</refname>
+ <refpurpose>include a message body with a request</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_request_body_buffer</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ <paramdef>const char *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>count</parameter></paramdef>
+ </funcprototype>
+
+ <!-- this is a better interface for set_request_body_fd:
+ <funcprototype>
+ <funcdef>int <function>ne_set_request_body_fd</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ <paramdef>int <parameter>fd</parameter></paramdef>
+ <paramdef>off_t <parameter>begin</parameter></paramdef>
+ <paramdef>size_t <parameter>count</parameter></paramdef>
+ </funcprototype>
+ -->
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_set_request_body_buffer</function>
+function specifies that a message body should be included with the
+body, which is stored in the <parameter>count</parameter> bytes buffer
+<parameter>buf</parameter>.</para>
+
+<!--
+ <para>The <function>ne_set_request_body_fd</function> function
+can be used to include a message body with a request which is read
+from a file descriptor. The body is read from the file descriptor
+<parameter>fd</parameter>, which must be a associated with a seekable
+file (not a pipe, socket, or FIFO). <parameter>count</parameter>
+bytes are read, beginning at offset <parameter>begin</parameter>
+(passing <parameter>begin</parameter> as zero means the body is read
+from the beginning of the file).</para>
+
+-->
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_request_create"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/reqhdr.xml b/doc/ref/reqhdr.xml
new file mode 100644
index 0000000..4d811b1
--- /dev/null
+++ b/doc/ref/reqhdr.xml
@@ -0,0 +1,63 @@
+ <refentry id="refreqhdr">
+
+ <refmeta>
+ <refentrytitle>ne_add_request_header</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_add_request_header">ne_add_request_header</refname>
+ <refname id="ne_print_request_header">ne_print_request_header</refname>
+ <refpurpose>add headers to a request</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_add_request_header</function></funcdef>
+ <paramdef>ne_request *<parameter>request</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>const char *<parameter>value</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_print_request_header</function></funcdef>
+ <paramdef>ne_request *<parameter>request</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>...</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The functions <function>ne_add_request_header</function>
+and <function>ne_print_request_header</function> can be used to add
+headers to a request, before it is sent.</para>
+
+ <para><function>ne_add_request_header</function> simply adds a
+header of given <parameter>name</parameter>, with given
+<parameter>value</parameter>.</para>
+
+ <para><function>ne_print_request_header</function> adds a
+header of given <parameter>name</parameter>, taking the value from the
+<function>printf</function>-like <parameter>format</parameter> string
+parameter and subsequent variable-length argument list.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_request_create"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/resolve.xml b/doc/ref/resolve.xml
new file mode 100644
index 0000000..067cf61
--- /dev/null
+++ b/doc/ref/resolve.xml
@@ -0,0 +1,145 @@
+<refentry id="refresolve">
+
+ <refmeta>
+ <refentrytitle>ne_addr_resolve</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_addr_resolve">ne_addr_resolve</refname>
+ <refname id="ne_addr_result">ne_addr_result</refname>
+ <refname id="ne_addr_first">ne_addr_first</refname>
+ <refname id="ne_addr_next">ne_addr_next</refname>
+ <refname id="ne_addr_error">ne_addr_error</refname>
+ <refname id="ne_addr_destroy">ne_addr_destroy</refname>
+ <refpurpose>functions to resolve hostnames to addresses</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_socket.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>ne_sock_addr *<function>ne_addr_resolve</function></funcdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>ne_addr_result</function></funcdef>
+ <paramdef>const ne_sock_addr *<parameter>addr</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const ne_inet_addr *<function>ne_addr_first</function></funcdef>
+ <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <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_error</function></funcdef>
+ <paramdef>const ne_sock_addr *<parameter>addr</parameter></paramdef>
+ <paramdef>char *<parameter>buffer</parameter></paramdef>
+ <paramdef>size_t <parameter>bufsiz</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_addr_destroy</function></funcdef>
+ <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_addr_resolve</function> function resolves
+ the given <parameter>hostname</parameter>, returning an
+ <type>ne_sock_addr</type> object representing the address (or
+ addresses) associated with the hostname. The
+ <parameter>flags</parameter> parameter is currently unused, and
+ 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
+ support IPv6, an IPv6 hex address, which may be enclosed in
+ brackets, e.g. <literal>"[::1]"</literal>.</para>
+
+ <para>To determine whether the hostname was successfully resolved,
+ the <function>ne_addr_result</function> function is used, which
+ returns non-zero if an error occurred. If an error did occur, the
+ <function>ne_addr_error</function> function can be used, which
+ will copy the error string into a given
+ <parameter>buffer</parameter> (of size
+ <parameter>bufsiz</parameter>).</para>
+
+ <para>The functions <function>ne_addr_first</function> and
+ <function>ne_addr_next</function> are used to retrieve the
+ Internet addresses associated with an address object which has
+ been successfully resolved. <function>ne_addr_first</function>
+ returns the first address; <function>ne_addr_next</function>
+ returns the next address after the most recent call to
+ <function>ne_addr_next</function> or
+ <function>ne_addr_first</function>, or &null; if there are no more
+ addresses. The <type>ne_inet_addr</type> pointer returned by
+ these functions can be passed to
+ <function>ne_sock_connect</function> to connect a socket.</para>
+
+ <para>After the address object has been used, it should be
+ destroyed using <function>ne_addr_destroy</function>.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>ne_addr_resolve</function> returns a pointer to an
+ address object, and never &null;.
+ <function>ne_addr_error</function> returns the
+ <parameter>buffer</parameter> parameter .</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The code below prints out the set of addresses associated
+ with the hostname <literal>www.google.com</literal>.</para>
+
+ <programlisting>ne_sock_addr *addr;
+char buf[256];
+
+addr = ne_addr_resolve("www.google.com", 0);
+if (ne_addr_result(addr)) {
+ printf("Could not resolve www.google.com: %s\n",
+ ne_addr_error(addr, buf, sizeof buf));
+} else {
+ 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);
+</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_iaddr_print"/></para>
+ </refsect1>
+
+</refentry>
+
diff --git a/doc/ref/sess.xml b/doc/ref/sess.xml
new file mode 100644
index 0000000..8fcd9cb
--- /dev/null
+++ b/doc/ref/sess.xml
@@ -0,0 +1,123 @@
+ <refentry id="refsess">
+
+ <refmeta>
+ <refentrytitle>ne_session_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_session_create">ne_session_create</refname>
+ <refname id="ne_close_connection">ne_close_connection</refname>
+ <refname id="ne_session_proxy">ne_session_proxy</refname>
+ <refname id="ne_session_destroy">ne_session_destroy</refname>
+ <refpurpose>set up HTTP sessions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+ <funcprototype>
+ <funcdef>ne_session *<function>ne_session_create</function></funcdef>
+ <paramdef>const char *<parameter>scheme</parameter></paramdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ <paramdef>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>unsigned int <parameter>port</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_close_connection</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_session_destroy</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>An <type>ne_session</type> object represents an HTTP
+session - 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>). 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>
+
+ <para>If an HTTP proxy server should be used for the session,
+<function>ne_session_proxy</function> must be called giving the
+hostname and port on which to contact the proxy.</para>
+
+ <para>If it is known that the session will not be used for a
+significant period of time, <function>ne_close_connection</function>
+can be called to close the connection, if one remains open. Use of
+this function is entirely optional, but it must not be called if there
+is a request active using the session.</para>
+
+ <para>Once a session has been completed,
+<function>ne_session_destroy</function> must be called to destroy the
+resources associated with the session. Any subsequent use of the
+session pointer produces undefined behaviour.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>The hostname passed to
+<function>ne_session_create</function> is resolved when the first
+request using the session is dispached; 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>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <para>Create and destroy a session:</para>
+ <programlisting>ne_session *sess;
+sess = ne_session_create("http", "host.example.com", 80);
+/* ... use sess ... */
+ne_session_destroy(sess);
+</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para><xref linkend="ne_ssl_set_verify"/>, <xref linkend="ne_ssl_load_ca"/>, <xref linkend="ne_sock_init"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/shave.xml b/doc/ref/shave.xml
new file mode 100644
index 0000000..a5b745a
--- /dev/null
+++ b/doc/ref/shave.xml
@@ -0,0 +1,51 @@
+ <refentry id="refshave">
+
+ <refmeta>
+ <refentrytitle>ne_shave</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_shave</refname>
+ <refpurpose>trim whitespace from a string</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_shave</function></funcdef>
+ <paramdef>char *<parameter>str</parameter></paramdef>
+ <paramdef>const char *<parameter>whitespace</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>ne_shave</function> returns a portion of
+<parameter>str</parameter> with any leading or trailing characters in
+the <parameter>whitespace</parameter> array removed.
+<parameter>str</parameter> may be modified.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The following code segment will output
+ <literal>"fish"</literal>:</para>
+
+ <programlisting>char s[] = ".!.fish!.!";
+puts(ne_shave(s, ".!"));</programlisting>
+
+ </refsect1>
+
+ </refentry>
+
diff --git a/doc/ref/sslca.xml b/doc/ref/sslca.xml
new file mode 100644
index 0000000..c68739d
--- /dev/null
+++ b/doc/ref/sslca.xml
@@ -0,0 +1,81 @@
+ <refentry id="refsslca">
+
+ <refmeta>
+ <refentrytitle>ne_ssl_load_ca</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_ssl_load_ca">ne_ssl_load_ca</refname>
+ <refname id="ne_ssl_load_default_ca">ne_ssl_load_default_ca</refname>
+ <refpurpose>load SSL Certificate Authorities</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>ne_ssl_load_ca</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>filename</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>ne_ssl_load_default_ca</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>To indicate that a given CA certificate is trusted by the user,
+the certificate can be loaded using the <function>ne_ssl_load_ca</function>
+function. The <parameter>filename</parameter> parameter given must specify
+the location of a PEM-encoded CA certificate.</para>
+
+ <para>The SSL library in use by neon may include a default set
+of CA certificates; calling the
+<function>ne_ssl_load_default_ca</function> function will indicate
+that these CAs are trusted by the user.</para>
+
+ <para>If no CA certificates are loaded, or the server presents
+a certificate which is invalid in some way, then the certificate must
+be manually verified (see <xref linkend="ne_ssl_set_verify"/>), otherwise the
+connection will fail.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>Both <function>ne_ssl_load_ca</function> and
+<function>ne_ssl_load_default_ca</function> functions return
+<literal>0</literal> on success, or non-zero on failure.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>Load the CA certificate stored in <filename>/path/to/cacert.pem</filename>:</para>
+ <programlisting>&egsess;
+
+if (ne_ssl_load_ca(sess, "/path/to/cacert.pem")) {
+ printf("Could not load CA cert: %s\n", ne_get_error(sess));
+}</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_get_error"/>, <xref
+ linkend="ne_ssl_set_verify"/></para> </refsect1>
+
+ </refentry>
diff --git a/doc/ref/sslcert.xml b/doc/ref/sslcert.xml
new file mode 100644
index 0000000..46dbffd
--- /dev/null
+++ b/doc/ref/sslcert.xml
@@ -0,0 +1,66 @@
+ <refentry id="refsslcert">
+
+ <refmeta>
+ <refentrytitle>ne_ssl_certificate</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_ssl_certificate">ne_ssl_certificate</refname>
+ <refname id="ne_ssl_dname">ne_ssl_dname</refname>
+ <refpurpose>structures representing SSL certificates</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis><funcsynopsisinfo>#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>;
+
+</funcsynopsisinfo></funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <type>ne_ssl_dname</type> structure is used to
+represent a simplified X.509 distinguished name, as used in SSL
+certificates; a distinguished name is used to uniquely identify an
+entity. Along with the fields giving the geographical and
+organizational location of the entity, the
+<structfield>commonName</structfield> field will be assigned the DNS
+hostname of the entity. The
+<function>ne_ssl_readable_dname</function> function can be used to
+create a single-line string out of an <type>ne_ssl_dname</type>
+structure.</para>
+
+ <para>The <type>ne_ssl_certificate</type> structure is used to
+represent a simplified SSL certificate; containing the distinguished
+names of the <firstterm>issuer</firstterm> and
+<firstterm>subject</firstterm> of the certificate. The issuer is the
+entity which has digitally signed the certificate to guarantee its
+authenticity; the subject is the owner of the certificate. A
+certificate is only valid for a certain period of time: the
+<structfield>from</structfield> and <structfield>until</structfield>
+contain strings giving the validity period.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_set_verify"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/ssldname.xml b/doc/ref/ssldname.xml
new file mode 100644
index 0000000..accc2cb
--- /dev/null
+++ b/doc/ref/ssldname.xml
@@ -0,0 +1,66 @@
+
+ <refentry id="refssldname">
+
+ <refmeta>
+ <refentrytitle>ne_ssl_dname</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <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_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>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_ssl_readable_dname</function> function
+creates a single-line, human-readable string out of an
+<type>ne_ssl_dname</type> object. The returned string is
+<function>malloc</function>()-allocated, and must be
+<function>free</function>()d by the caller.</para>
+
+ <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>ne_ssl_readable_dname</function> returns a
+ <function>malloc</function>-allocated string, and never
+ NULL.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_ssl_certificate"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/sslvfy.xml b/doc/ref/sslvfy.xml
new file mode 100644
index 0000000..98f0054
--- /dev/null
+++ b/doc/ref/sslvfy.xml
@@ -0,0 +1,140 @@
+ <refentry id="refsslvfy">
+
+ <refmeta>
+ <refentrytitle>ne_ssl_set_verify</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_ssl_set_verify">ne_ssl_set_verify</refname>
+ <refpurpose>register an SSL certificate verification callback</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <!-- hard to put data type declarations here -->
+
+ <funcprototype>
+ <funcdef>typedef int (*<function>ne_ssl_verify_fn</function>)</funcdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ <paramdef>int <parameter>failures</parameter></paramdef>
+ <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_ssl_set_verify</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>ne_ssl_verify_fn <parameter>verify_fn</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>To enable manual SSL certificate verification, a
+callback can be registered using
+<function>ne_ssl_set_verify</function>. If such a callback is not
+registered, when a connection is established to an SSL server which
+does not present a certificate signed by a trusted CA (see <xref
+linkend="ne_ssl_load_ca"/>), or if the certificate presented is invalid in
+some way, the connection will fail.</para>
+
+ <para>When the callback is invoked, the
+<parameter>failures</parameter> parameter gives a bitmask indicating
+in what way the automatic certificate verification failed. The value
+is equal to the bit-wise OR of one or more of the following
+constants (and is guaranteed to be non-zero):</para>
+
+ <variablelist>
+ <varlistentry><term><filename>NE_SSL_NOTYETVALID</filename></term>
+ <listitem>
+ <para>The certificate is not yet valid.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><filename>NE_SSL_EXPIRED</filename></term>
+ <listitem>
+ <para>The certificate has expired.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><filename>NE_SSL_CNMISMATCH</filename></term>
+ <listitem>
+ <para>The hostname used for the session does not match
+the hostname to which the certificate was issued: this could mean that
+the connection has been intercepted.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><filename>NE_SSL_UNKNOWNCA</filename></term>
+ <listitem>
+ <para>The Certificate Authority which signed the certificate
+is not trusted.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>The <parameter>cert</parameter> parameter passed to the
+callback describes the certificate which was presented by the server,
+see <xref linkend="ne_ssl_certificate"/> for more details. The certificate
+object given is only valid until the callback returns.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>The verification callback must return zero to indicate
+that the certificate should be trusted; and non-zero otherwise (in
+which case, the connection will fail).</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>Manual certificate verification:</para>
+ <programlisting>
+static int
+my_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
+{
+ /* leak the return values of ne_ssl_readable_dname for simplicity! */
+ printf("Issuer: %s\n", ne_ssl_readable_dname(cert->issuer);
+ printf("Subject: %s\n", ne_ssl_readable_dname(cert->subject);
+ if (failures &amp; NE_SSL_CNMISMATCH) {
+ printf("Server certificate was issued to `%s'; "
+ "connection may have been intercepted!\n",
+ cert->subject->commonName);
+ }
+ if (failures &amp; NE_SSL_EXPIRED) {
+ printf("Server certificate expired on %s!", cert->until);
+ }
+ /* ... check for other failures ... */
+ if (prompt_user())
+ return 1; /* fail verification */
+ else
+ return 0; /* trust certificate */
+}
+
+int
+main(...)
+{
+ ne_session *sess = ne_session_create("https", "some.host.name", 443);
+ ne_ssl_set_verify(sess, my_verify, NULL);
+ ...
+}</programlisting>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_ssl_certificate"/>, <xref linkend="ne_ssl_load_ca"/>,
+ <xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_readable_dname"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/status.xml b/doc/ref/status.xml
new file mode 100644
index 0000000..56616ea
--- /dev/null
+++ b/doc/ref/status.xml
@@ -0,0 +1,74 @@
+ <refentry id="refstatus">
+
+ <refmeta>
+ <refentrytitle>ne_status</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_status">ne_status</refname>
+ <refpurpose>HTTP status structure</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <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>;</funcsynopsisinfo></funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>An <type>ne_status</type> type represents an HTTP
+response status; used in response messages giving a result of request.
+The <structfield>major_version</structfield> and
+<structfield>minor_version</structfield> fields give the HTTP version
+supported by the server issuing the response. The
+<structfield>code</structfield> field gives the status code of the
+result (lying between 100 and 999 inclusive), and the
+<structfield>klass</structfield> field gives the class, which is equal
+to the most significant digit of the status.</para>
+
+ <para>There are five classes of HTTP status code defined by
+ RFC2616:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>1xx</literal></term>
+ <listitem><para>Informational response.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>2xx</literal></term>
+ <listitem><para>Success: the operation was successful</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>3xx</literal></term>
+ <listitem><para>Redirection</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>4xx</literal></term> <listitem><para>Client
+ error: the request made was incorrect in some
+ manner.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>5xx</literal></term>
+ <listitem><para>Server error</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1> <title>See also</title> <para><xref
+linkend="ne_get_status"/>.</para> </refsect1>
+
+ </refentry>
diff --git a/doc/ref/tok.xml b/doc/ref/tok.xml
new file mode 100644
index 0000000..2e6211f
--- /dev/null
+++ b/doc/ref/tok.xml
@@ -0,0 +1,76 @@
+ <refentry id="reftok">
+
+ <refmeta>
+ <refentrytitle>ne_token</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_token</refname>
+ <refname>ne_qtoken</refname>
+ <refpurpose>string tokenizers</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_token</function></funcdef>
+ <paramdef>char **<parameter>str</parameter></paramdef>
+ <paramdef>char <parameter>sep</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_qtoken</function></funcdef>
+ <paramdef>char **<parameter>str</parameter></paramdef>
+ <paramdef>char <parameter>sep</parameter></paramdef>
+ <paramdef>const char *<parameter>quotes</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <!-- FIXME: italics on tokenize -->
+
+ <para><function>ne_token</function> and
+<function>ne_qtoken</function> tokenize the string at the location
+stored in the pointer <parameter>str</parameter>. Each time the
+function is called, it returns the next token, and modifies the
+<parameter>str</parameter> pointer to point to the remainer of the
+string, or &null; if there are no more tokens in the string. A token
+is delimited by the separator character <parameter>sep</parameter>; if
+<function>ne_qtoken</function> is used any quoted segments of the
+string are skipped when searching for a separator. A quoted segment
+is enclosed in a pair of one of the characters given in the
+<parameter>quotes</parameter> string.</para>
+
+ <para>The string being tokenized is modified each time
+the tokenizing function is called; replacing the next separator
+character with a &nul; terminator.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The following function prints out each token in a
+comma-separated string <parameter>list</parameter>, which is
+modified in-place:</para>
+
+ <programlisting>static void splitter(char *list)
+{
+ do {
+ printf("Token: %s\n", ne_token(&amp;list, ','));
+ while (list);
+}</programlisting>
+
+ </refsect1>
+
+ </refentry>
diff --git a/doc/ref/vers.xml b/doc/ref/vers.xml
new file mode 100644
index 0000000..2e7a50d
--- /dev/null
+++ b/doc/ref/vers.xml
@@ -0,0 +1,63 @@
+<refentry id="refvers">
+
+ <refmeta>
+ <refentrytitle>ne_version_match</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_version_match</refname>
+ <refname>ne_version_string</refname>
+ <refpurpose>library versioning</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_utils.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>ne_version_match</function></funcdef>
+ <paramdef>int <parameter>major</parameter></paramdef>
+ <paramdef>int <parameter>minor</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_version_string</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_version_match</function> function returns
+ non-zero if the library version is not of major version
+ <parameter>major</parameter>, or the minor version is less than
+ <parameter>minor</parameter>. 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>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>To require &neon; 1.x, version 1.2 or later:</para>
+
+ <programlisting>if (ne_version_match(1, 2)) {
+ printf("Library version out of date: 1.2 required, found %s.",
+ ne_version_string());
+ exit(1);
+}</programlisting>
+
+ </refsect1>
+
+</refentry>
diff --git a/doc/refentry.xml b/doc/refentry.xml
new file mode 100644
index 0000000..bf724e0
--- /dev/null
+++ b/doc/refentry.xml
@@ -0,0 +1,56 @@
+<refentry id="refXXXX">
+
+ <refmeta>
+ <refentrytitle>ne_foo</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_foo">ne_foo</refname>
+ <refname id="ne_bar">ne_bar</refname>
+ <refpurpose>functions which do foo and bar</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_header.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_useragent</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>product</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>XXX</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>XXX</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>XXX</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para>XXX</para>
+ </refsect1>
+
+</refentry>
+
diff --git a/doc/using-neon.txt b/doc/using-neon.txt
new file mode 100644
index 0000000..45ddfc3
--- /dev/null
+++ b/doc/using-neon.txt
@@ -0,0 +1,166 @@
+
+Guide to neon
+=============
+
+Using libneon from applications
+-------------------------------
+
+The neon source package is designed to be easily incorporated into
+applications:
+
+- autoconf macros are distributed in the 'macros' subdirectory of the
+ neon distribution. Use NEON_LIBRARY from your configure.in to check
+ for the presence of the neon library installed on the system. The
+ macro adds an '--with-neon=...' argument to configure, which allows
+ the user to specify a location for the library (the standard /usr
+ and /usr/local directories are checked automatically without having
+ to be specified).
+
+- The 'src' directory of the neon package can be imported directly
+ into your application, if you do not wish to add an external
+ dependency. 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':
+
+ NEON_BUNDLED(libneon, ...)
+
+ If your application supports builds where srcdir != builddir, you
+ should use the NEON_VPATH_BUNDLED macro like this:
+
+ NEON_VPATH_BUNDLED(${srcdir}/libneon, libneon, ...)
+
+ (thanks to Peter Moulder for getting this working properly).
+
+ 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.
+
+- 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.
+
+- A full fragment might be:
+
+ NEON_BUNDLED(libneon, [
+ NEON_NORMAL_BUILD
+ NEON_XML_PARSER
+ SUBDIRS="libneon $SUBDIRS"
+ ])
+
+ 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.
+
+The neon API
+============
+
+neon offers two levels of API for use in applications:
+
+- Low-level HTTP request/response handling
+- High-level method invocation
+
+The low-level interface allows for easily designing new method
+handlers, taking care of things like persistent connections,
+authentication, and proxy servers. The high-level interface allows
+you to easily integrate existing HTTP (and WebDAV) methods into your
+application. This document details both interfaces.
+
+N.B.: Documentation is always WRONG. The definitive API reference is in
+src/*.c, with src/*.h is hopefully fairly similar.
+
+PLEASE NOTE: the neon API is NOT STABLE, and is not considered to be
+stable or backwards-compatible until version 1.0 is reached. If
+you are not happy with this constraint, then please either:
+
+1. pick a version of neon and never upgrade
+2. don't use neon (yet).
+3. contribute sufficient development resources to neon that all
+ bugs are fixed yesterday, features added tomorrow, and 1.0 is
+ reached on by the end of the week.
+
+An Important Note
+-----------------
+
+Most neon functions which allocate memory with malloc() will call
+abort() if malloc() returns NULL. This is a design decision, the
+rationale being:
+
+- it makes the interfaces cleaner.
+
+- if malloc() DOES return NULL there is not much you can do about it.
+
+- Apparently, malloc() won't return NULL on systems which over-commit
+ memory (e.g. Linux), so it doesn't make any real difference anyway.
+
+The author is open to persuasion on this: mail neon@webdav.org.
+
+Namespaces
+----------
+
+neon reserves these namespaces:
+ ne_*
+ uri_*
+ sock_*
+
+Eventually all symbols globally defined by the library should fall
+within a reserved namespace. The author is considering moving
+all symbols into the ne_* namespace.
+
+Note that the XML parser used will also reserve a namespace:
+expat takes XML_*, libxml takes xml*
+
+The http_session type
+---------------------
+
+The http_session type is used whether you are writing to the low-level
+or the high-level interface. An http_session object is created to
+store data which persists beyond a single HTTP request:
+
+ - Protocol options, e.g. (proxy) server details
+ - Authentication information
+ - Persistent connection
+
+A session is created with the 'ne_session_create' call. Before
+creating a request for the session, the server details must be set, as
+follows:
+
+ ne_session *sess;
+ /* Initialize the socket library */
+ sock_init();
+ /* Create the session */
+ sess = ne_session_create();
+ /* Optionally, set a proxy server */
+ ne_session_proxy(sess, "proxy.myisp.com", 8080);
+ /* Set the server */
+ ne_session_server(sess, "my.server.com", 80);
+
+The proxy server should be set BEFORE the origin server; otherwise a
+DNS lookup will be performed on the origin server by the
+ne_session_server call, which may well fail if the client is
+firewalled. ne_session_{proxy,server} will return NE_LOOKUP if
+the DNS lookup fails; otherwise NE_OK.
+
+The 'ne_set_persist' call can be used to turn off persistent
+connection handling: it is on by default.
+
+The 'ne_set_useragent' call can be used to set the User-Agent header
+to be sent with requests. A product token of the form
+"myhttpclient/0.1.2" should be passed, and will have "neon/x.y.z"
+appended in the actual header sent.
+
+When a session has been finished with, it should be destroyed using
+ne_session_destroy. Any subsequent operations on the session object
+will have undefined results (i.e. will segfault).
+
+Low-level HTTP Request/Response Handling
+----------------------------------------
+
+...
+
+
diff --git a/doc/using.xml b/doc/using.xml
new file mode 100644
index 0000000..efb53d1
--- /dev/null
+++ b/doc/using.xml
@@ -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/doc/xml.xml b/doc/xml.xml
new file mode 100644
index 0000000..c001073
--- /dev/null
+++ b/doc/xml.xml
@@ -0,0 +1,207 @@
+<!-- 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
+ support</ulink>.</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>
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..ebc6691
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/macros/ChangeLog b/macros/ChangeLog
new file mode 100644
index 0000000..5a5ac37
--- /dev/null
+++ b/macros/ChangeLog
@@ -0,0 +1,929 @@
+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 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.
+
+Wed May 10 14:33:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon-checks.m4: New file.
+
+Wed May 10 14:26:57 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon-xml-parser.m4 (NEON_XML_PARSER): Use "neon_" prefix for
+ variables.
+
+Wed May 10 13:47:04 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * acconfig.h: New file.
+
+Wed May 10 13:42:16 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon-xml-parser.m4: New file.
+
+Sun May 7 21:57:32 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * gnome-x-checks.m4 (GNOME_X_CHECKS): Check for Gtk 1.2.7 or
+ later, passing "gthread" module argument.
+
diff --git a/macros/neon-test.m4 b/macros/neon-test.m4
new file mode 100644
index 0000000..3e5f052
--- /dev/null
+++ b/macros/neon-test.m4
@@ -0,0 +1,43 @@
+# 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.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the neon library code
+# itself may be copied and distributed under the terms of the GNU
+# LGPL, see COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version.
+# Please send any feedback to <neon@webdav.org>
+
+# Tests needed for the neon-test common test code.
+
+AC_DEFUN([NEON_TEST], [
+
+AC_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)
+
+AC_REQUIRE([NE_FIND_AR])
+
+NEON_FORMAT(time_t, [
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif])
+
+])
diff --git a/macros/neon-xml-parser.m4 b/macros/neon-xml-parser.m4
new file mode 100644
index 0000000..0df534a
--- /dev/null
+++ b/macros/neon-xml-parser.m4
@@ -0,0 +1,144 @@
+# Copyright (C) 1998-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.
+# 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
+ $1
+fi
+])
+
+dnl Configure for a bundled expat build.
+AC_DEFUN([NE_XML_BUNDLED_EXPAT], [
+
+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
+
+# 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"
+ # no dependency on libexpat => crippled libneon, so do partial install
+ ALLOW_INSTALL=lib
+ 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
+
+])
diff --git a/macros/neon.m4 b/macros/neon.m4
new file mode 100644
index 0000000..6b0cee6
--- /dev/null
+++ b/macros/neon.m4
@@ -0,0 +1,850 @@
+# Copyright (C) 1998-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.
+# 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>
+
+#
+# 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_DEFUN([NEON_VPATH_BUNDLED],[
+
+neon_bundled_srcdir=$1
+neon_bundled_builddir=$2
+NEON_COMMON_BUNDLED([$3], [$4])
+
+])
+
+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
+
+])
+
+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=0
+NEON_VERSION_TAG=
+
+NEON_VERSION="${NEON_VERSION_MAJOR}.${NEON_VERSION_MINOR}.${NEON_VERSION_RELEASE}${NEON_VERSION_TAG}"
+
+# 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).
+
+ # 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
+ 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
+
+# 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
+ 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_save_LIBS=$LIBS
+ne_cv_libsfor_$1="not found"
+for lib in $2; do
+ LIBS="$ne_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_save_LIBS -l$lib $3 $NEON_LIBS"
+ AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib $3"; break])])
+done
+LIBS=$ne_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 Wrapper for AC_CHECK_FUNCS; uses libraries from $NEON_LIBS.
+AC_DEFUN([NE_CHECK_FUNCS], [
+ne_save_LIBS=$LIBS
+LIBS="$LIBS $NEON_LIBS"
+AC_CHECK_FUNCS($@)
+LIBS=$ne_save_LIBS])
+
+dnl Checks needed when compiling the neon source.
+AC_DEFUN([LIBNEON_SOURCE_CHECKS], [
+
+dnl Run all the normal C language/compiler tests
+AC_REQUIRE([NEON_COMMON_CHECKS])
+
+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 netdb.h])
+
+AC_REQUIRE([NE_SNPRINTF])
+
+AC_REPLACE_FUNCS(strcasecmp)
+
+AC_CHECK_FUNCS(signal setvbuf setsockopt stpcpy)
+
+# Unixware 7 can only link gethostbyname with -lnsl -lsocket
+# Pick up -lsocket first, then the gethostbyname check will work.
+NE_SEARCH_LIBS(socket, socket inet)
+NE_SEARCH_LIBS(gethostbyname, nsl)
+
+# 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])
+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()
+
+AC_SUBST(NEON_CFLAGS)
+AC_SUBST(NEON_LIBS)
+
+])
+
+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
+
+NEON_COMMON_BUILD($#, $*)
+
+])
+
+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 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"
+ 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])])
diff --git a/macros/socklen-arg-type.m4 b/macros/socklen-arg-type.m4
new file mode 100644
index 0000000..2d84ac4
--- /dev/null
+++ b/macros/socklen-arg-type.m4
@@ -0,0 +1,43 @@
+dnl This function is (C) 1997,98,99 Stephan Kulow (coolo@kde.org)
+dnl Modifications (C) Joe Orton 1999,2000
+
+AC_DEFUN([SOCKLEN_ARG_TYPE],[
+
+dnl Check for the type of the third argument of getsockname
+AC_MSG_CHECKING(for the third argument of getsockname)
+AC_CACHE_VAL(ac_cv_ksize_t,
+[AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+],[
+socklen_t a=0;
+getsockname(0,(struct sockaddr*)0, &a);
+],
+ac_cv_ksize_t=socklen_t,
+ac_cv_ksize_t=)
+if test -z "$ac_cv_ksize_t"; then
+ac_safe_cflags="$CFLAGS"
+if test "$GCC" = "yes"; then
+ CFLAGS="-Werror $CFLAGS"
+fi
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+],[
+int a=0;
+getsockname(0,(struct sockaddr*)0, &a);
+],
+ac_cv_ksize_t=int,
+ac_cv_ksize_t=size_t)
+CFLAGS="$ac_safe_cflags"
+fi
+])
+
+if test -z "$ac_cv_ksize_t"; then
+ ac_cv_ksize_t=int
+fi
+
+AC_MSG_RESULT($ac_cv_ksize_t)
+AC_DEFINE_UNQUOTED(ksize_t, $ac_cv_ksize_t, [Define to be the type of the third argument to getsockname])
+
+]) \ No newline at end of file
diff --git a/neon-config.in b/neon-config.in
new file mode 100644
index 0000000..27b74d1
--- /dev/null
+++ b/neon-config.in
@@ -0,0 +1,105 @@
+#! /bin/sh
+# Originally from libxml, Copyright (C) Daniel Veillard
+# Modifications for neon Copyright (C) 2000-2002 Joe Orton.
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+libdir=@libdir@
+
+usage()
+{
+ cat <<EOF
+Usage: neon-config [OPTION]
+
+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
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+
+ case "$1" in
+ --prefix=*)
+ prefix=$optarg
+ ;;
+
+ --prefix)
+ echo $prefix
+ ;;
+
+ --version)
+ echo neon @NEON_VERSION@
+ exit 0
+ ;;
+
+ --help)
+ usage 0
+ ;;
+
+ --cflags)
+ echo -I${includedir}/neon @NEON_CFLAGS@
+ ;;
+
+ --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
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+exit 0
diff --git a/neon.mak b/neon.mak
new file mode 100644
index 0000000..14d0d30
--- /dev/null
+++ b/neon.mak
@@ -0,0 +1,186 @@
+#**** neon Win32 -*- Makefile -*- ********************************************
+#
+# Define DEBUG_BUILD to create a debug version of the library.
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+########
+# Support for Expat integration
+# IF EXPAT_SRC is set, then assume compiling against a pre-built
+# binary Expat 1.95.X. If EXPAT_SRC is not set, then the user can
+# still set EXPAT_FLAGS to specify very specific compile behavior.
+# If both EXPAT_SRC and EXPAT_FLAGS are not set, disable WebDAV
+# support.
+BUILD_EXPAT = 1
+!IF "$(EXPAT_SRC)" == ""
+!IF "$(EXPAT_FLAGS)" == ""
+EXPAT_FLAGS = /D NEON_NODAV
+BUILD_EXPAT =
+!ENDIF
+!ELSE
+EXPAT_FLAGS = /I "$(EXPAT_SRC)\Source\Lib" /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 $(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:"$(TARGET)"
+
+LIB32_OBJS= \
+ "$(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_md5.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"
+
+!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"
+ -@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_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 "$(TARGET)"
+ -@erase ".\src\config.h"
+
+"$(TARGET)": $(DEF_FILE) $(LIB32_OBJS)
+ -@if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+ $(LIB32) @<<
+ $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+{src}.c{$(INTDIR)}.obj::
+ -@if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+ $(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
+<<
+
+"$(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.pc.in b/neon.pc.in
new file mode 100644
index 0000000..9d431de
--- /dev/null
+++ b/neon.pc.in
@@ -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@
diff --git a/src/.cvsignore b/src/.cvsignore
new file mode 100644
index 0000000..5561c9e
--- /dev/null
+++ b/src/.cvsignore
@@ -0,0 +1,15 @@
+*.lo
+.libs
+Makefile
+libneon.la
+libneon.a
+neon_config.h
+*.diff
+c++.c
+checkincl.c
+*.*.*
+*.out
+*log
+*.bb
+*.da
+*.bbg
diff --git a/src/COPYING.LIB b/src/COPYING.LIB
new file mode 100644
index 0000000..161a3d1
--- /dev/null
+++ b/src/COPYING.LIB
@@ -0,0 +1,482 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/src/ChangeLog b/src/ChangeLog
new file mode 100644
index 0000000..2893290
--- /dev/null
+++ b/src/ChangeLog
@@ -0,0 +1,4929 @@
+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.
+
+Wed May 10 17:46:48 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * uri.c (uri_parse, uri_free): New functions.
+
+Wed May 10 17:43:37 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (get_to_fd, http_get): Set error appropriately if
+ fwrite() fails.
+
+Wed May 10 14:25:38 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_utils.c (http_debug): New function.
+
+Wed May 10 14:25:08 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (get_callback): Call sock_call_progress.
+
+Wed May 10 14:24:20 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.c (sock_call_progress): New function. (many places): Use
+ it.
+
+Wed May 10 14:22:48 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * uri.c (uri_has_trailing_slash): Moved from being inline.
+
+Tue May 9 23:34:25 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.c: Use handler as userdata for 207 callbacks, unified
+ handler and context structures. (start_prop, end_prop,
+ start_propelm, end_propelm): Removed functions.
+ (dav_propfind_get_current_resource): New function.
+
+Tue May 9 23:29:44 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * xalloc.[ch]: New files.
+
+Tue May 9 23:05:47 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.[ch]: Removed property and property element callbacks.
+
+Tue May 9 23:01:00 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c: Use separate name/namespace for element names.
+ (dav_207_init_with_handler): New function. (end_element):
+ Unescape URI in href element.
+
+Tue May 9 19:54:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.c (dav_propfind_allprop, dav_propfind_named, propfind,
+ start_response, end_response, start_prop, end_prop, start_propelm,
+ end_propelm): New functions; PROPFIND support.
+
+Tue May 9 19:45:17 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (build_request): Renamed from make_request.
+
+Tue May 9 19:36:01 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.[ch]: Added sock_block_reader.
+
+Tue May 9 15:52:56 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * uri.c (uri_childof): Return false when parent is the same length
+ as child.
+
+Sun May 7 15:07:49 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c: Separated element namespace/names.
+
+Tue May 2 16:40:59 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.[ch]: Added HIP_XML_UTF8DECODE flag.
+
+Tue May 2 16:16:57 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.[ch]: Separate element name and namespace.
+
+Mon May 1 00:21:24 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (dav_accept_207): Moved function from dav_basic.c.
+
+ * dav_basic.c (dav_accept_207, dav_parse_xml_block): Removed
+ functions.
+
+Sun Apr 30 22:47:47 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.[ch]: Renamed dav_proppatch_item to
+ dav_proppatch_operation.
+
+Sun Apr 30 22:45:04 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c (start_element): Clearer error message.
+
+Sun Apr 30 19:12:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_content_type_handler, dav_hdr_handler): New
+ functions. (http_options): Handle DAV header.
+
+Sun Apr 30 18:08:53 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.c (dav_proppatch): New function.
+
+Sun Apr 30 18:05:55 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (handle_error): New function. (end_response,
+ end_propstat): Use it. (dav_simple_request): Don't return the 207
+ error string if we get all 2xx class status elements.
+
+Sun Apr 30 16:56:41 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (dav_add_depth_header): New function.
+
+Sun Apr 30 14:49:06 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (start_element): Unknown element is only a property if
+ the parent is DAV:propstat.
+
+Sun Apr 30 14:43:28 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (end_response, end_propstat): Only write error line
+ if we have status information and the status is not a 424.
+
+Sun Apr 30 14:28:23 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.h: Added DAV_DEPTH_*.
+
+Sun Apr 30 12:47:50 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (check_context): Allow (and ignore) unknown elements
+ anywhere other than as the root.
+
+Sun Apr 30 12:35:39 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.h (ASC2HEX, HEX2ASC): New macros.
+
+Sun Apr 30 12:34:37 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c [STANDALONE]: Removed. (everywhere): Switch to using
+ md5_to_ascii rather than md5_hexify.
+
+Sun Apr 30 12:32:35 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (read_response_block): Fixed to return errors
+ properly and block length to parameter. (read_response_body):
+ Changed accordingly.
+
+Sun Apr 30 12:29:45 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c (friendly_name): New function, was PRETTY_NAME macro.
+ (start_element, end_element): Fix COLLECT handling.
+ (hip_xml_parse): Only write parse error if the document has not
+ already been marked invalid.
+
+Sun Apr 30 12:28:36 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (dav_simple_request): Rewritten for new 207
+ interface. (start_response, end_response, end_propstat): New
+ functions.
+
+Sun Apr 30 12:27:52 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (dav_207_error): Return the parser error.
+
+Sat Apr 29 14:46:48 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.c (sock_register_progress, sock_register_notify): New
+ functions. (everywhere): Use progress + notify callbacks rather
+ than fe_*.
+
+Sat Apr 29 14:15:23 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.c (md5_to_ascii, ascii_to_md5): New functions.
+
+Sat Apr 29 13:55:39 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c (hip_xml_init): abort() on out-of-memory.
+
+Sat Apr 29 12:56:11 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon_i18n.h: New file.
+
+Sat Apr 29 12:55:24 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.[ch]: Re-implemented with sensible interface.
+
+Fri Apr 28 14:56:01 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c (http_auth_request_header): Renamed from
+ http_auth_request.
+
+ * http_request.c (make_request): As above.
+
+Thu Apr 13 11:52:14 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_put): Switched URI and stream arguments.
+
+Thu Apr 13 09:51:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c: Added user_agent field to session structure.
+ (http_set_useragent): New function. (add_fixed_headers): Only set
+ user-agent if sess->user_agent is set.
+
+Thu Apr 13 09:49:32 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (lookup_host): New function, split from
+ set_hostinfo. (set_hostinfo): Doesn't perform DNS lookup.
+ (http_session_server): Don't do a DNS lookup if we have a proxy.
+
+Wed Apr 12 22:32:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (http_request_dispatch, http_request_create):
+ Store auth header values in local variables rather than request
+ structure. (http_request_create): Don't leak everything on error.
+ Handle http_auth_challenge return value.
+
+Wed Apr 12 22:30:06 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_options): Pass server capabilites object,
+ parse Server header to detect Apache/1.3.6 and before, indicating
+ broken 100-continue support. (server_hdr_handler): New function.
+
+Mon Apr 10 17:42:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.c: Use 'int' for return values.
+
+Mon Apr 10 17:41:40 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c (is_in_domain): Dummy implementation.
+
+Mon Apr 10 17:40:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c: Handle read() returning 0 when it shouldn't.
+ i18n'ized error messages.
+
+Mon Apr 10 14:45:09 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dates.[ch], md5.[ch], base64.[ch]: Imported date handling
+ utilities, MD5 checksum functions, and text->base64 converter.
+
+Mon Apr 10 14:44:08 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * Makefile.incl: Dependancies updated for socket.[ch].
+
+Mon Apr 10 14:43:36 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c: Replaced malloc() calls with xmalloc() calls.
+
+Mon Apr 10 14:42:35 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c, uri.c, string_utils.h: Replaced malloc() calls with
+ xmalloc() calls.
+
+Mon Apr 10 14:41:40 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.[ch]: Imported socket handling utilities.
+
+Mon Apr 10 14:36:03 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.h (CONCAT*): Use xmalloc.
+
+Mon Apr 10 13:52:17 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (set_sockerr): Added handling for socket errors.
+
+Sat Apr 8 13:49:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.[ch]: Imported string utilites.
+
+Sat Apr 8 00:26:06 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (http_set_persist, http_set_expect100): New
+ functions.
+
+Sat Apr 8 00:25:37 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_options): New function.
+
+Fri Apr 7 13:01:35 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon.h: New file.
+
+Fri Apr 7 12:59:40 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (normalize_response_length, read_response_body):
+ New functions. (http_add_response_body_reader): Take a callback
+ to determine whether the body reader wants to read the response
+ body.
+
+Fri Apr 7 11:46:41 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (http_set_server_auth, http_set_proxy_auth): New
+ functions. (give_creds): Use supplied callbacks for
+ authentication. (get_request_bodysize): Send Content-Length: 0 if
+ no entity-body is being sent with a request. (te_hdr_handler,
+ connection_hdr_handler): New functions. (make_request): Don't use
+ Expect: 100-continue if server is not HTTP/1.1 compliant.
+ (read_message_header): Only read until HTTP_MAXIMUM_HEADER_LENGTH
+ bytes of header have been read. (read_response_headers): No
+ hard-coded header handling. (http_request_create): Set
+ req->method_is_head here.
+
+Thu Apr 6 14:39:28 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c [HIP_XML_DECODE_UTF8] (decode_utf8_double): New
+ function. (char_data) [HIP_XML_DECODE_UTF8]: Decode UTF-8.
+
+Tue Mar 28 13:54:51 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * Makefile.incl: Imported makefile fragment.
+
+Tue Mar 28 13:54:09 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.[ch] (http_get_error): New function.
+
+Thu Mar 23 18:48:42 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.[ch]: Imported generic XML parsing layer.
+
+ * dav_207.[ch]: Imported generic WebDAV 207 response handling.
+
+ * dav_basic.[ch]: Imported/implemented DAV response handling and
+ basic Class 1 namespace methods.
+
+Thu Mar 23 18:46:14 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (add_hooks, run_hooks, http_add_destroy_hook):
+ Adding hooks support. (add_fixed_headers): Send TE token in
+ Connection header. Only send Keep-Alive header & token to pre-1.1
+ origin servers (i.e., not proxies).
+
+Thu Mar 23 12:49:01 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.[ch], uri.[ch]: Imported HTTP authentication and URI
+ handling modules.
+
+Thu Mar 23 12:47:05 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_utils.c: Imported HTTP utility functions.
+
+Thu Mar 23 12:44:38 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.[ch]: Implemented modular HTTP request handling.
+
+ * http_basic.[ch]: Implemented basic HTTP methods GET, PUT, and
+ PUT with If-Unmodified.
+
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..04b308e
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,147 @@
+#
+# neon source directory Makefile
+#
+# Use the NEON_NORMAL_BUILD or NEON_LIBTOOL_BUILD autoconf
+# macros to set up this Makefile correctly.
+#
+
+SHELL = @SHELL@
+
+# Installation paths
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+
+# Build paths
+VPATH = @srcdir@
+top_builddir = ..
+top_srcdir = @top_srcdir@
+
+# Toolchain settings.
+CC = @CC@
+AR = @AR@
+RANLIB = @RANLIB@
+LIBTOOL = @LIBTOOL@
+
+# 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@
+
+COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS)
+LINK = $(LIBTOOL) --quiet --mode=link $(CC) $(LDFLAGS)
+
+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@
+
+NEON_DAVOBJS = $(NEON_BASEOBJS) \
+ ne_207.@NEON_OBJEXT@ ne_xml.@NEON_OBJEXT@ \
+ ne_props.@NEON_OBJEXT@ ne_locks.@NEON_OBJEXT@
+
+OBJECTS = @NEONOBJS@ @NEON_EXTRAOBJS@
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o
+
+NEON_TARGET = @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 $@
+.c.o:
+ $(COMPILE) -c $< -o $@
+
+libneon.la: $(OBJECTS)
+ $(LINK) -rpath $(libdir) $(NEON_LINK_FLAGS) -o $@ $(OBJECTS) $(LIBS)
+
+libneon.a: $(OBJECTS)
+ $(AR) cru $@ $(OBJECTS)
+ $(RANLIB) $@
+
+clean:
+ rm -f $(NEON_TARGET) *.o *.lo
+ rm -rf .libs
+
+c++.c:
+ find . -name ne_\*.h -print | sed 's/.*/#include "&"/;/ne_private/d' > $@
+ echo "int main(void) {}" >> $@
+
+check-c++: c++.c
+ c++ -I. c++.c
+
+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
+
+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
+
+ne_request.@NEON_OBJEXT@: ne_request.c $(neonreq) ne_i18n.h ne_private.h \
+ ne_uri.h
+
+ne_session.@NEON_OBJEXT@: ne_session.c ne_session.h ne_alloc.h \
+ ne_utils.h ne_private.h $(top_builddir)/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
+
+ne_socket.@NEON_OBJEXT@: ne_socket.c ne_socket.h $(top_builddir)/config.h \
+ ne_privssl.h ne_string.h
+
+ne_auth.@NEON_OBJEXT@: ne_auth.c ne_auth.h $(neonreq) \
+ ne_dates.h ne_md5.h ne_uri.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_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
+
+ne_alloc.@NEON_OBJEXT@: ne_alloc.c ne_alloc.h $(top_builddir)/config.h
+
+ne_dates.@NEON_OBJEXT@: ne_dates.c ne_dates.h $(top_builddir)/config.h
+
+ne_uri.@NEON_OBJEXT@: ne_uri.c ne_uri.h ne_utils.h ne_string.h ne_alloc.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
+
+ne_redirect.@NEON_OBJEXT@: ne_redirect.c $(neonreq) ne_redirect.h \
+ ne_uri.h ne_private.h
+
+ne_cookies.@NEON_OBJEXT@: ne_cookies.c $(neonreq) ne_cookies.h ne_uri.h \
+ ne_private.h
+
+ne_compress.@NEON_OBJEXT@: ne_compress.c $(neonreq) ne_compress.h
+
+ne_acl.@NEON_OBJEXT@: ne_acl.c ne_acl.h $(neonreq)
diff --git a/src/README b/src/README
new file mode 100644
index 0000000..0cb9a3f
--- /dev/null
+++ b/src/README
@@ -0,0 +1,15 @@
+
+This is the source directory of the 'neon' HTTP/WebDAV client library,
+which can be bundled inside other packages. For the complete neon
+package, see:
+
+ http://www.webdav.org/neon/
+
+This source directory may be distributed and/or modified under the
+terms of the GNU Library General Public License, as given in
+COPYING.LIB.
+
+Please send questions, bug reports, feature requests, etc, regarding
+the neon library, to the mailing list at:
+
+ neon@webdav.org
diff --git a/src/memleak.h b/src/memleak.h
new file mode 100644
index 0000000..8d77dcb
--- /dev/null
+++ b/src/memleak.h
@@ -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 */
diff --git a/src/ne_207.c b/src/ne_207.c
new file mode 100644
index 0000000..ad5e6a1
--- /dev/null
+++ b/src/ne_207.c
@@ -0,0 +1,345 @@
+/*
+ WebDAV 207 multi-status response handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* Generic handling for WebDAV 207 Multi-Status responses. */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_utils.h"
+#include "ne_xml.h"
+#include "ne_207.h"
+#include "ne_uri.h"
+#include "ne_basic.h"
+
+#include "ne_i18n.h"
+
+struct ne_207_parser_s {
+ ne_207_start_response *start_response;
+ ne_207_end_response *end_response;
+ ne_207_start_propstat *start_propstat;
+ ne_207_end_propstat *end_propstat;
+ ne_xml_parser *parser;
+ void *userdata;
+
+ 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;
+};
+
+#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)
+{
+ p->start_response = start;
+ p->end_response = end;
+}
+
+void ne_207_set_propstat_handlers(ne_207_parser *p,
+ ne_207_start_propstat start,
+ ne_207_end_propstat end)
+{
+ p->start_propstat = start;
+ p->end_propstat = end;
+}
+
+void *ne_207_get_current_response(ne_207_parser *p)
+{
+ return p->response;
+}
+
+void *ne_207_get_current_propstat(ne_207_parser *p)
+{
+ return p->propstat;
+}
+
+/* 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;
+
+ 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, int state, const char *nspace, const char *name)
+{
+ ne_207_parser *p = userdata;
+ const char *cdata = p->cdata->data;
+
+ switch (state) {
+ case ELM_responsedescription:
+ if (HAVE_CDATA(p)) {
+ NE_FREE(p->description);
+ p->description = ne_strdup(cdata);
+ }
+ break;
+ case ELM_href:
+ /* Now we have the href, begin the response */
+ if (p->start_response && HAVE_CDATA(p)) {
+ p->response = p->start_response(p->userdata, cdata);
+ p->in_response = 1;
+ }
+ break;
+ 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);
+ 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);
+ return -1;
+ } else {
+ NE_DEBUG(NE_DBG_XML, "Decoded status line: %s\n", cdata);
+ }
+ }
+ break;
+ 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.reason_phrase);
+ break;
+ 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;
+ p->in_response = 0;
+ NE_FREE(p->status.reason_phrase);
+ NE_FREE(p->description);
+ break;
+ }
+ return 0;
+}
+
+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, start_element, cdata_207, end_element, p);
+
+ return p;
+}
+
+void ne_207_destroy(ne_207_parser *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, const ne_status *status)
+{
+ return (status->code == 207);
+}
+
+/* Handling of 207 errors: we keep a string buffer, and append
+ * messages to it as they come down.
+ *
+ * Note, 424 means it would have worked but something else went wrong.
+ * We will have had the error for "something else", so we display
+ * that, and skip 424 errors. */
+
+/* This is passed as userdata to the 207 code. */
+struct context {
+ char *href;
+ ne_buffer *buf;
+ unsigned int is_error;
+};
+
+static void *start_response(void *userdata, const char *href)
+{
+ struct context *ctx = userdata;
+ NE_FREE(ctx->href);
+ ctx->href = ne_strdup(href);
+ return NULL;
+}
+
+static void handle_error(struct context *ctx, const ne_status *status,
+ const char *description)
+{
+ if (status && status->klass != 2 && status->code != 424) {
+ char buf[50];
+ ctx->is_error = 1;
+ 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. */
+ ne_buffer_concat(ctx->buf, " -> ", description, "\n", NULL);
+ }
+ }
+
+}
+
+static void end_response(void *userdata, void *response,
+ const ne_status *status, const char *description)
+{
+ struct context *ctx = userdata;
+ handle_error(ctx, status, description);
+}
+
+static void
+end_propstat(void *userdata, void *propstat,
+ const ne_status *status, const char *description)
+{
+ struct context *ctx = userdata;
+ 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;
+ struct context ctx = {0};
+ ne_207_parser *p207;
+ ne_xml_parser *p;
+
+ p = ne_xml_create();
+ p207 = ne_207_create(p, &ctx);
+ /* The error string is progressively written into the
+ * ne_buffer by the element callbacks */
+ ctx.buf = ne_buffer_create();
+
+ ne_207_set_response_handlers(p207, start_response, end_response);
+ ne_207_set_propstat_handlers(p207, NULL, end_propstat);
+
+ ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK) {
+ if (ne_get_status(req)->code == 207) {
+ if (!ne_xml_valid(p)) {
+ /* The parse was invalid */
+ ne_set_error(sess, ne_xml_get_error(p));
+ ret = NE_ERROR;
+ } else if (ctx.is_error) {
+ /* If we've actually got any error information
+ * from the 207, then set that as the error */
+ ne_set_error(sess, ctx.buf->data);
+ ret = NE_ERROR;
+ }
+ } else if (ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+ }
+
+ ne_207_destroy(p207);
+ ne_xml_destroy(p);
+ ne_buffer_destroy(ctx.buf);
+ NE_FREE(ctx.href);
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
diff --git a/src/ne_207.h b/src/ne_207.h
new file mode 100644
index 0000000..890ce84
--- /dev/null
+++ b/src/ne_207.h
@@ -0,0 +1,93 @@
+/*
+ WebDAV 207 multi-status response handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DAV207_H
+#define DAV207_H
+
+#include "ne_xml.h"
+#include "ne_request.h" /* for ne_request */
+
+BEGIN_NEON_DECLS
+
+/* 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);
+
+typedef struct ne_207_parser_s ne_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);
+
+/* Register response handling callbacks. */
+void ne_207_set_response_handlers(ne_207_parser *p,
+ ne_207_start_response *start,
+ ne_207_end_response *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, const ne_status *status);
+
+void *ne_207_get_current_propstat(ne_207_parser *p);
+void *ne_207_get_current_response(ne_207_parser *p);
+
+/* Dispatch a DAV request and handle a 207 error response appropriately */
+int ne_simple_request(ne_session *sess, ne_request *req);
+
+END_NEON_DECLS
+
+#endif /* DAV207_H */
diff --git a/src/ne_acl.c b/src/ne_acl.c
new file mode 100644
index 0000000..c540a0e
--- /dev/null
+++ b/src/ne_acl.c
@@ -0,0 +1,130 @@
+/*
+ Access control
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* Contributed by Arun Garg <arung@pspl.co.in> */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_locks.h"
+#include "ne_alloc.h"
+#include "ne_string.h"
+#include "ne_acl.h"
+#include "ne_uri.h"
+#include "ne_xml.h" /* for NE_XML_MEDIA_TYPE */
+
+static ne_buffer *acl_body(ne_acl_entry *right, int count)
+{
+ ne_buffer *body = ne_buffer_create();
+ int m;
+
+ ne_buffer_zappend(body,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<acl xmlns='DAV:'>" EOL);
+
+ for (m = 0; m < count; m++) {
+ const char *type;
+
+ type = (right[m].type == ne_acl_grant ? "grant" : "deny");
+
+ ne_buffer_concat(body, "<ace>" EOL "<principal>", NULL);
+
+ switch (right[m].apply) {
+ case ne_acl_all:
+ ne_buffer_zappend(body, "<all/>" EOL);
+ break;
+ case ne_acl_property:
+ ne_buffer_concat(body, "<property><", right[m].principal,
+ "/></property>" EOL, NULL);
+ break;
+ case ne_acl_href:
+ ne_buffer_concat(body, "<href>", right[m].principal,
+ "</href>" EOL, NULL);
+ break;
+ }
+
+ ne_buffer_concat(body, "</principal>" EOL "<", type, ">" EOL, NULL);
+
+ if (right[m].read == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<read/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].read_acl == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<read-acl/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].write == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<write/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].write_acl == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<write-acl/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].read_cuprivset == 0)
+ ne_buffer_concat(body,
+ "<privilege>"
+ "<read-current-user-privilege-set/>"
+ "</privilege>" EOL, NULL);
+ ne_buffer_concat(body, "</", type, ">" EOL, NULL);
+ ne_buffer_zappend(body, "</ace>" EOL);
+ }
+ ne_buffer_zappend(body, "</acl>" EOL);
+
+ return body;
+}
+
+int ne_acl_set(ne_session *sess, const char *uri,
+ ne_acl_entry *entries, int numentries)
+{
+ int ret;
+ ne_request *req = ne_request_create(sess, "ACL", uri);
+ ne_buffer *body = acl_body(entries, numentries);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, 0);
+#endif
+
+ ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+ ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
+ ret = ne_request_dispatch(req);
+
+ ne_buffer_destroy(body);
+
+ if (ret == NE_OK && ne_get_status(req)->code == 207) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+ return ret;
+}
diff --git a/src/ne_acl.h b/src/ne_acl.h
new file mode 100644
index 0000000..bf1a1ec
--- /dev/null
+++ b/src/ne_acl.h
@@ -0,0 +1,56 @@
+/*
+ Access control
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_ACL_H
+#define NE_ACL_H
+
+#include "ne_session.h"
+
+BEGIN_NEON_DECLS
+
+typedef struct
+{
+ enum {
+ ne_acl_href,
+ ne_acl_property,
+ ne_acl_all
+ } apply;
+
+ enum {
+ ne_acl_grant,
+ ne_acl_deny
+ } type;
+
+ char *principal;
+ int read;
+ int read_acl;
+ int write;
+ int write_acl;
+ int read_cuprivset;
+} ne_acl_entry;
+
+/* Set the ACL for the given resource to the list of ACL entries. */
+int ne_acl_set(ne_session *sess, const char *uri,
+ ne_acl_entry entries[], int numentries);
+
+END_NEON_DECLS
+
+#endif /* NE_ACL_H */
diff --git a/src/ne_alloc.c b/src/ne_alloc.c
new file mode 100644
index 0000000..1bee5aa
--- /dev/null
+++ b/src/ne_alloc.c
@@ -0,0 +1,208 @@
+/*
+ Replacement memory allocation handling etc.
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <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;
+ DO_MALLOC(ptr, len);
+ return ptr;
+}
+
+void *ne_calloc(size_t 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)
+{
+ char *ret;
+ DO_MALLOC(ret, strlen(s) + 1);
+ return strcpy(ret, s);
+}
+
+char *ne_strndup(const char *s, size_t n)
+{
+ 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 */
diff --git a/src/ne_alloc.h b/src/ne_alloc.h
new file mode 100644
index 0000000..1da4a38
--- /dev/null
+++ b/src/ne_alloc.h
@@ -0,0 +1,58 @@
+/*
+ Replacement memory allocation handling etc.
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_ALLOC_H
+#define NE_ALLOC_H
+
+#ifdef WIN32
+#include <stdlib.h>
+#else
+#include <sys/types.h>
+#endif
+
+#include "ne_defs.h"
+
+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 */
diff --git a/src/ne_auth.c b/src/ne_auth.c
new file mode 100644
index 0000000..ba0095c
--- /dev/null
+++ b/src/ne_auth.c
@@ -0,0 +1,1026 @@
+/*
+ HTTP Authentication routines
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+
+/* HTTP Authentication, as per RFC2617.
+ *
+ * TODO:
+ * - 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
+
+#ifdef NEON_SSL
+#include <openssl/rand.h>
+#endif
+
+#include <time.h>
+
+#include "ne_md5.h"
+#include "ne_dates.h"
+#include "ne_request.h"
+#include "ne_auth.h"
+#include "ne_string.h"
+#include "ne_utils.h"
+#include "ne_alloc.h"
+#include "ne_uri.h"
+#include "ne_i18n.h"
+
+/* TODO: should remove this eventually. Need it for
+ * ne_pull_request_body. */
+#include "ne_private.h"
+
+#define HOOK_SERVER_ID "http://webdav.org/neon/hooks/server-auth"
+#define HOOK_PROXY_ID "http://webdav.org/neon/hooks/proxy-auth"
+
+/* The authentication scheme we are using */
+typedef enum {
+ auth_scheme_basic,
+ auth_scheme_digest
+} auth_scheme;
+
+typedef enum {
+ auth_alg_md5,
+ auth_alg_md5_sess,
+ auth_alg_unknown
+} auth_algorithm;
+
+/* Selected method of qop which the client is using */
+typedef enum {
+ auth_qop_none,
+ auth_qop_auth,
+ auth_qop_auth_int
+} auth_qop;
+
+/* A challenge */
+struct auth_challenge {
+ auth_scheme scheme;
+ 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 */
+ unsigned int qop_auth_int:1; /* "auth-int" token in qop attrib */
+ auth_algorithm alg;
+ struct auth_challenge *next;
+};
+
+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
+};
+
+/* Authentication session state. */
+typedef struct {
+ ne_session *sess;
+
+ /* 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_auth_creds creds;
+ void *userdata;
+
+ /*** Session details ***/
+
+ /* The username and password we are using to authenticate with */
+ char username[NE_ABUFSIZ];
+ /* Whether we CAN supply authentication at the moment */
+ unsigned int can_handle:1;
+ /* This used for Basic auth */
+ char *basic;
+ /* These all used for Digest auth */
+ char *realm;
+ char *nonce;
+ char *cnonce;
+ char *opaque;
+ auth_qop qop;
+ auth_algorithm alg;
+ int nonce_count;
+ /* The ASCII representation of the session's H(A1) value */
+ char h_a1[33];
+
+ /* Temporary store for half of the Request-Digest
+ * (an optimisation - used in the response-digest calculation) */
+ struct ne_md5_ctx stored_rdig;
+
+ /* Details of server... needed to reconstruct absoluteURI's when
+ * necessary */
+ const char *host;
+ const char *uri_scheme;
+ unsigned int port;
+
+ int attempt;
+} auth_session;
+
+struct auth_request {
+ /*** Per-request details. ***/
+ ne_request *request; /* the request object. */
+
+ /* The method and URI we are using for the current request */
+ const char *uri;
+ const char *method;
+ /* Whether we WILL supply authentication for this request or not */
+ unsigned int will_handle:1;
+
+ /* Used for calculation of H(entity-body) of the response */
+ struct ne_md5_ctx response_body;
+
+ /* Results of response-header callbacks */
+ char *auth_hdr, *auth_info_hdr;
+};
+
+static void clean_session(auth_session *sess)
+{
+ sess->can_handle = 0;
+ NE_FREE(sess->basic);
+ NE_FREE(sess->nonce);
+ NE_FREE(sess->cnonce);
+ NE_FREE(sess->opaque);
+ NE_FREE(sess->realm);
+}
+
+/* Returns client nonce string. */
+static char *get_cnonce(void)
+{
+ char data[256], ret[33];
+ unsigned char tmp[16];
+ struct ne_md5_ctx hash;
+
+ ne_md5_init_ctx(&hash);
+
+#ifdef NEON_SSL
+ if (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. */
+
+ /* 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
+ pid_t pid = getpid();
+#endif
+ ne_md5_process_bytes(&pid, sizeof pid, &hash);
+ }
+
+#ifdef NEON_SSL
+ }
+#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)
+{
+ char *tmp, password[NE_ABUFSIZ];
+
+ /* Verify challenge... must have a realm */
+ if (parms->realm == NULL) {
+ return -1;
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got Basic challenge with realm [%s]\n",
+ parms->realm);
+
+ clean_session(sess);
+
+ sess->realm = ne_strdup(parms->realm);
+
+ if (get_credentials(sess, password)) {
+ /* Failed to get credentials */
+ return -1;
+ }
+
+ sess->scheme = auth_scheme_basic;
+
+ tmp = ne_concat(sess->username, ":", password, NULL);
+ sess->basic = ne_base64(tmp, strlen(tmp));
+ ne_free(tmp);
+
+ /* Paranoia. */
+ memset(password, 0, sizeof password);
+
+ return 0;
+}
+
+/* Add Basic authentication credentials to a request */
+static char *request_basic(auth_session *sess)
+{
+ return ne_concat("Basic ", sess->basic, "\r\n", NULL);
+}
+
+/* Examine a digest challenge: return 0 if it is a valid Digest challenge,
+ * else non-zero. */
+static int digest_challenge(auth_session *sess, struct auth_challenge *parms)
+{
+ struct ne_md5_ctx tmp;
+ unsigned char tmp_md5[16];
+ char password[NE_ABUFSIZ];
+
+ /* Verify they've given us the right bits. */
+ if (parms->alg == auth_alg_unknown ||
+ ((parms->alg == auth_alg_md5_sess) &&
+ !(parms->qop_auth || parms->qop_auth_int)) ||
+ parms->realm==NULL || parms->nonce==NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Invalid challenge.");
+ return -1;
+ }
+
+ if (parms->stale) {
+ /* Just a stale response, don't need to get a new username/password */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Stale digest challenge.\n");
+ } else {
+ /* Forget the old session details */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "In digest challenge.\n");
+
+ clean_session(sess);
+
+ sess->realm = ne_strdup(parms->realm);
+
+ /* Not a stale response: really need user authentication */
+ if (get_credentials(sess, password)) {
+ /* Failed to get credentials */
+ return -1;
+ }
+ }
+ sess->alg = parms->alg;
+ sess->scheme = auth_scheme_digest;
+ 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 */
+ }
+
+ if (parms->got_qop) {
+ /* What type of qop are we to apply to the message? */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got qop directive.\n");
+ sess->nonce_count = 0;
+ if (parms->qop_auth_int) {
+ sess->qop = auth_qop_auth_int;
+ } else {
+ sess->qop = auth_qop_auth;
+ }
+ } else {
+ /* No qop at all/ */
+ sess->qop = auth_qop_none;
+ }
+
+ if (!parms->stale) {
+ /* Calculate H(A1).
+ * tmp = H(unq(username-value) ":" unq(realm-value) ":" passwd)
+ */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating H(A1).\n");
+ ne_md5_init_ctx(&tmp);
+ ne_md5_process_bytes(sess->username, strlen(sess->username), &tmp);
+ ne_md5_process_bytes(":", 1, &tmp);
+ ne_md5_process_bytes(sess->realm, strlen(sess->realm), &tmp);
+ ne_md5_process_bytes(":", 1, &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];
+ struct ne_md5_ctx a1;
+ char tmp_md5_ascii[33];
+ /* Now we calculate the SESSION H(A1)
+ * A1 = H(...above...) ":" unq(nonce-value) ":" unq(cnonce-value)
+ */
+ ne_md5_to_ascii(tmp_md5, tmp_md5_ascii);
+ ne_md5_init_ctx(&a1);
+ ne_md5_process_bytes(tmp_md5_ascii, 32, &a1);
+ ne_md5_process_bytes(":", 1, &a1);
+ ne_md5_process_bytes(sess->nonce, strlen(sess->nonce), &a1);
+ ne_md5_process_bytes(":", 1, &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);
+ } else {
+ ne_md5_to_ascii(tmp_md5, sess->h_a1);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "H(A1) is [%s]\n", sess->h_a1);
+ }
+
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "I like this Digest challenge.\n");
+
+ return 0;
+}
+
+/* callback for ne_pull_request_body. */
+static int digest_body(void *userdata, const char *buf, size_t count)
+{
+ struct ne_md5_ctx *ctx = userdata;
+ ne_md5_process_bytes(buf, count, ctx);
+ return 0;
+}
+
+/* Return Digest authentication credentials header value for the given
+ * session. */
+static char *request_digest(auth_session *sess, struct auth_request *req)
+{
+ struct ne_md5_ctx a2, rdig;
+ unsigned char a2_md5[16], rdig_md5[16];
+ char a2_md5_ascii[33], rdig_md5_ascii[33];
+ char nc_value[9] = {0};
+ const char *qop_value; /* qop-value */
+ ne_buffer *ret;
+
+ /* Increase the nonce-count */
+ if (sess->qop != auth_qop_none) {
+ sess->nonce_count++;
+ ne_snprintf(nc_value, 9, "%08x", sess->nonce_count);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Nonce count is %d, nc is [%s]\n",
+ sess->nonce_count, nc_value);
+ }
+
+ qop_value = sess->qop == auth_qop_auth_int ? "auth-int" : "auth";
+
+ /* Calculate H(A2). */
+ ne_md5_init_ctx(&a2);
+ ne_md5_process_bytes(req->method, strlen(req->method), &a2);
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(req->uri, strlen(req->uri), &a2);
+
+ if (sess->qop == auth_qop_auth_int) {
+ struct ne_md5_ctx body;
+ char tmp_md5_ascii[33];
+ unsigned char tmp_md5[16];
+
+ ne_md5_init_ctx(&body);
+
+ /* Calculate H(entity-body): pull in the request body from
+ * where-ever it is coming from, and calculate the digest. */
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body...\n");
+ ne_pull_request_body(req->request, digest_body, &body);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body done.\n");
+
+ ne_md5_finish_ctx(&body, tmp_md5);
+ ne_md5_to_ascii(tmp_md5, tmp_md5_ascii);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "H(entity-body) is [%s]\n", tmp_md5_ascii);
+
+ /* Append to A2 */
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(tmp_md5_ascii, 32, &a2);
+ }
+ ne_md5_finish_ctx(&a2, a2_md5);
+ ne_md5_to_ascii(a2_md5, a2_md5_ascii);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "H(A2): %s\n", a2_md5_ascii);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating Request-Digest.\n");
+ /* Now, calculation of the Request-Digest.
+ * The first section is the regardless of qop value
+ * H(A1) ":" unq(nonce-value) ":" */
+ ne_md5_init_ctx(&rdig);
+
+ /* Use the calculated H(A1) */
+ ne_md5_process_bytes(sess->h_a1, 32, &rdig);
+
+ ne_md5_process_bytes(":", 1, &rdig);
+ ne_md5_process_bytes(sess->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->cnonce, qop_value);
+ ne_md5_process_bytes(nc_value, 8, &rdig);
+ ne_md5_process_bytes(":", 1, &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;
+ ne_md5_process_bytes(qop_value, strlen(qop_value), &rdig);
+ ne_md5_process_bytes(":", 1, &rdig);
+ } else {
+ /* Store a copy of this structure... we do this because the
+ * calculation of the rspauth= field in the Auth-Info header
+ * is the same as this digest, up to this point. */
+ sess->stored_rdig = rdig;
+ }
+ /* And finally, H(A2) */
+ ne_md5_process_bytes(a2_md5_ascii, 32, &rdig);
+ ne_md5_finish_ctx(&rdig, rdig_md5);
+ ne_md5_to_ascii(rdig_md5, rdig_md5_ascii);
+
+ ret = ne_buffer_create();
+
+ ne_buffer_concat(ret,
+ "Digest username=\"", sess->username, "\", "
+ "realm=\"", sess->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) {
+ 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->cnonce, "\", "
+ "nc=", nc_value, ", "
+ "qop=\"", qop_value, "\"", NULL);
+ }
+
+ ne_buffer_zappend(ret, "\r\n");
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digest request header is %s\n", ret->data);
+
+ return ne_buffer_finish(ret);
+}
+
+/* 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');
+
+ *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!).
+ */
+static int verify_response(struct auth_request *req, auth_session *sess,
+ const char *value)
+{
+ 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 */
+ *qop_value = NULL;
+ int nonce_count, okay;
+
+ if (!req->will_handle) {
+ /* Ignore it */
+ return 0;
+ }
+
+ if (sess->scheme != auth_scheme_digest) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Found Auth-Info header not in response "
+ " to Digest credentials - dodgy.\n");
+ return -1;
+ }
+
+ pnt = hdr = ne_strdup(value);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Auth-Info header: %s\n", value);
+
+ 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(val, "auth") == 0) {
+ qop = auth_qop_auth;
+ } else {
+ qop = auth_qop_none;
+ }
+ } 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);
+ }
+ }
+ }
+
+ /* Presume the worst */
+ okay = -1;
+
+ if ((qop != auth_qop_none) && (qop_value != NULL)) {
+ if ((rspauth == NULL) || (cnonce == NULL) || (nc == NULL)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Missing rspauth, cnonce or nc with qop.\n");
+ } else { /* Have got rspauth, cnonce and nc */
+ if (strcmp(cnonce, sess->cnonce) != 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Response cnonce doesn't match.\n");
+ } else if (nonce_count != sess->nonce_count) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Response nonce count doesn't match.\n");
+ } else {
+ /* Calculate and check the response-digest value.
+ * joe: IMO the spec is slightly ambiguous as to whether
+ * we use the qop which WE sent, or the qop which THEY
+ * sent... */
+ struct ne_md5_ctx a2;
+ unsigned char a2_md5[16], rdig_md5[16];
+ char a2_md5_ascii[33], rdig_md5_ascii[33];
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating response-digest.\n");
+
+ /* First off, H(A2) again. */
+ ne_md5_init_ctx(&a2);
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(req->uri, strlen(req->uri), &a2);
+ if (qop == auth_qop_auth_int) {
+ unsigned char heb_md5[16];
+ char heb_md5_ascii[33];
+ /* Add on ":" H(entity-body) */
+ ne_md5_finish_ctx(&req->response_body, heb_md5);
+ ne_md5_to_ascii(heb_md5, heb_md5_ascii);
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(heb_md5_ascii, 32, &a2);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digested [:%s]\n", heb_md5_ascii);
+ }
+ ne_md5_finish_ctx(&a2, a2_md5);
+ ne_md5_to_ascii(a2_md5, a2_md5_ascii);
+
+ /* We have the stored digest-so-far of
+ * H(A1) ":" unq(nonce-value)
+ * [ ":" nc-value ":" unq(cnonce-value) ] for qop
+ * in sess->stored_rdig, to save digesting them again.
+ *
+ */
+ if (qop != auth_qop_none) {
+ /* Add in qop-value */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting qop-value [%s:].\n",
+ qop_value);
+ ne_md5_process_bytes(qop_value, strlen(qop_value),
+ &sess->stored_rdig);
+ ne_md5_process_bytes(":", 1, &sess->stored_rdig);
+ }
+ /* Digest ":" H(A2) */
+ ne_md5_process_bytes(a2_md5_ascii, 32, &sess->stored_rdig);
+ /* All done */
+ ne_md5_finish_ctx(&sess->stored_rdig, rdig_md5);
+ ne_md5_to_ascii(rdig_md5, rdig_md5_ascii);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculated response-digest of: "
+ "[%s]\n", rdig_md5_ascii);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Given response-digest of: "
+ "[%s]\n", rspauth);
+
+ /* And... do they match? */
+ okay = (strcasecmp(rdig_md5_ascii, rspauth) == 0)?0:-1;
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Matched: %s\n", okay?"nope":"YES!");
+ }
+ }
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "No qop directive, auth okay.\n");
+ okay = 0;
+ }
+
+ /* Check for a nextnonce */
+ if (nextnonce != NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Found nextnonce of [%s].\n", nextnonce);
+ if (sess->nonce != NULL)
+ ne_free(sess->nonce);
+ sess->nonce = ne_strdup(nextnonce);
+ }
+
+ ne_free(hdr);
+
+ return okay;
+}
+
+/* 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 *pnt, *key, *val, *hdr;
+ struct auth_challenge *chall = NULL, *challenges = NULL;
+ 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. */
+
+ while (!tokenize(&pnt, &key, &val, 1)) {
+
+ if (val == NULL) {
+ /* We have a new challenge */
+ 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 (strcasecmp(key, "basic") == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Basic scheme.\n");
+ chall->scheme = auth_scheme_basic;
+ } else if (strcasecmp(key, "digest") == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n");
+ chall->scheme = auth_scheme_digest;
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n");
+ ne_free(chall);
+ challenges = NULL;
+ break;
+ }
+ 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;
+ }
+
+ /* Strip quotes of value. */
+ val = ne_shave(val, "\"'");
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, val);
+
+ if (strcasecmp(key, "realm") == 0) {
+ chall->realm = val;
+ } else if (strcasecmp(key, "nonce") == 0) {
+ chall->nonce = val;
+ } else if (strcasecmp(key, "opaque") == 0) {
+ chall->opaque = val;
+ } else if (strcasecmp(key, "stale") == 0) {
+ /* Truth value */
+ chall->stale = (strcasecmp(val, "true") == 0);
+ } else if (strcasecmp(key, "algorithm") == 0) {
+ if (strcasecmp(val, "md5") == 0) {
+ chall->alg = auth_alg_md5;
+ } 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(val, ',', 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);
+ }
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Finished parsing parameters.\n");
+
+ /* Did we find any challenges */
+ if (challenges == NULL) {
+ 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)) {
+ success = 1;
+ break;
+ }
+ }
+ }
+
+ if (!success) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "No good Digest challenges, looking for Basic.\n");
+ for (chall = challenges; chall != NULL; chall = chall->next) {
+ if (chall->scheme == auth_scheme_basic) {
+ if (!basic_challenge(sess, chall)) {
+ success = 1;
+ break;
+ }
+ }
+ }
+
+ if (!success) {
+ /* No good challenges - record this in the session state */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Did not understand any challenges.\n");
+ }
+
+ }
+
+ /* Remember whether we can now supply the auth details */
+ sess->can_handle = success;
+
+ while (challenges != NULL) {
+ chall = challenges->next;
+ ne_free(challenges);
+ challenges = chall;
+ }
+
+ ne_free(hdr);
+
+ return !success;
+}
+
+/* The body reader callback. */
+static void auth_body_reader(void *cookie, const char *block, size_t length)
+{
+ struct ne_md5_ctx *ctx = cookie;
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "Digesting %" NE_FMT_SIZE_T " bytes of response body.\n", length);
+ ne_md5_process_bytes(block, length, ctx);
+}
+
+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->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(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(ne_request *r, void *cookie, ne_buffer *request)
+{
+ auth_session *sess = cookie;
+ struct auth_request *req = ne_get_request_private(r, sess->spec->id);
+
+ if (!sess->can_handle) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Not handling session.\n");
+ } else {
+ char *value;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Handling.");
+ req->will_handle = 1;
+
+ if (sess->qop == auth_qop_auth_int) {
+ /* Digest mode / qop=auth-int: take an MD5 digest of the
+ * response body. */
+ ne_md5_init_ctx(&req->response_body);
+ ne_add_response_body_reader(req->request, ne_accept_always,
+ auth_body_reader, &req->response_body);
+ }
+
+ switch(sess->scheme) {
+ case auth_scheme_basic:
+ value = request_basic(sess);
+ break;
+ case auth_scheme_digest:
+ value = request_digest(sess, req);
+ break;
+ default:
+ value = NULL;
+ break;
+ }
+
+ if (value != NULL) {
+ ne_buffer_concat(request, sess->spec->req_hdr, ": ", value, NULL);
+ ne_free(value);
+ }
+
+ }
+
+}
+
+#define SAFELY(x) ((x) != NULL?(x):"null")
+
+static int ah_post_send(ne_request *req, void *cookie, const ne_status *status)
+{
+ auth_session *sess = cookie;
+ struct auth_request *areq = ne_get_request_private(req, sess->spec->id);
+ int ret = NE_OK;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "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->spec->fail_msg));
+ ret = NE_ERROR;
+ } else if (status->code == sess->spec->status_code &&
+ areq->auth_hdr != NULL) {
+ 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;
+ }
+ }
+
+ NE_FREE(areq->auth_info_hdr);
+ NE_FREE(areq->auth_hdr);
+
+ return ret;
+}
+
+static void ah_destroy(ne_request *req, void *session)
+{
+ auth_session *sess = session;
+ struct auth_request *areq = ne_get_request_private(req, sess->spec->id);
+ ne_free(areq);
+}
+
+static void free_auth(void *cookie)
+{
+ auth_session *sess = cookie;
+
+ clean_session(sess);
+ ne_free(sess);
+}
+
+static void auth_register(ne_session *sess,
+ const struct auth_class *ahc, const char *id,
+ ne_auth_creds creds, void *userdata)
+{
+ auth_session *ahs = ne_calloc(sizeof *ahs);
+
+ ahs->creds = creds;
+ ahs->userdata = userdata;
+ ahs->sess = sess;
+ ahs->spec = ahc;
+
+ /* 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_server_auth(ne_session *sess, ne_auth_creds creds, void *userdata)
+{
+ auth_register(sess, &ah_server_class, HOOK_SERVER_ID, creds, userdata);
+}
+
+void ne_set_proxy_auth(ne_session *sess, ne_auth_creds creds, void *userdata)
+{
+ auth_register(sess, &ah_proxy_class, HOOK_PROXY_ID, creds, userdata);
+}
+
+void ne_forget_auth(ne_session *sess)
+{
+ 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);
+}
+
diff --git a/src/ne_auth.h b/src/ne_auth.h
new file mode 100644
index 0000000..fe1370c
--- /dev/null
+++ b/src/ne_auth.h
@@ -0,0 +1,62 @@
+/*
+ HTTP authentication routines
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_AUTH_H
+#define NE_AUTH_H
+
+#include "ne_session.h" /* for 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 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);
+
+END_NEON_DECLS
+
+#endif /* NE_AUTH_H */
diff --git a/src/ne_basic.c b/src/ne_basic.c
new file mode 100644
index 0000000..d3b4eb3
--- /dev/null
+++ b/src/ne_basic.c
@@ -0,0 +1,554 @@
+/*
+ Basic HTTP and WebDAV methods
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <errno.h>
+
+#include "ne_request.h"
+#include "ne_alloc.h"
+#include "ne_utils.h"
+#include "ne_basic.h"
+#include "ne_207.h"
+
+#ifndef NEON_NODAV
+#include "ne_uri.h"
+#endif
+
+#ifdef USE_DAV_LOCKS
+#include "ne_locks.h"
+#endif
+#include "ne_dates.h"
+#include "ne_i18n.h"
+
+/* Header parser to retrieve Last-Modified date */
+static void get_lastmodified(void *userdata, const char *value) {
+ time_t *modtime = userdata;
+ *modtime = ne_httpdate_parse(value);
+}
+
+int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime)
+{
+ ne_request *req = ne_request_create(sess, "HEAD", uri);
+ int ret;
+
+ ne_add_response_header_handler(req, "Last-Modified", get_lastmodified,
+ modtime);
+
+ *modtime = -1;
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ *modtime = -1;
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+/* PUT's from fd to URI */
+int ne_put(ne_session *sess, const char *uri, int fd)
+{
+ ne_request *req = ne_request_create(sess, "PUT", uri);
+ int ret;
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, 0);
+ ne_lock_using_parent(req, uri);
+#endif
+
+ ne_set_request_body_fd(req, fd);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2)
+ ret = NE_ERROR;
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+/* Conditional HTTP put.
+ * PUTs from fd to uri, returning NE_FAILED if resource as URI has
+ * been modified more recently than 'since'.
+ */
+int
+ne_put_if_unmodified(ne_session *sess, const char *uri, int fd,
+ time_t since)
+{
+ ne_request *req;
+ char *date;
+ int ret;
+
+ if (ne_version_pre_http11(sess)) {
+ time_t modtime;
+ /* Server is not minimally HTTP/1.1 compliant. Do a HEAD to
+ * check the remote mod time. Of course, this makes the
+ * operation very non-atomic, but better than nothing. */
+ ret = ne_getmodtime(sess, uri, &modtime);
+ if (ret != NE_OK) return ret;
+ if (modtime != since)
+ return NE_FAILED;
+ }
+
+ req = ne_request_create(sess, "PUT", uri);
+
+ date = ne_rfc1123_date(since);
+ /* Add in the conditionals */
+ ne_add_request_header(req, "If-Unmodified-Since", date);
+ ne_free(date);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, 0);
+ /* FIXME: this will give 412 if the resource doesn't exist, since
+ * PUT may modify the parent... does that matter? */
+#endif
+
+ ne_set_request_body_fd(req, fd);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK) {
+ if (ne_get_status(req)->code == 412) {
+ ret = NE_FAILED;
+ } else if (ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+struct get_context {
+ int error;
+ ne_session *session;
+ off_t total;
+ int fd; /* used in get_to_fd */
+ ne_content_range *range;
+};
+
+static void get_to_fd(void *userdata, const char *block, size_t length)
+{
+ struct get_context *ctx = userdata;
+ ssize_t ret;
+
+ if (!ctx->error) {
+ while (length > 0) {
+ ret = write(ctx->fd, block, length);
+ if (ret < 0) {
+ 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, const ne_status *st)
+{
+ return (st->code == 206);
+}
+
+static void clength_hdr_handler(void *ud, const char *value)
+{
+ struct get_context *ctx = ud;
+ off_t len = strtol(value, NULL, 10);
+
+ if (ctx->range->end == -1) {
+ ctx->range->end = ctx->range->start + len - 1;
+ ctx->range->total = len;
+ }
+ else if (len != ctx->total) {
+ NE_DEBUG(NE_DBG_HTTP,
+ "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;
+ }
+}
+
+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;
+ }
+
+ /* TODO: verify against requested range. */
+}
+
+int ne_get_range(ne_session *sess, const char *uri,
+ ne_content_range *range, int fd)
+{
+ ne_request *req = ne_request_create(sess, "GET", uri);
+ struct get_context ctx;
+ const ne_status *status;
+ int ret;
+
+ if (range->end == -1) {
+ ctx.total = -1;
+ }
+ else {
+ 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);
+ ne_add_response_header_handler(req, "Content-Range",
+ content_range_hdr_handler,
+ &ctx);
+
+ ne_add_response_body_reader(req, accept_206, get_to_fd, &ctx);
+
+ if (range->end == -1) {
+ ne_print_request_header(req, "Range", "bytes=%" NE_FMT_OFF_T "-",
+ range->start);
+ }
+ else {
+ 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);
+
+ status = ne_get_status(req);
+
+ if (ctx.error) {
+ ret = NE_ERROR;
+ } 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);
+
+ return ret;
+}
+
+
+/* Get to given fd */
+int ne_get(ne_session *sess, const char *uri, int fd)
+{
+ ne_request *req = ne_request_create(sess, "GET", uri);
+ struct get_context ctx;
+ int ret;
+
+ ctx.total = -1;
+ ctx.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",
+ ne_handle_numeric_header,
+ &ctx.total);
+
+ ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
+
+ ret = ne_request_dispatch(req);
+
+ if (ctx.error) {
+ ret = NE_ERROR;
+ } else if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+
+/* Get to given fd */
+int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer)
+{
+ ne_request *req = ne_request_create(sess, "POST", uri);
+ struct get_context ctx;
+ int ret;
+
+ ctx.total = -1;
+ ctx.fd = fd;
+ ctx.error = 0;
+ ctx.session = sess;
+
+ /* Read the value of the Content-Length header into ctx.total */
+ ne_add_response_header_handler(req, "Content-Length",
+ ne_handle_numeric_header, &ctx.total);
+
+ ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
+
+ ne_set_request_body_buffer(req, buffer, strlen(buffer));
+
+ ret = ne_request_dispatch(req);
+
+ if (ctx.error) {
+ ret = NE_ERROR;
+ }
+ else if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+void ne_content_type_handler(void *userdata, const char *value)
+{
+ ne_content_type *ct = userdata;
+ char *sep, *stype;
+
+ ct->value = ne_strdup(value);
+
+ stype = strchr(ct->value, '/');
+ if (!stype) {
+ NE_FREE(ct->value);
+ return;
+ }
+
+ *stype++ = '\0';
+ ct->type = ct->value;
+
+ 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 *tokens = ne_strdup(value), *pnt = tokens;
+ ne_server_capabilities *caps = userdata;
+
+ do {
+ char *tok = ne_qtoken(&pnt, ',', "\"'");
+ if (!tok) break;
+
+ tok = ne_shave(tok, " \r\t\n");
+
+ if (strcmp(tok, "1") == 0) {
+ caps->dav_class1 = 1;
+ } else if (strcmp(tok, "2") == 0) {
+ caps->dav_class2 = 1;
+ } else if (strcmp(tok, "<http://apache.org/dav/propset/fs/1>") == 0) {
+ caps->dav_executable = 1;
+ }
+ } while (pnt != NULL);
+
+ ne_free(tokens);
+
+}
+
+int ne_options(ne_session *sess, const char *uri,
+ ne_server_capabilities *caps)
+{
+ ne_request *req = ne_request_create(sess, "OPTIONS", uri);
+
+ int ret;
+
+ ne_add_response_header_handler(req, "DAV", dav_hdr_handler, caps);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+#ifndef NEON_NODAV
+
+void ne_add_depth_header(ne_request *req, int depth)
+{
+ const char *value;
+ switch(depth) {
+ case NE_DEPTH_ZERO:
+ value = "0";
+ break;
+ case NE_DEPTH_ONE:
+ value = "1";
+ break;
+ default:
+ value = "infinity";
+ break;
+ }
+ ne_add_request_header(req, "Depth", value);
+}
+
+static int copy_or_move(ne_session *sess, int is_move, int overwrite,
+ 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);
+ }
+ ne_lock_using_resource(req, dest, NE_DEPTH_INFINITE);
+ /* And we need to be able to add members to the destination's parent */
+ ne_lock_using_parent(req, dest);
+#endif
+
+ ne_print_request_header(req, "Destination", "%s://%s%s",
+ ne_get_scheme(sess),
+ ne_get_server_hostport(sess), dest);
+
+ ne_add_request_header(req, "Overwrite", overwrite?"T":"F");
+
+ return ne_simple_request(sess, req);
+}
+
+int ne_copy(ne_session *sess, int overwrite, int depth,
+ const char *src, const char *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, 0, src, dest);
+}
+
+/* Deletes the specified resource. (and in only two lines of code!) */
+int ne_delete(ne_session *sess, const char *uri)
+{
+ ne_request *req = ne_request_create(sess, "DELETE", uri);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, NE_DEPTH_INFINITE);
+ ne_lock_using_parent(req, uri);
+#endif
+
+ /* joe: I asked on the DAV WG list about whether we might get a
+ * 207 error back from a DELETE... conclusion, you shouldn't if
+ * you don't send the Depth header, since we might be an HTTP/1.1
+ * client and a 2xx response indicates success to them. But
+ * it's all a bit unclear. In any case, DAV servers today do
+ * return 207 to DELETE even if we don't send the Depth header.
+ * So we handle 207 errors appropriately. */
+
+ return ne_simple_request(sess, req);
+}
+
+int ne_mkcol(ne_session *sess, const char *uri)
+{
+ ne_request *req;
+ char *real_uri;
+ int ret;
+
+ if (ne_path_has_trailing_slash(uri)) {
+ real_uri = ne_strdup(uri);
+ } else {
+ real_uri = ne_concat(uri, "/", NULL);
+ }
+
+ req = ne_request_create(sess, "MKCOL", real_uri);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, real_uri, 0);
+ ne_lock_using_parent(req, real_uri);
+#endif
+
+ ret = ne_simple_request(sess, req);
+
+ ne_free(real_uri);
+
+ return ret;
+}
+
+#endif /* NEON_NODAV */
diff --git a/src/ne_basic.h b/src/ne_basic.h
new file mode 100644
index 0000000..1953fc2
--- /dev/null
+++ b/src/ne_basic.h
@@ -0,0 +1,140 @@
+/*
+ HTTP/1.1 methods
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_BASIC_H
+#define NE_BASIC_H
+
+#include <sys/types.h> /* for time_t */
+
+#include "ne_request.h"
+
+BEGIN_NEON_DECLS
+
+/* Perform a GET request on resource at 'path', writing the entity
+ * body which is returned to 'fd'. */
+int ne_get(ne_session *sess, const char *path, int fd);
+
+/* Perform a PUT request on resource at 'path', reading the entity
+ * body to submit from 'fd'. */
+int ne_put(ne_session *sess, const char *path, int fd);
+
+#ifndef NEON_NODAV
+
+#define NE_DEPTH_ZERO (0)
+#define NE_DEPTH_ONE (1)
+#define NE_DEPTH_INFINITE (2)
+
+
+/* For ne_copy and ne_move:
+ *
+ * If a resource exists at "dest" and overwrite is zero, the operation
+ * will fail; if overwrite is non-zero, any existing resource will
+ * be over-written.
+ */
+
+/* Copy resource from 'src to 'dest' paths. If 'src' identifies a
+ * collection resource, depth may be NE_DEPTH_ZERO to request that the
+ * collection and its properties are to be copied, or
+ * NE_DEPTH_INFINITE to request that the collection and its contents
+ * are to be copied. */
+int ne_copy(ne_session *sess, int overwrite, int depth,
+ const char *src, const char *dest);
+
+/* Move resource from 'src' to dest 'path'. */
+int ne_move(ne_session *sess, int overwrite,
+ const char *src, const char *dest);
+
+/* Delete resource at 'path'. */
+int ne_delete(ne_session *sess, const char *path);
+/* Create a collection at 'path', which MUST have a trailing slash. */
+int ne_mkcol(ne_session *sess, const char *path);
+
+/* Adds a Depth: header to a request */
+void ne_add_depth_header(ne_request *req, int depth);
+
+#endif /* NEON_NODAV */
+
+/* PUT resource at location as above, only if it has not been modified
+ * since given modtime. If server is HTTP/1.1, uses If-Unmodified-Since
+ * header; guaranteed failure if resource is modified after 'modtime'.
+ * If server is HTTP/1.0, HEAD's the resource first to fetch current
+ * modtime; race condition if resource is modified between HEAD and PUT.
+ */
+int ne_put_if_unmodified(ne_session *sess,
+ const char *path, int fd, time_t modtime);
+
+/* Retrieve modification time of resource at location 'path', place in
+ * *modtime. (uses HEAD) */
+int ne_getmodtime(ne_session *sess, const char *path, time_t *modtime);
+
+typedef struct {
+ const char *type, *subtype;
+ const char *charset;
+ char *value;
+} ne_content_type;
+
+/* Sets (*ne_content_type)userdata appropriately.
+ * Caller must free ->value after use */
+void ne_content_type_handler(void *userdata, const char *value);
+
+/* Server capabilities: */
+typedef struct {
+ unsigned int dav_class1; /* True if Class 1 WebDAV server */
+ unsigned int dav_class2; /* True if Class 2 WebDAV server */
+ unsigned int dav_executable; /* True if supports the 'executable'
+ * property a. la. mod_dav */
+} ne_server_capabilities;
+
+/* Determines server capabilities (using OPTIONS). Pass 'path' as "*"
+ * to determine proxy server capabilities if using a proxy server. */
+int ne_options(ne_session *sess, const char *path,
+ ne_server_capabilities *caps);
+
+/* Defines a range of bytes, starting at 'start' and ending
+ * at 'end'. 'total' is the number of bytes in the range.
+ */
+typedef struct {
+ off_t start, end, total;
+} ne_content_range;
+
+/* Partial GET. range->start must be >= 0. range->total is ignored.
+ *
+ * If range->end is -1, then the rest of the resource from start is
+ * requested, and range->total and end are filled in on success.
+ *
+ * Otherwise, bytes from range->start to range->end are requested.
+ *
+ * This will write to the CURRENT position of f; so if you want
+ * to do a resume download, use:
+ * struct ne_content_range range;
+ * range.start = resume_from;
+ * range.end = range.start + 999; (= 1000 bytes)
+ * fseek(myfile, resume_from, SEEK_SET);
+ * ne_get_range(sess, path, &range, myfile); */
+int ne_get_range(ne_session *sess, const char *path,
+ ne_content_range *range, int fd);
+
+/* Post using buffer as request-body: stream response into f */
+int ne_post(ne_session *sess, const char *path, int fd, const char *buffer);
+
+END_NEON_DECLS
+
+#endif /* NE_BASIC_H */
diff --git a/src/ne_compress.c b/src/ne_compress.c
new file mode 100644
index 0000000..b84b46d
--- /dev/null
+++ b/src/ne_compress.c
@@ -0,0 +1,414 @@
+/*
+ Handling of compressed HTTP responses
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_compress.h"
+#include "ne_utils.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 (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;
+ void *userdata;
+
+ /* buffer for gzip header bytes. */
+ union {
+ unsigned char buf[10];
+ struct header {
+ unsigned char id1;
+ unsigned char id2;
+ unsigned char cmeth; /* compression method. */
+ unsigned char flags;
+ unsigned int mtime; /* 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 {
+ 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;
+};
+
+#define ID1 0x1f
+#define ID2 0x8b
+
+#define HDR_DONE 0
+#define HDR_EXTENDED 1
+#define HDR_ERROR 2
+
+/* parse_header parses the gzip header, sets the next state and returns
+ * HDR_DONE: all done, bytes following are raw DEFLATE data.
+ * HDR_EXTENDED: all done, expect a NUL-termianted string
+ * before the DEFLATE data
+ * HDR_ERROR: invalid header, give up.
+ */
+static int parse_header(ne_decompress *ctx)
+{
+ struct header *h = &ctx->in.hdr;
+
+ NE_DEBUG(NE_DBG_HTTP, "ID1: %d ID2: %d, cmeth %d, flags %d\n",
+ h->id1, h->id2, h->cmeth, h->flags);
+
+ if (h->id1 != ID1 || h->id2 != ID2 || h->cmeth != 8) {
+ ctx->state = NE_Z_ERROR;
+ ne_set_error(ctx->session, "Compressed stream invalid");
+ return HDR_ERROR;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "mtime: %d, xflags: %d, os: %d\n",
+ h->mtime, h->xflags, h->os);
+
+ /* TODO: we can only handle one NUL-terminated extensions field
+ * currently. Really, we should count the number of bits set, and
+ * skip as many fields as bits set (bailing if any reserved bits
+ * are set. */
+ if (h->flags == 8) {
+ ctx->state = NE_Z_POST_HEADER;
+ return HDR_EXTENDED;
+ } else if (h->flags != 0) {
+ 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 = NE_Z_INFLATING;
+ return HDR_DONE;
+}
+
+/* Convert 'buf' to unsigned int; 'buf' must be 'unsigned char *' */
+#define BUF2UINT(buf) ((buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0])
+
+/* 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;
+ 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;
+ }
+ }
+ }
+}
+
+/* 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 = (unsigned char *)buf;
+ ctx->zstr.total_in = 0;
+
+ do {
+ 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);
+
+ NE_DEBUG(NE_DBG_HTTP,
+ "compress: inflate %d, %ld bytes out, %d remaining\n",
+ ret, ctx->zstr.total_out, ctx->zstr.avail_in);
+#if 0
+ NE_DEBUG(NE_DBG_HTTPBODY,
+ "Inflated body block (%ld):\n[%.*s]\n",
+ ctx->zstr.total_out, (int)ctx->zstr.total_out,
+ ctx->outbuf);
+#endif
+ /* update checksum. */
+ ctx->checksum = crc32(ctx->checksum, (unsigned char *)ctx->outbuf,
+ ctx->zstr.total_out);
+
+ /* pass on the inflated data */
+ 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 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 = NE_Z_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->zstr.msg:"(no message)");
+ }
+}
+
+/* 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;
+ const char *zbuf;
+ size_t count;
+
+ switch (ctx->state) {
+ case NE_Z_PASSTHROUGH:
+ /* move along there. */
+ ctx->reader(ctx->userdata, buf, len);
+ return;
+
+ case NE_Z_ERROR:
+ /* beyond hope. */
+ break;
+
+ 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 NE_Z_BEFORE_DATA:
+ /* work out whether this is a compressed response or not. */
+ if (ctx->enchdr && strcasecmp(ctx->enchdr, "gzip") == 0) {
+ 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) {
+ ne_set_error(ctx->session, ctx->zstr.msg);
+ ctx->state = NE_Z_ERROR;
+ 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 = NE_Z_PASSTHROUGH;
+ ctx->reader(ctx->userdata, buf, len);
+ return;
+ }
+
+ ctx->state = NE_Z_IN_HEADER;
+ /* FALLTHROUGH */
+
+ case NE_Z_IN_HEADER:
+ /* copy as many bytes as possible into the buffer. */
+ if (len + ctx->incount > 10) {
+ count = 10 - ctx->incount;
+ } else {
+ count = len;
+ }
+ memcpy(ctx->in.buf + ctx->incount, buf, count);
+ ctx->incount += count;
+ /* have we got the full header yet? */
+ if (ctx->incount != 10) {
+ return;
+ }
+
+ buf += count;
+ len -= count;
+
+ switch (parse_header(ctx)) {
+ case HDR_ERROR:
+ return;
+ case HDR_EXTENDED:
+ if (len == 0)
+ return;
+ break;
+ case HDR_DONE:
+ if (len > 0) {
+ do_inflate(ctx, buf, len);
+ }
+ return;
+ }
+
+ /* FALLTHROUGH */
+
+ case NE_Z_POST_HEADER:
+ /* eating the filename string. */
+ zbuf = memchr(buf, '\0', len);
+ if (zbuf == NULL) {
+ /* not found it yet. */
+ return;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP,
+ "compresss: skipped %" NE_FMT_SIZE_T " header bytes.\n",
+ zbuf - buf);
+ /* found end of string. */
+ len -= (1 + zbuf - buf);
+ buf = zbuf + 1;
+ ctx->state = NE_Z_INFLATING;
+ if (len == 0) {
+ /* end of string was at end of buffer. */
+ return;
+ }
+
+ /* FALLTHROUGH */
+
+ 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)
+{
+ int ret;
+
+ if (ctx->zstrinit)
+ /* inflateEnd only fails if it's passed NULL etc; ignore
+ * return value. */
+ inflateEnd(&ctx->zstr);
+
+ if (ctx->enchdr)
+ 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;
+}
+
+ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt,
+ ne_block_reader rdr, void *userdata)
+{
+ ne_decompress *ctx = ne_calloc(sizeof *ctx);
+
+ ne_add_request_header(req, "Accept-Encoding", "gzip");
+
+ ne_add_response_header_handler(req, "Content-Encoding",
+ ne_duplicate_header, &ctx->enchdr);
+
+ ne_add_response_body_reader(req, acpt, gz_reader, ctx);
+
+ ctx->state = NE_Z_BEFORE_DATA;
+ ctx->reader = rdr;
+ ctx->userdata = userdata;
+ ctx->session = ne_get_session(req);
+ /* initialize the checksum. */
+ ctx->checksum = crc32(0L, Z_NULL, 0);
+
+ 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 */
diff --git a/src/ne_compress.h b/src/ne_compress.h
new file mode 100644
index 0000000..9fa6b73
--- /dev/null
+++ b/src/ne_compress.h
@@ -0,0 +1,44 @@
+/*
+ Compressed HTPT request/response Handling
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_COMPRESS_H
+#define NE_COMPRESS_H
+
+#include "ne_request.h"
+
+typedef struct ne_decompress_s ne_decompress;
+
+/* Call this to register a 'reader' callback which will be passed
+ * blocks of response body (if the 'acceptance' callback is
+ * successful). If the response body is returned compressed by the
+ * server, this reader will receive UNCOMPRESSED blocks.
+ *
+ * Returns pointer to context object which must be passed to
+ * ne_decompress_destroy after the request has been dispatched, to
+ * free any internal state. */
+ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response accpt,
+ ne_block_reader rdr, void *userdata);
+
+/* Free's up internal state. Returns non-zero if errors occured during
+ * decompression: the session error string will have the error. */
+int ne_decompress_destroy(ne_decompress *ctx);
+
+#endif /* NE_COMPRESS_H */
diff --git a/src/ne_cookies.c b/src/ne_cookies.c
new file mode 100644
index 0000000..f84f2fa
--- /dev/null
+++ b/src/ne_cookies.c
@@ -0,0 +1,135 @@
+/*
+ Basic cookie support for neon
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* A nice demo of hooks, since it doesn't need any external
+ * interface to muck with the stored states.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#include "ne_cookies.h"
+
+#include "ne_request.h"
+#include "ne_string.h"
+#include "ne_alloc.h"
+
+#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, ';', '=', "\"'", " \r\n\t");
+ ne_cookie *cook;
+ ne_cookie_cache *cache = userdata;
+ int n;
+
+ /* Check sanity */
+ if (pairs[0] == NULL || pairs[1] == NULL) {
+ /* yagaboo */
+ return;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Got cookie name=%s\n", pairs[0]);
+
+ /* Search for a cookie of this name */
+ NE_DEBUG(NE_DBG_HTTP, "Searching for existing cookie...\n");
+ for (cook = cache->cookies; cook != NULL; cook = cook->next) {
+ if (strcasecmp(cook->name, pairs[0]) == 0) {
+ break;
+ }
+ }
+
+ if (cook == NULL) {
+ NE_DEBUG(NE_DBG_HTTP, "New cookie.\n");
+ cook = ne_malloc(sizeof *cook);
+ memset(cook, 0, sizeof *cook);
+ cook->name = ne_strdup(pairs[0]);
+ cook->next = cache->cookies;
+ cache->cookies = cook;
+ } else {
+ /* Free the old value */
+ ne_free(cook->value);
+ }
+
+ cook->value = ne_strdup(pairs[1]);
+
+ for (n = 2; pairs[n] != NULL; n+=2) {
+ NE_DEBUG(NE_DBG_HTTP, "Cookie parm %s=%s\n", pairs[n], pairs[n+1]);
+ if (strcasecmp(pairs[n], "path") == 0) {
+ cook->path = 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 = ne_strdup(pairs[n+1]);
+ }
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "End of parms.\n");
+
+ pair_string_free(pairs);
+}
+
+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);
+}
+
+/* Just before sending the request: let them add headers if they want */
+static void pre_send(ne_request *req, void *session, ne_buffer *request)
+{
+ ne_cookie_cache *cache = session;
+ ne_cookie *cook;
+
+ if (cache->cookies == NULL) {
+ return;
+ }
+
+ ne_buffer_zappend(request, "Cookie: ");
+
+ for (cook = cache->cookies; cook != NULL; cook=cook->next) {
+ ne_buffer_concat(request, cook->name, "=", cook->value, NULL);
+ if (cook->next != NULL) {
+ ne_buffer_zappend(request, "; ");
+ }
+ }
+
+ ne_buffer_zappend(request, "\r\n");
+}
+
+/* Register cookie handling for given session using given cache. */
+void ne_cookie_register(ne_session *sess, ne_cookie_cache *cache)
+{
+ ne_hook_create_request(sess, create, cache);
+ ne_hook_pre_send(sess, pre_send, cache);
+}
+
diff --git a/src/ne_cookies.h b/src/ne_cookies.h
new file mode 100644
index 0000000..feb3a37
--- /dev/null
+++ b/src/ne_cookies.h
@@ -0,0 +1,51 @@
+/*
+ HTTP Request Handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_COOKIES_H
+#define NE_COOKIES_H
+
+#include "ne_request.h"
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+struct ne_cookie_s;
+typedef struct ne_cookie_s ne_cookie;
+
+struct ne_cookie_s {
+ char *name, *value;
+ unsigned int secure:1;
+ unsigned int discard:1;
+ char *domain, *path;
+ time_t expiry; /* time at which the cookie expires */
+ ne_cookie *next;
+};
+
+typedef struct {
+ ne_cookie *cookies;
+} ne_cookie_cache;
+
+/* Register cookie handling for given session using given cache. */
+void ne_cookie_register(ne_session *sess, ne_cookie_cache *cache);
+
+END_NEON_DECLS
+
+#endif /* NE_COOKIES_H */
diff --git a/src/ne_dates.c b/src/ne_dates.c
new file mode 100644
index 0000000..6896bd2
--- /dev/null
+++ b/src/ne_dates.c
@@ -0,0 +1,251 @@
+/*
+ Date manipulation routines
+ 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
+ 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>
+
+#include <time.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.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"
+/* asctime: Wed Jun 30 21:49:08 1993 */
+#define ASCTIME_FORMAT "%3s %3s %2d %2d:%2d:%2d %4d"
+
+static const char *rfc1123_weekdays[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+static const char *short_months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+#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) {
+ struct tm *gmt;
+ char *ret;
+ gmt = gmtime(&anytime);
+ if (gmt == NULL)
+ return NULL;
+ ret = ne_malloc(29 + 1); /* dates are 29 chars long */
+/* it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+ 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];
+ int n;
+/* it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+ n = sscanf(date, RFC1123_FORMAT,
+ wkday, &gmt.tm_mday, mon, &gmt.tm_year, &gmt.tm_hour,
+ &gmt.tm_min, &gmt.tm_sec);
+ /* Is it portable to check n==7 here? */
+ gmt.tm_year -= 1900;
+ for (n=0; n<12; n++)
+ if (strcmp(mon, short_months[n]) == 0)
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ gmt.tm_isdst = -1;
+ return mktime(&gmt) + GMTOFF(gmt);
+}
+
+/* Takes a string containing a RFC1036-style date and returns the time_t */
+time_t ne_rfc1036_parse(const char *date)
+{
+ struct tm gmt = {0};
+ int n;
+ static char wkday[10], mon[4];
+ /* RFC850/1036 style dates: Sunday, 06-Nov-94 08:49:37 GMT */
+ n = sscanf(date, RFC1036_FORMAT,
+ wkday, &gmt.tm_mday, mon, &gmt.tm_year,
+ &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec);
+ 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) + GMTOFF(gmt);
+}
+
+
+/* (as)ctime dates are like:
+ * Wed Jun 30 21:49:08 1993
+ */
+time_t ne_asctime_parse(const char *date)
+{
+ struct tm gmt = {0};
+ int n;
+ static char wkday[4], mon[4];
+ n = sscanf(date, ASCTIME_FORMAT,
+ wkday, mon, &gmt.tm_mday,
+ &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec,
+ &gmt.tm_year);
+ /* portable to check n here? */
+ for (n=0; n<12; n++)
+ if (strcmp(mon, short_months[n]) == 0)
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ gmt.tm_isdst = -1;
+ return mktime(&gmt) + GMTOFF(gmt);
+}
+
+/* HTTP-date parser */
+time_t ne_httpdate_parse(const char *date)
+{
+ time_t tmp;
+ tmp = ne_rfc1123_parse(date);
+ if (tmp == -1) {
+ tmp = ne_rfc1036_parse(date);
+ if (tmp == -1)
+ tmp = ne_asctime_parse(date);
+ }
+ return tmp;
+}
+
+#undef RFC1036_FORMAT
+#undef ASCTIME_FORMAT
+#undef RFC1123_FORMAT
+
+#ifdef RFC1123_TEST
+
+int main(int argc, char **argv) {
+ time_t now, in;
+ char *out;
+ if (argc > 1) {
+ printf("Got: %s\n", argv[1]);
+ in = ne_rfc1123_parse(argv[1]);
+ printf("Parsed: %d\n", in);
+ out = ne_rfc1123_date(in);
+ printf("Back again: %s\n", out);
+ } else {
+ now = time(NULL);
+ out = ne_rfc1123_date(now);
+ in = ne_rfc1123_parse(out);
+ printf("time(NULL) = %d\n", now);
+ printf("RFC1123 Time: [%s]\n", out);
+ printf("Parsed = %d\n", in);
+ out = ne_rfc1123_date(in);
+ printf("Back again: [%s]\n", out);
+ }
+ return 0;
+}
+
+#endif
+
+
diff --git a/src/ne_dates.h b/src/ne_dates.h
new file mode 100644
index 0000000..ed628fd
--- /dev/null
+++ b/src/ne_dates.h
@@ -0,0 +1,54 @@
+/*
+ Date manipulation routines
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DATES_H
+#define DATES_H
+
+#include <sys/types.h>
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Date manipulation routines as per RFC1123 and RFC1036 */
+
+/* Return current date/time in RFC1123 format */
+char *ne_rfc1123_date(time_t anytime);
+
+/* Returns time from date/time using the subset of the ISO8601 format
+ * referenced in RFC2518 (e.g as used in the creationdate property in
+ * the DAV: namespace). */
+time_t ne_iso8601_parse(const char *date);
+
+/* Returns time from date/time in RFC1123 format */
+time_t ne_rfc1123_parse(const char *date);
+
+time_t ne_rfc1036_parse(const char *date);
+
+/* Parses asctime date string */
+time_t ne_asctime_parse(const char *date);
+
+/* Parse an HTTP-date as per RFC2616 */
+time_t ne_httpdate_parse(const char *date);
+
+END_NEON_DECLS
+
+#endif /* DATES_H */
diff --git a/src/ne_defs.h b/src/ne_defs.h
new file mode 100644
index 0000000..f029edf
--- /dev/null
+++ b/src/ne_defs.h
@@ -0,0 +1,10 @@
+
+#undef BEGIN_NEON_DECLS
+#undef END_NEON_DECLS
+#ifdef __cplusplus
+# define BEGIN_NEON_DECLS extern "C" {
+# define END_NEON_DECLS }
+#else
+# define BEGIN_NEON_DECLS /* empty */
+# define END_NEON_DECLS /* empty */
+#endif
diff --git a/src/ne_i18n.c b/src/ne_i18n.c
new file mode 100644
index 0000000..afbb3f9
--- /dev/null
+++ b/src/ne_i18n.c
@@ -0,0 +1,32 @@
+/*
+ Internationalization of neon
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+void neon_i18n_init(void)
+{
+#if defined(ENABLE_NLS) && defined(NEON_IS_LIBRARY)
+ /* if neon is build bundled in (i.e., not as a standalone
+ * library), then there is probably no point in this, since the
+ * messages won't be pointing in the right direction.
+ * there's not really any point in doing this if neon is
+ * a library since the messages aren't i18n'ized, but... */
+ bindtextdomain("neon", LOCALEDIR);
+#endif
+}
diff --git a/src/ne_i18n.h b/src/ne_i18n.h
new file mode 100644
index 0000000..2b715a8
--- /dev/null
+++ b/src/ne_i18n.h
@@ -0,0 +1,37 @@
+/*
+ Internationalization of neon
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NEON_I18N_H
+#define NEON_I18N_H
+
+#undef _
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(str) gettext(str)
+#else
+#define _(str) (str)
+#endif /* ENABLE_NLS */
+#define N_(str) (str)
+
+/* Initialize i18n in neon */
+void neon_i18n_init(void);
+
+#endif /* NEON_I18N_H */
diff --git a/src/ne_locks.c b/src/ne_locks.c
new file mode 100644
index 0000000..6df2f2c
--- /dev/null
+++ b/src/ne_locks.c
@@ -0,0 +1,819 @@
+/*
+ WebDAV Class 2 locking operations
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ctype.h> /* for isdigit() */
+
+#include "ne_alloc.h"
+
+#include "ne_request.h"
+#include "ne_xml.h"
+#include "ne_locks.h"
+#include "ne_uri.h"
+#include "ne_basic.h"
+#include "ne_props.h"
+#include "ne_207.h"
+#include "ne_i18n.h"
+
+#define HOOK_ID "http://webdav.org/neon/hooks/webdav-locking"
+
+/* A list of lock objects. */
+struct lock_list {
+ struct ne_lock *lock;
+ struct lock_list *next, *prev;
+};
+
+struct ne_lock_store_s {
+ struct lock_list *locks;
+ struct lock_list *cursor; /* current position in 'locks' */
+};
+
+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;
+};
+
+/* 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;
+};
+
+/* 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[] = {
+ { "DAV:", "lockdiscovery" },
+ { NULL }
+};
+
+/* this simply registers the accessor for the function. */
+static void lk_create(ne_request *req, void *session,
+ const char *method, const char *uri)
+{
+ 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 lk_pre_send(ne_request *r, void *userdata, ne_buffer *req)
+{
+ 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 = 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);
+ }
+}
+
+/* Insert 'lock' into lock list *list. */
+static void insert_lock(struct lock_list **list, struct ne_lock *lock)
+{
+ struct lock_list *item = ne_malloc(sizeof *item);
+ if (*list != NULL) {
+ (*list)->prev = item;
+ }
+ item->prev = NULL;
+ item->next = *list;
+ item->lock = lock;
+ *list = item;
+}
+
+static void free_list(struct lock_list *list, int destroy)
+{
+ struct lock_list *next;
+
+ while (list != NULL) {
+ next = list->next;
+ if (destroy)
+ ne_lock_destroy(list->lock);
+ ne_free(list);
+ list = next;
+ }
+}
+
+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);
+}
+
+void ne_lockstore_destroy(ne_lock_store *store)
+{
+ free_list(store->locks, 1);
+ ne_free(store);
+}
+
+ne_lock_store *ne_lockstore_create(void)
+{
+ 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_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 lh_req_cookie *lrc, struct ne_lock *lock)
+{
+ struct lock_list *item;
+
+ /* Check for dups */
+ for (item = lrc->submit; item != NULL; item = item->next) {
+ if (strcasecmp(item->lock->token, lock->token) == 0)
+ return;
+ }
+
+ insert_lock(&lrc->submit, lock);
+}
+
+struct ne_lock *ne_lockstore_findbyuri(ne_lock_store *store,
+ const ne_uri *uri)
+{
+ 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 *path)
+{
+ struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
+ ne_uri u;
+ struct lock_list *item;
+ char *parent;
+
+ 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);
+
+ for (item = lrc->store->locks; item != NULL; item = item->next) {
+
+ /* 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);
+ }
+ }
+
+ 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)
+{
+ struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
+ struct lock_list *item;
+ int match;
+
+ if (lrc == NULL)
+ return;
+
+ /* 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 &&
+ 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", item->lock->token);
+ match = 1;
+ }
+ 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", item->lock->token);
+ match = 1;
+ }
+ 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", item->lock->token);
+ match = 1;
+ }
+
+ if (match) {
+ submit_lock(lrc, item->lock);
+ }
+ }
+
+}
+
+void ne_lockstore_add(ne_lock_store *store, struct ne_lock *lock)
+{
+ insert_lock(&store->locks, lock);
+}
+
+void ne_lockstore_remove(ne_lock_store *store, struct ne_lock *lock)
+{
+ 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 {
+ store->locks = item->next;
+ }
+ 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);
+
+ 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;
+ 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_uri_free(&lock->uri);
+ NE_FREE(lock->owner);
+ NE_FREE(lock->token);
+}
+
+void ne_lock_destroy(struct ne_lock *lock)
+{
+ 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);
+
+ /* 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);
+
+ if (ret == NE_OK && ne_get_status(req)->klass == 2) {
+ ret = NE_OK;
+ } else {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+static int parse_depth(const char *depth)
+{
+ if (strcasecmp(depth, "infinity") == 0) {
+ return NE_DEPTH_INFINITE;
+ } else if (isdigit(depth[0])) {
+ return atoi(depth);
+ } else {
+ return -1;
+ }
+}
+
+static long parse_timeout(const char *timeout)
+{
+ if (strcasecmp(timeout, "infinite") == 0) {
+ return NE_TIMEOUT_INFINITE;
+ } else if (strncasecmp(timeout, "Second-", 7) == 0) {
+ long to = strtol(timeout+7, NULL, 10);
+ if (to == LONG_MIN || to == LONG_MAX)
+ return NE_TIMEOUT_INVALID;
+ return to;
+ } else {
+ return NE_TIMEOUT_INVALID;
+ }
+}
+
+static void discover_results(void *userdata, const char *href,
+ const ne_prop_result_set *set)
+{
+ struct discover_ctx *ctx = userdata;
+ struct ne_lock *lock = ne_propset_private(set);
+ 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, int state, const char *cdata)
+{
+ switch (state) {
+ case ELM_write:
+ l->type = ne_locktype_write;
+ break;
+ case ELM_exclusive:
+ l->scope = ne_lockscope_exclusive;
+ break;
+ case ELM_shared:
+ l->scope = ne_lockscope_shared;
+ break;
+ 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 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 ELM_owner:
+ l->owner = strdup(cdata);
+ break;
+ case ELM_href:
+ l->token = strdup(cdata);
+ break;
+ }
+ return 0;
+}
+
+/* End-element handler for lock discovery PROPFIND response */
+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);
+}
+
+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 lk_endelm(void *userdata, int state,
+ const char *nspace, const char *name)
+{
+ 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 *ld_create(void *userdata, const char *href)
+{
+ 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_propfind_handler *handler;
+ struct discover_ctx ctx = {0};
+ int ret;
+
+ 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, ld_create, &ctx);
+
+ 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.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, lk_startelm, lk_cdata, lk_endelm, &ctx);
+
+ /* Create the body */
+ ne_buffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<lockinfo xmlns='DAV:'>" EOL " <lockscope>",
+ lock->scope==ne_lockscope_exclusive?
+ "<exclusive/>":"<shared/>",
+ "</lockscope>" EOL
+ "<locktype><write/></locktype>", NULL);
+
+ if (lock->owner) {
+ ne_buffer_concat(body, "<owner>", lock->owner, "</owner>" EOL, NULL);
+ }
+ ne_buffer_zappend(body, "</lockinfo>" EOL);
+
+ ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+ ne_add_response_body_reader(req, ne_accept_2xx,
+ ne_xml_parse_v, parser);
+ ne_add_request_header(req, "Content-Type", 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.path);
+ /* This one is clearer from 2518 sec 8.10.4. */
+ 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, 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));
+ }
+ else if (ne_get_status(req)->code == 207) {
+ ret = NE_ERROR;
+ /* TODO: set the error string appropriately */
+ }
+ } else {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+ ne_xml_destroy(parser);
+
+ return ret;
+}
diff --git a/src/ne_locks.h b/src/ne_locks.h
new file mode 100644
index 0000000..702e1fb
--- /dev/null
+++ b/src/ne_locks.h
@@ -0,0 +1,166 @@
+/*
+ WebDAV Class 2 locking operations
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_LOCKS_H
+#define NE_LOCKS_H
+
+#include "ne_request.h" /* for ne_session + ne_request */
+#include "ne_uri.h" /* for ne_uri */
+
+BEGIN_NEON_DECLS
+
+/* The scope of a lock */
+enum ne_lock_scope {
+ ne_lockscope_exclusive,
+ ne_lockscope_shared
+};
+
+/* Lock type. Only write locks are defined in RFC2518. */
+enum ne_lock_type {
+ ne_locktype_write
+};
+
+/* A lock object. */
+struct ne_lock {
+ ne_uri uri;
+ int depth; /* the depth of the lock (NE_DEPTH_*). */
+ enum ne_lock_type type;
+ enum ne_lock_scope scope;
+ char *token; /* the lock token: uniquely identifies this lock. */
+ char *owner; /* string describing the owner of the lock. */
+ long timeout; /* timeout in seconds. (or NE_TIMEOUT_*) */
+};
+/* NB: struct ne_lock Would be typedef'ed to ne_lock except lock is
+ * a verb and a noun, so we already have ne_lock the function. Damn
+ * the English language. */
+
+#define NE_TIMEOUT_INFINITE -1
+#define NE_TIMEOUT_INVALID -2
+
+/* Create a depth zero, exclusive write lock, with default timeout
+ * (allowing a server to pick a default). token, owner and uri are
+ * unset. */
+struct ne_lock *ne_lock_create(void);
+
+/* HINT: to initialize uri host/port/scheme for the lock's URI, use
+ * ne_fill_server_uri from ne_session.h. */
+
+/* Deep-copy a lock structure: strdup's any of path, token, owner,
+ * hostport which are set. */
+struct ne_lock *ne_lock_copy(const struct ne_lock *lock);
+
+/* Free a lock structure; free's any of any of the URI, token and
+ * owner which are set, but not the lock object itself. */
+void ne_lock_free(struct ne_lock *lock);
+
+/* Like ne_lock_free; but free's the lock object itself too. */
+void ne_lock_destroy(struct ne_lock *lock);
+
+/* ne_lock_store: an opaque type which is used to store a set of lock
+ * objects. */
+typedef struct ne_lock_store_s ne_lock_store;
+
+/* Create a lock store. */
+ne_lock_store *ne_lockstore_create(void);
+
+/* Register the lock store 'store' with the HTTP session 'sess': any
+ * operations made using 'sess' which operate on a locked resource,
+ * can use the locks from 'store' if needed. */
+void ne_lockstore_register(ne_lock_store *store, ne_session *sess);
+
+/* Destroy a lock store, free'ing any locks remaining inside. */
+void ne_lockstore_destroy(ne_lock_store *store);
+
+/* Add a lock to the store: the store then "owns" the lock object, and
+ * you must not free it. The lock MUST have all of:
+ * - a completed URI structure: scheme, host, port, and path all set
+ * - a valid lock token
+ * - a valid depth
+ */
+void ne_lockstore_add(ne_lock_store *store, struct ne_lock *lock);
+
+/* Remove given lock object from store: 'lock' MUST point to a lock
+ * object which is known to be in the store. */
+void ne_lockstore_remove(ne_lock_store *store, struct ne_lock *lock);
+
+/* Returns the first lock in the lock store, or NULL if the store is
+ * empty. */
+struct ne_lock *ne_lockstore_first(ne_lock_store *store);
+
+/* After ne_lockstore_first has been called; returns the next lock in
+ * the lock store, or NULL if there are no more locks stored.
+ * Behaviour is undefined if ne_lockstore_first has not been called on
+ * 'store' since the store was created, or the last time this function
+ * returned NULL for the store.. */
+struct ne_lock *ne_lockstore_next(ne_lock_store *store);
+
+/* Find a lock in the store for the given server, and with the given
+ * path. */
+struct ne_lock *ne_lockstore_findbyuri(ne_lock_store *store,
+ const ne_uri *uri);
+
+/* Issue a LOCK request for the given lock. Requires that the uri,
+ * depth, type, scope, and timeout members of 'lock' are filled in.
+ * owner and token must be malloc-allocated if not NULL; and may be
+ * free()d by this function. On successful return, lock->token will
+ * contain the lock token. */
+int ne_lock(ne_session *sess, struct ne_lock *lock);
+
+/* Issue an UNLOCK request for the given lock */
+int ne_unlock(ne_session *sess, const struct ne_lock *lock);
+
+/* Refresh a lock. Updates lock->timeout appropriately. */
+int ne_lock_refresh(ne_session *sess, struct ne_lock *lock);
+
+/* Callback for lock discovery. If 'lock' is NULL, something went
+ * wrong performing lockdiscovery for the resource, look at 'status'
+ * for the details.
+ *
+ * If lock is non-NULL, at least lock->uri and lock->token will be
+ * filled in; and status will be NULL. */
+typedef void (*ne_lock_result)(void *userdata, const struct ne_lock *lock,
+ const char *uri, const ne_status *status);
+
+/* Perform lock discovery on the given path. 'result' is called with
+ * the results (possibly >1 times). */
+int ne_lock_discover(ne_session *sess, const char *path,
+ ne_lock_result result, void *userdata);
+
+
+/* The ne_lock_using_* functions should be used before dispatching a
+ * request which modify resources. If a lock store has been
+ * registered with the session associated with the request, and locks
+ * are present in the lock store which cover the resources which are
+ * being modified by the request, then the appropriate lock tokens are
+ * submitted in the request headers. */
+
+/* Indicate that request 'req' will modify the resource at 'path', and
+ * is an operation of given 'depth'. */
+void ne_lock_using_resource(ne_request *req, const char *path, int depth);
+
+/* Indicate that request 'req' will modify the parent collection of
+ * the resource found at 'path' (for instance when removing the
+ * resource from the collection). */
+void ne_lock_using_parent(ne_request *req, const char *path);
+
+END_NEON_DECLS
+
+#endif /* NE_LOCKS_H */
diff --git a/src/ne_md5.c b/src/ne_md5.c
new file mode 100644
index 0000000..8a33054
--- /dev/null
+++ b/src/ne_md5.c
@@ -0,0 +1,432 @@
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+ according to the definition of MD5 in RFC 1321 from April 1992.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include <ctype.h> /* for tolower */
+
+#include "ne_md5.h"
+#include "ne_string.h" /* for NE_ASC2HEX */
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define WORDS_BIGENDIAN 1
+# endif
+#endif
+
+#define md5_init_ctx ne_md5_init_ctx
+#define md5_process_block ne_md5_process_block
+#define md5_process_bytes ne_md5_process_bytes
+#define md5_finish_ctx ne_md5_finish_ctx
+#define md5_read_ctx ne_md5_read_ctx
+#define md5_stream ne_md5_stream
+#define md5_ctx ne_md5_ctx
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+void
+md5_init_ctx (ctx)
+ struct md5_ctx *ctx;
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result
+ must be in little endian byte order.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_read_ctx (ctx, resbuf)
+ const struct md5_ctx *ctx;
+ void *resbuf;
+{
+ ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+ ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+ ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+ ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+ return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_finish_ctx (ctx, resbuf)
+ struct md5_ctx *ctx;
+ void *resbuf;
+{
+ /* Take yet unprocessed bytes into account. */
+ md5_uint32 bytes = ctx->buflen;
+ size_t pad;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+ memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+ *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+ (ctx->total[0] >> 29));
+
+ /* Process last bytes. */
+ md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+ return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+int
+md5_stream (stream, resblock)
+ FILE *stream;
+ void *resblock;
+{
+ /* Important: BLOCKSIZE must be a multiple of 64. */
+#define BLOCKSIZE 4096
+ struct md5_ctx ctx;
+ char buffer[BLOCKSIZE + 72];
+ size_t sum;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ do
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+ }
+ while (sum < BLOCKSIZE && n != 0);
+ if (n == 0 && ferror (stream))
+ return 1;
+
+ /* If end of file is reached, end the loop. */
+ if (n == 0)
+ break;
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ md5_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+ /* Add the last bytes if necessary. */
+ if (sum > 0)
+ md5_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ md5_finish_ctx (&ctx, resblock);
+ return 0;
+}
+
+void
+md5_process_bytes (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0)
+ {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy (&ctx->buffer[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (left_over + add > 64)
+ {
+ md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+ (left_over + add) & 63);
+ ctx->buflen = (left_over + add) & 63;
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len > 64)
+ {
+ md5_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0)
+ {
+ memcpy (ctx->buffer, buffer, len);
+ ctx->buflen = len;
+ }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+
+void
+md5_process_block (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ md5_uint32 correct_words[16];
+ const unsigned char *words = buffer;
+ const unsigned char *endp = words + len;
+ md5_uint32 A = ctx->A;
+ md5_uint32 B = ctx->B;
+ md5_uint32 C = ctx->C;
+ md5_uint32 D = ctx->D;
+
+ /* First increment the byte count. RFC 1321 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += len;
+ if (ctx->total[0] < len)
+ ++ctx->total[1];
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (words < endp)
+ {
+ md5_uint32 *cwp = correct_words;
+ md5_uint32 A_save = A;
+ md5_uint32 B_save = B;
+ md5_uint32 C_save = C;
+ md5_uint32 D_save = D;
+
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ md5_uint32 WORD_ = (md5_uint32)words[0] | ((md5_uint32)words[1] << 8) \
+ | ((md5_uint32)words[2] << 16) | ((md5_uint32)words[3] << 24); \
+ a += FF (b, c, d) + (*cwp++ = WORD_) + T; \
+ words += 4; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
+
+/* 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] = NE_HEX2ASC(md5_buf[count] >> 4);
+ buffer[count*2+1] = NE_HEX2ASC(md5_buf[count] & 0x0f);
+ }
+ buffer[32] = '\0';
+}
+
+/* Reads the ASCII representation of an MD5 digest. The buffer must
+ * be at least 32 characters long. */
+void ne_ascii_to_md5(const char *buffer, unsigned char md5_buf[16])
+{
+ int count;
+ for (count = 0; count<16; count++) {
+ md5_buf[count] = ((NE_ASC2HEX(buffer[count*2])) << 4) |
+ NE_ASC2HEX(buffer[count*2+1]);
+ }
+}
diff --git a/src/ne_md5.h b/src/ne_md5.h
new file mode 100644
index 0000000..9714ec3
--- /dev/null
+++ b/src/ne_md5.h
@@ -0,0 +1,144 @@
+/* Declaration of functions and data types used for MD5 sum computing
+ library functions.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef NEON_MD5_H
+#define NEON_MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+ to determine an unsigned integral type that is 32 bits wide. An
+ alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+ doing that would require that the configure script compile and *run*
+ the resulting executable. Locally running cross-compiled executables
+ is usually not possible. */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+# define UINT_MAX_32_BITS 4294967295U
+# else
+# define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+ This should be valid for all systems GNU cares about because
+ that doesn't include 16-bit systems, and only modern systems
+ (that certainly have <limits.h>) have 64+-bit integral types. */
+
+# ifndef UINT_MAX
+# define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+ typedef unsigned int md5_uint32;
+# else
+# if USHRT_MAX == UINT_MAX_32_BITS
+ typedef unsigned short md5_uint32;
+# else
+# if ULONG_MAX == UINT_MAX_32_BITS
+ typedef unsigned long md5_uint32;
+# else
+ /* The following line is intended to evoke an error.
+ Using #error is not portable enough. */
+ "Cannot determine unsigned 32-bit data type."
+# endif
+# endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+# define __P(x) x
+#else
+# define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps. */
+struct ne_md5_ctx
+{
+ md5_uint32 A;
+ md5_uint32 B;
+ md5_uint32 C;
+ md5_uint32 D;
+
+ md5_uint32 total[2];
+ md5_uint32 buflen;
+ char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+extern void ne_md5_init_ctx __P ((struct ne_md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void ne_md5_process_block __P ((const void *buffer, size_t len,
+ struct ne_md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void ne_md5_process_bytes __P ((const void *buffer, size_t len,
+ struct ne_md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 16 bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *ne_md5_finish_ctx __P ((struct ne_md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *ne_md5_read_ctx __P ((const struct ne_md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+extern int ne_md5_stream __P ((FILE *stream, void *resblock));
+
+/* MD5 ascii->binary conversion */
+void ne_md5_to_ascii(const unsigned char md5_buf[16], char *buffer);
+void ne_ascii_to_md5(const char *buffer, unsigned char md5_buf[16]);
+
+#endif /* NEON_MD5_H */
diff --git a/src/ne_openssl.c b/src/ne_openssl.c
new file mode 100644
index 0000000..5d21ef1
--- /dev/null
+++ b/src/ne_openssl.c
@@ -0,0 +1,790 @@
+/*
+ neon SSL/TLS support using OpenSSL
+ Copyright (C) 2002-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
+ 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"
+
+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);
+ ne_buffer_append(dump, ent->value->data, ent->value->length);
+ flag = 1;
+ }
+ }
+
+ 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). TODO: could do more advanced wildcard
+ * matching using fnmatch() here, if fnmatch is present. */
+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. */
+static int check_identity(const char *hostname, X509 *cert, char **identity)
+{
+ STACK_OF(GENERAL_NAME) *names;
+ int match = 0, found = 0;
+
+ names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (names) {
+ /* Got a subject alt. name extension. */
+ int n;
+
+ for (n = 0; n < sk_GENERAL_NAME_num(names) && !match; n++) {
+ GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, n);
+
+ /* only care about this if it is a DNS name. */
+ 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;
+ }
+ }
+ /* 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 RFC2818. */
+ 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);
+ 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. */
+ ret = check_identity(sess->server.hostname, cert, NULL);
+ 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);
+
+ if (ca_list == NULL) {
+ /* no list of acceptable CA names provided. */
+ dnames = NULL;
+ count = 0;
+ } else {
+ count = sk_X509_NAME_num(ca_list);
+
+ 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;
+
+ 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);
+ 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) {
+ if (X509_cmp(sk_X509_value(chain, 0), sess->server_cert->subject)) {
+ /* 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 (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) {
+ 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;
+}
diff --git a/src/ne_private.h b/src/ne_private.h
new file mode 100644
index 0000000..964800d
--- /dev/null
+++ b/src/ne_private.h
@@ -0,0 +1,118 @@
+/*
+ HTTP Request Handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* THIS IS NOT A PUBLIC INTERFACE. You CANNOT include this header file
+ * from an application. */
+
+#ifndef NE_PRIVATE_H
+#define NE_PRIVATE_H
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_ssl.h"
+
+struct host_info {
+ char *hostname;
+ 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 */
+};
+
+/* Store every registered callback in a generic container, and cast
+ * the function pointer when calling it. */
+struct hook {
+ void (*fn)(void);
+ void *userdata;
+ const char *id; /* non-NULL for accessors. */
+ struct hook *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 */
+ ne_socket *socket;
+
+ /* non-zero if connection has been established. */
+ int connected;
+
+ /* non-zero if connection has persisted beyond one request. */
+ int persisted;
+
+ 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 use_proxy:1; /* do we have a proxy server? */
+ unsigned int no_persist:1; /* set to disable persistent connections */
+ unsigned int use_ssl:1; /* whether a secure connection is required */
+ unsigned int in_connect:1; /* doing a proxy CONNECT */
+
+ int expect100_works; /* known state of 100-continue support */
+
+ ne_progress progress_cb;
+ void *progress_ud;
+
+ ne_notify_status notify_cb;
+ void *notify_ud;
+
+ int rdtimeout; /* read timeout. */
+
+ struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks;
+ struct hook *destroy_req_hooks, *destroy_sess_hooks, *private;
+
+ char *user_agent; /* full User-Agent string */
+
+#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;
+
+ /* Error string */
+ char error[BUFSIZ];
+};
+
+typedef int (*ne_push_fn)(void *userdata, const char *buf, size_t count);
+
+/* Pulls the request body for the given request, passing blocks to the
+ * given callback.
+ */
+int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud);
+
+/* Do the SSL negotiation. */
+int ne_negotiate_ssl(ne_request *req);
+
+#endif /* HTTP_PRIVATE_H */
diff --git a/src/ne_privssl.h b/src/ne_privssl.h
new file mode 100644
index 0000000..1f63759
--- /dev/null
+++ b/src/ne_privssl.h
@@ -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
diff --git a/src/ne_props.c b/src/ne_props.c
new file mode 100644
index 0000000..9b7da0f
--- /dev/null
+++ b/src/ne_props.c
@@ -0,0 +1,620 @@
+/*
+ WebDAV property manipulation
+ Copyright (C) 2000-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"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_xml.h"
+#include "ne_props.h"
+#include "ne_basic.h"
+#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;
+ ne_request *request;
+
+ int has_props; /* whether we've already written some
+ * props to the body. */
+ ne_buffer *body;
+
+ ne_207_parser *parser207;
+ ne_xml_parser *parser;
+
+ /* Callback to create the private structure. */
+ ne_props_create_complex private_creator;
+ void *private_userdata;
+
+ /* Current propset, 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_flatprop (NE_207_STATE_TOP - 1)
+
+/* We build up the results of one 'response' element in memory. */
+struct prop {
+ char *name, *nspace, *value, *lang;
+ /* Store a ne_propname here too, for convienience. pname.name =
+ * name, pname.nspace = nspace, but they are const'ed in pname. */
+ ne_propname pname;
+};
+
+#define NSPACE(x) ((x) ? (x) : "")
+
+struct propstat {
+ struct prop *props;
+ int numprops;
+ ne_status status;
+};
+
+/* Results set. */
+struct ne_prop_result_set_s {
+ struct propstat *pstats;
+ int numpstats;
+ void *private;
+ char *href;
+};
+
+
+static int
+startelm(void *userdata, int state, const char *name, const char *nspace,
+ const char **atts);
+static int
+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)
+{
+ return handler->parser;
+}
+
+ne_request *ne_propfind_get_request(ne_propfind_handler *handler)
+{
+ return handler->request;
+}
+
+static int propfind(ne_propfind_handler *handler,
+ ne_props_result results, void *userdata)
+{
+ int ret;
+ ne_request *req = handler->request;
+
+ /* Register the flat property handler to catch any properties
+ * which the user isn't handling as 'complex'. */
+ ne_xml_push_handler(handler->parser, startelm, chardata, endelm, handler);
+
+ 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", NE_XML_MEDIA_TYPE);
+
+ ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v,
+ handler->parser);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ } else if (!ne_xml_valid(handler->parser)) {
+ ne_set_error(handler->sess, ne_xml_get_error(handler->parser));
+ ret = NE_ERROR;
+ }
+
+ return ret;
+}
+
+static void set_body(ne_propfind_handler *hdl, const ne_propname *names)
+{
+ ne_buffer *body = hdl->body;
+ int n;
+
+ if (!hdl->has_props) {
+ ne_buffer_zappend(body, "<prop>" EOL);
+ hdl->has_props = 1;
+ }
+
+ for (n = 0; names[n].name != NULL; n++) {
+ ne_buffer_concat(body, "<", names[n].name, " xmlns=\"",
+ NSPACE(names[n].nspace), "\"/>" EOL, NULL);
+ }
+
+}
+
+int ne_propfind_allprop(ne_propfind_handler *handler,
+ ne_props_result results, void *userdata)
+{
+ ne_buffer_zappend(handler->body, "<allprop/></propfind>" EOL);
+ return propfind(handler, results, userdata);
+}
+
+int ne_propfind_named(ne_propfind_handler *handler, const ne_propname *props,
+ ne_props_result results, void *userdata)
+{
+ set_body(handler, props);
+ ne_buffer_zappend(handler->body, "</prop></propfind>" EOL);
+ return propfind(handler, results, userdata);
+}
+
+
+/* The easy one... PROPPATCH */
+int ne_proppatch(ne_session *sess, const char *uri,
+ const ne_proppatch_operation *items)
+{
+ ne_request *req = ne_request_create(sess, "PROPPATCH", uri);
+ ne_buffer *body = ne_buffer_create();
+ int n, ret;
+
+ /* Create the request body */
+ ne_buffer_zappend(body, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
+ "<D:propertyupdate xmlns:D=\"DAV:\">");
+
+ for (n = 0; items[n].name != NULL; n++) {
+ 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, "</D:propertyupdate>" EOL);
+
+ ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+ 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);
+
+ return ret;
+}
+
+/* Compare two property names. */
+static int pnamecmp(const ne_propname *pn1, const ne_propname *pn2)
+{
+ 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
+ * to the containing propstat, likewise prop_ret, and returns zero.
+ * If not found, returns non-zero. */
+static int findprop(const ne_prop_result_set *set, const ne_propname *pname,
+ struct propstat **pstat_ret, struct prop **prop_ret)
+{
+
+ int ps, p;
+
+ for (ps = 0; ps < set->numpstats; ps++) {
+ for (p = 0; p < set->pstats[ps].numprops; p++) {
+ struct prop *prop = &set->pstats[ps].props[p];
+
+ if (pnamecmp(&prop->pname, pname) == 0) {
+ if (pstat_ret != NULL)
+ *pstat_ret = &set->pstats[ps];
+ if (prop_ret != NULL)
+ *prop_ret = prop;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+const char *ne_propset_value(const ne_prop_result_set *set,
+ const ne_propname *pname)
+{
+ struct prop *prop;
+
+ if (findprop(set, pname, NULL, &prop)) {
+ return NULL;
+ } else {
+ return prop->value;
+ }
+}
+
+const char *ne_propset_lang(const ne_prop_result_set *set,
+ const ne_propname *pname)
+{
+ struct prop *prop;
+
+ if (findprop(set, pname, NULL, &prop)) {
+ return NULL;
+ } else {
+ return prop->lang;
+ }
+}
+
+void *ne_propfind_current_private(ne_propfind_handler *handler)
+{
+ return handler->current ? handler->current->private : NULL;
+}
+
+void *ne_propset_private(const ne_prop_result_set *set)
+{
+ return set->private;
+}
+
+int ne_propset_iterate(const ne_prop_result_set *set,
+ ne_propset_iterator iterator, void *userdata)
+{
+ int ps, p;
+
+ for (ps = 0; ps < set->numpstats; ps++) {
+ for (p = 0; p < set->pstats[ps].numprops; p++) {
+ struct prop *prop = &set->pstats[ps].props[p];
+ int ret = iterator(userdata, &prop->pname, prop->value,
+ &set->pstats[ps].status);
+ if (ret)
+ return ret;
+
+ }
+ }
+
+ return 0;
+}
+
+const ne_status *ne_propset_status(const ne_prop_result_set *set,
+ const ne_propname *pname)
+{
+ struct propstat *pstat;
+
+ if (findprop(set, pname, &pstat, NULL)) {
+ /* TODO: it is tempting to return a dummy status object here
+ * rather than NULL, which says "Property result was not given
+ * by server." but I'm not sure if this is best left to the
+ * client. */
+ return NULL;
+ } else {
+ return &pstat->status;
+ }
+}
+
+static void *start_response(void *userdata, const char *href)
+{
+ ne_prop_result_set *set = ne_calloc(sizeof(*set));
+ ne_propfind_handler *hdl = userdata;
+
+ set->href = ne_strdup(href);
+
+ if (hdl->private_creator != NULL) {
+ set->private = hdl->private_creator(hdl->private_userdata, href);
+ }
+
+ hdl->current = set;
+
+ return set;
+}
+
+static void *start_propstat(void *userdata, void *response)
+{
+ ne_prop_result_set *set = response;
+ int n;
+ struct propstat *pstat;
+
+ n = set->numpstats;
+ set->pstats = ne_realloc(set->pstats, sizeof(struct propstat) * (n+1));
+ set->numpstats = n+1;
+
+ pstat = &set->pstats[n];
+ memset(pstat, 0, sizeof(*pstat));
+
+ /* And return this as the new pstat. */
+ return &set->pstats[n];
+}
+
+static int startelm(void *userdata, 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);
+ struct prop *prop;
+ int n;
+ const char *lang;
+
+ /* 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 = 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(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,
+ 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(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);
+ } else {
+ prop->lang = NULL;
+ }
+
+ hdl->depth = 0;
+
+ return ELM_flatprop;
+}
+
+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 (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();
+ }
+ return 0;
+}
+
+static void end_propstat(void *userdata, void *pstat_v,
+ 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
+ * we will have done an unnecessary strdup("") above, but there is
+ * no easy way round that given the fact that we don't know
+ * whether we've got an error or not till after we get the
+ * property element.
+ *
+ * Interestingly IIS breaks the 2518 DTD and puts the status
+ * element first in the propstat. This is useful since then we
+ * *do* know whether each subsequent empty prop element means, but
+ * we can't rely on that here. */
+ if (status->klass != 2) {
+ int n;
+
+ for (n = 0; n < pstat->numprops; n++) {
+ 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 */
+static void free_propset(ne_prop_result_set *set)
+{
+ int n;
+
+ for (n = 0; n < set->numpstats; n++) {
+ int m;
+ struct propstat *p = &set->pstats[n];
+
+ for (m = 0; m < p->numprops; m++) {
+ NE_FREE(p->props[m].nspace);
+ ne_free(p->props[m].name);
+ NE_FREE(p->props[m].lang);
+ NE_FREE(p->props[m].value);
+ }
+
+ if (p->status.reason_phrase)
+ ne_free(p->status.reason_phrase);
+ if (p->props)
+ ne_free(p->props);
+ }
+
+ if (set->pstats)
+ ne_free(set->pstats);
+ ne_free(set->href);
+ ne_free(set);
+}
+
+static void end_response(void *userdata, void *resource,
+ const ne_status *status,
+ const char *description)
+{
+ ne_propfind_handler *handler = userdata;
+ ne_prop_result_set *set = resource;
+
+ /* Pass back the results for this resource. */
+ if (handler->callback && set->numpstats > 0)
+ handler->callback(handler->userdata, set->href, set);
+
+ /* Clean up the propset tree we've just built. */
+ free_propset(set);
+ handler->current = NULL;
+}
+
+ne_propfind_handler *
+ne_propfind_create(ne_session *sess, const char *uri, int depth)
+{
+ ne_propfind_handler *ret = ne_calloc(sizeof(ne_propfind_handler));
+
+ ret->parser = ne_xml_create();
+ ret->parser207 = ne_207_create(ret->parser, ret);
+ ret->sess = sess;
+ ret->body = ne_buffer_create();
+ ret->request = ne_request_create(sess, "PROPFIND", uri);
+ ret->value = ne_buffer_create();
+
+ ne_add_depth_header(ret->request, depth);
+
+ ne_207_set_response_handlers(ret->parser207,
+ start_response, end_response);
+
+ ne_207_set_propstat_handlers(ret->parser207, start_propstat,
+ end_propstat);
+
+ /* The start of the request body is fixed: */
+ ne_buffer_concat(ret->body,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<propfind xmlns=\"DAV:\">", NULL);
+
+ return ret;
+}
+
+/* Destroy a propfind handler */
+void ne_propfind_destroy(ne_propfind_handler *handler)
+{
+ ne_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);
+ ne_free(handler);
+}
+
+int ne_simple_propfind(ne_session *sess, const char *href, int depth,
+ const ne_propname *props,
+ ne_props_result results, void *userdata)
+{
+ ne_propfind_handler *hdl;
+ int ret;
+
+ hdl = ne_propfind_create(sess, href, depth);
+ if (props != NULL) {
+ ret = ne_propfind_named(hdl, props, results, userdata);
+ } else {
+ ret = ne_propfind_allprop(hdl, results, userdata);
+ }
+
+ ne_propfind_destroy(hdl);
+
+ return ret;
+}
+
+int ne_propnames(ne_session *sess, const char *href, int depth,
+ ne_props_result results, void *userdata)
+{
+ ne_propfind_handler *hdl;
+ int ret;
+
+ hdl = ne_propfind_create(sess, href, depth);
+
+ ne_buffer_zappend(hdl->body, "<propname/></propfind>");
+
+ ret = propfind(hdl, results, userdata);
+
+ ne_propfind_destroy(hdl);
+
+ return ret;
+}
+
+void ne_propfind_set_private(ne_propfind_handler *hdl,
+ ne_props_create_complex creator,
+ void *userdata)
+{
+ hdl->private_creator = creator;
+ hdl->private_userdata = userdata;
+}
diff --git a/src/ne_props.h b/src/ne_props.h
new file mode 100644
index 0000000..b86ac76
--- /dev/null
+++ b/src/ne_props.h
@@ -0,0 +1,244 @@
+/*
+ WebDAV Properties manipulation
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_PROPS_H
+#define NE_PROPS_H
+
+#include "ne_request.h"
+#include "ne_207.h"
+
+BEGIN_NEON_DECLS
+
+/* There are two interfaces for fetching properties. The first is
+ * 'ne_simple_propfind', which is relatively simple, and easy to use,
+ * but only lets you fetch FLAT properties, i.e. properties which are
+ * just a string of bytes. The complex interface is 'ne_propfind_*',
+ * which is complicated, and hard to use, but lets you parse
+ * structured properties, i.e. properties which have XML content. */
+
+/* The 'ne_simple_propfind' interface. ***
+ *
+ * ne_simple_propfind allows you to fetch a set of properties for a
+ * single resource, or a tree of resources. You set the operation
+ * going by passing these arguments:
+ *
+ * - the session which should be used.
+ * - the URI and the depth of the operation (0, 1, infinite)
+ * - the names of the properties which you want to fetch
+ * - a results callback, and the userdata for the callback.
+ *
+ * For each resource found, the results callback is called, passing
+ * you two things along with the userdata you passed in originally:
+ *
+ * - the URI of the resource (const char *href)
+ * - the properties results set (const ne_prop_result_set *results)
+ * */
+
+/* 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;
+
+/* Get the value of a given property. Will return NULL if there was an
+ * error fetching this property on this resource. Call
+ * ne_propset_result to get the response-status if so. */
+const char *ne_propset_value(const ne_prop_result_set *set,
+ const ne_propname *propname);
+
+/* Returns the status structure for fetching the given property on
+ * this resource. This function will return NULL if the server did not
+ * return the property (which is a server error). */
+const ne_status *ne_propset_status(const ne_prop_result_set *set,
+ const ne_propname *propname);
+
+/* Returns the private pointer for the given propset. */
+void *ne_propset_private(const ne_prop_result_set *set);
+
+/* Return language string of property (may be NULL). */
+const char *ne_propset_lang(const ne_prop_result_set *set,
+ const ne_propname *pname);
+
+/* ne_propset_iterate iterates over a properties result set,
+ * calling the callback for each property in the set. userdata is
+ * passed as the first argument to the callback. value may be NULL,
+ * indicating an error occurred fetching this property: look at
+ * status for the error in that case.
+ *
+ * If the iterator returns non-zero, ne_propset_iterate will return
+ * immediately with that value.
+ */
+typedef int (*ne_propset_iterator)(void *userdata,
+ const ne_propname *pname,
+ const char *value,
+ const ne_status *status);
+
+/* Iterate over all the properties in 'set', calling 'iterator'
+ * for each, passing 'userdata' as the first argument to callback.
+ *
+ * Returns:
+ * whatever value iterator returns.
+ */
+int ne_propset_iterate(const ne_prop_result_set *set,
+ ne_propset_iterator iterator, void *userdata);
+
+/* Callback for handling the results of fetching properties for a
+ * single resource (named by 'href'). The results are stored in the
+ * result set 'results': use ne_propset_* to examine this object. */
+typedef void (*ne_props_result)(void *userdata, const char *href,
+ const ne_prop_result_set *results);
+
+/* Fetch properties for a resource (if depth == NE_DEPTH_ZERO),
+ * or a tree of resources (if depth == NE_DEPTH_ONE or _INFINITE).
+ *
+ * Names of the properties required must be given in 'props',
+ * or if props is NULL, *all* properties are fetched.
+ *
+ * 'results' is called for each resource in the response, userdata is
+ * passed as the first argument to the callback. It is important to
+ * note that the callback is called as the response is read off the
+ * socket, so don't do anything silly in it (e.g. sleep(100), or call
+ * any functions which use this session).
+ *
+ * Note that if 'depth' is NE_DEPTH_INFINITY, some servers may refuse
+ * the request.
+ *
+ * Returns NE_*. */
+int ne_simple_propfind(ne_session *sess, const char *path, int depth,
+ const ne_propname *props,
+ ne_props_result results, void *userdata);
+
+/* 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. */
+typedef struct {
+ const ne_propname *name;
+ enum {
+ ne_propset,
+ ne_propremove
+ } type;
+ const char *value;
+} ne_proppatch_operation;
+
+/* 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 '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 *path, int depth,
+ ne_props_result results, void *userdata);
+
+/* The complex, you-do-all-the-work, property fetch interface:
+ */
+
+struct ne_propfind_handler_s;
+typedef struct ne_propfind_handler_s ne_propfind_handler;
+
+/* Retrieve the 'private' pointer for the current propset for the
+ * given handler, as returned by the ne_props_create_complex callback
+ * installed using 'ne_propfind_set_private'. If this callback was
+ * not registered, this function will return NULL. */
+void *ne_propfind_current_private(ne_propfind_handler *handler);
+
+/* Create a PROPFIND handler, for the given resource or set of
+ * resources.
+ *
+ * Depth must be one of NE_DEPTH_*. */
+ne_propfind_handler *
+ne_propfind_create(ne_session *sess, const char *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). 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
+ * complex properties, you must set up and register an XML handler
+ * which will understand the elements which make up such properties.
+ * The handler must be registered with the parser returned by
+ * 'ne_propfind_get_parser'.
+ *
+ * To store the parsed value of the property, a 'private' structure is
+ * allocated in each propset (i.e. one per resource). When parsing the
+ * property value elements, for each new resource encountered in the
+ * response, the 'creator' callback is called to retrieve a 'private'
+ * structure for this resource.
+ *
+ * Whilst in XML element callbacks you will have registered to handle
+ * complex properties, you can use the 'ne_propfind_current_private'
+ * call to retrieve the pointer to this private structure.
+ *
+ * To retrieve this 'private' structure from the propset in the
+ * results callback, simply call 'ne_propset_private'.
+ * */
+typedef void *(*ne_props_create_complex)(void *userdata,
+ const char *href);
+
+void ne_propfind_set_private(ne_propfind_handler *handler,
+ ne_props_create_complex creator,
+ void *userdata);
+
+/* Fetch all properties.
+ *
+ * Returns NE_*. */
+int ne_propfind_allprop(ne_propfind_handler *handler,
+ ne_props_result result, void *userdata);
+
+/* 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 *names,
+ ne_props_result result, void *userdata);
+
+/* Destroy a propfind handler after use. */
+void ne_propfind_destroy(ne_propfind_handler *handler);
+
+END_NEON_DECLS
+
+#endif /* NE_PROPS_H */
diff --git a/src/ne_redirect.c b/src/ne_redirect.c
new file mode 100644
index 0000000..1985793
--- /dev/null
+++ b/src/ne_redirect.c
@@ -0,0 +1,142 @@
+/*
+ HTTP-redirect support
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_session.h"
+#include "ne_request.h"
+#include "ne_alloc.h"
+#include "ne_uri.h"
+#include "ne_redirect.h"
+#include "ne_i18n.h"
+#include "ne_string.h"
+
+#define REDIRECT_ID "http://www.webdav.org/neon/hooks/http-redirect"
+
+struct redirect {
+ char *location;
+ char *requri;
+ int valid; /* non-zero if .uri contains a redirect */
+ ne_uri uri;
+ ne_session *sess;
+};
+
+static void
+create(ne_request *req, void *session, const char *method, const char *uri)
+{
+ struct redirect *red = session;
+ NE_FREE(red->location);
+ NE_FREE(red->requri);
+ red->requri = ne_strdup(uri);
+ ne_add_response_header_handler(req, "Location", ne_duplicate_header,
+ &red->location);
+}
+
+#define REDIR(n) ((n) == 301 || (n) == 302 || (n) == 303 || \
+ (n) == 307)
+
+static int post_send(ne_request *req, void *private, const ne_status *status)
+{
+ struct redirect *red = private;
+
+ /* 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);
+
+ /* 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 (!red->uri.host) {
+ /* Not an absoluteURI: breaks 2616 but everybody does it. */
+ ne_fill_server_uri(red->sess, &red->uri);
+ }
+
+ return NE_REDIRECT;
+}
+
+static void free_redirect(void *cookie)
+{
+ struct redirect *red = cookie;
+ NE_FREE(red->location);
+ ne_uri_free(&red->uri);
+ if (red->requri)
+ ne_free(red->requri);
+ ne_free(red);
+}
+
+void ne_redirect_register(ne_session *sess)
+{
+ struct redirect *red = ne_calloc(sizeof *red);
+
+ red->sess = sess;
+
+ 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 ne_uri *ne_redirect_location(ne_session *sess)
+{
+ struct redirect *red = ne_get_session_private(sess, REDIRECT_ID);
+
+ if (red && red->valid)
+ return &red->uri;
+ else
+ return NULL;
+}
+
diff --git a/src/ne_redirect.h b/src/ne_redirect.h
new file mode 100644
index 0000000..d0dd7b5
--- /dev/null
+++ b/src/ne_redirect.h
@@ -0,0 +1,41 @@
+/*
+ HTTP-redirect support
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_REDIRECT_H
+#define NE_REDIRECT_H
+
+#include "ne_request.h"
+
+BEGIN_NEON_DECLS
+
+/* 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);
+
+/* 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
+
+#endif /* NE_REDIRECT_H */
diff --git a/src/ne_request.c b/src/ne_request.c
new file mode 100644
index 0000000..0d39214
--- /dev/null
+++ b/src/ne_request.c
@@ -0,0 +1,1389 @@
+/*
+ HTTP request/response handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* This is the HTTP client request/response implementation.
+ * The goal of this code is to be modular and simple.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __EMX__
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h> /* for UINT_MAX etc */
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_i18n.h"
+
+#include "ne_alloc.h"
+#include "ne_request.h"
+#include "ne_string.h" /* for ne_buffer */
+#include "ne_utils.h"
+#include "ne_socket.h"
+#include "ne_uri.h"
+
+#include "ne_private.h"
+
+#define HTTP_EXPECT_TIMEOUT 15
+/* 100-continue only used if size > HTTP_EXPECT_MINSIZ */
+#define HTTP_EXPECT_MINSIZE 1024
+
+/* with thanks to Jim Blandy; this macro simplified loads of code. */
+#define HTTP_ERR(x) do { int _ret = (x); if (_ret != NE_OK) return _ret; } while (0)
+
+#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, 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
+ * magic "*33" hash function. Ralf Engelschall has done some amazing
+ * statistical analysis to show that *33 really is a good hash
+ * function: check the new-httpd list archives, or his 'str' library
+ * source code, for the details.
+ *
+ * TODO: due to limited range of characters used in header names,
+ * could maybe get a better hash function to use? */
+
+#define HH_ITERATE(hash, ch) (((hash)*33 + (ch)) % HH_HASHSIZE);
+
+/* Returns hash value for header 'name', converting it to lower-case
+ * in-place. */
+static inline unsigned int hash_and_lower(char *name)
+{
+ char *pnt;
+ unsigned int hash = 0;
+
+ for (pnt = name; *pnt != '\0'; pnt++) {
+ *pnt = tolower(*pnt);
+ hash = HH_ITERATE(hash,*pnt);
+ }
+
+ return hash;
+}
+
+/* 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 NE_SOCK_CLOSED:
+ if (sess->use_proxy) {
+ ne_set_error(sess, _("%s: connection was closed by proxy server."),
+ doing);
+ } else {
+ 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;
+ }
+
+ ne_close_connection(sess);
+ return ret;
+}
+
+static void notify_status(ne_session *sess, ne_conn_status status,
+ const char *info)
+{
+ if (sess->notify_cb) {
+ sess->notify_cb(sess->notify_ud, status, info);
+ }
+}
+
+void ne_duplicate_header(void *userdata, const char *value)
+{
+ char **location = userdata;
+ *location = ne_strdup(value);
+}
+
+void ne_handle_numeric_header(void *userdata, const char *value)
+{
+ int *location = userdata;
+ *location = atoi(value);
+}
+
+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)
+{
+ return get_private(req->private, id);
+}
+
+void *ne_get_session_private(ne_session *sess, const char *id)
+{
+ return get_private(sess->private, id);
+}
+
+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;
+ }
+
+ hk->id = id;
+ hk->fn = fn;
+ hk->userdata = ud;
+ hk->next = NULL;
+}
+
+void ne_hook_create_request(ne_session *sess,
+ ne_create_request_fn fn, void *userdata)
+{
+ ADD_HOOK(sess->create_req_hooks, fn, userdata);
+}
+
+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);
+}
+
+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)
+{
+ ne_request *req = userdata;
+
+ if (count == 0) {
+ 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.buf.left < count)
+ count = req->body.buf.left;
+
+ memcpy(buffer, req->body.buf.pnt, count);
+ req->body.buf.pnt += count;
+ req->body.buf.left -= count;
+ }
+
+ return count;
+}
+
+static ssize_t body_fd_send(void *userdata, char *buffer, size_t count)
+{
+ ne_request *req = userdata;
+
+ if (count) {
+ return read(req->body.fd, buffer, count);
+ } else {
+ /* rewind since we may have to send it again */
+ return lseek(req->body.fd, SEEK_SET, 0);
+ }
+}
+
+/* Pulls the request body from the source and pushes it to the given
+ * callback. Returns 0 on success, or NE_* code */
+int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud)
+{
+ int ret = 0;
+ char buffer[BUFSIZ];
+ ssize_t bytes;
+
+ /* tell the source to start again from the beginning. */
+ (void) req->body_cb(req->body_ud, NULL, 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) {
+ ne_set_error(req->session, _("Error reading request body."));
+ ret = NE_ERROR;
+ }
+
+ 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_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);
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Request body sent: %s.\n", ret?"failed":"okay");
+ return ret;
+}
+
+/* Lob the User-Agent, connection and host headers in to the request
+ * headers */
+static void add_fixed_headers(ne_request *req)
+{
+ if (req->session->user_agent) {
+ ne_buffer_concat(req->headers,
+ "User-Agent: ", req->session->user_agent, EOL, NULL);
+ }
+ /* Send Connection: Keep-Alive for pre-1.1 origin servers, so we
+ * might get a persistent connection. 2068 sec 19.7.1 says we MUST
+ * NOT do this for proxies, though. So we don't. Note that on the
+ * first request on any session, we don't know whether the server
+ * is 1.1 compliant, so we presume that it is not. */
+ if (!req->session->is_http11 && !req->session->use_proxy) {
+ ne_buffer_zappend(req->headers, "Keep-Alive: " EOL);
+ ne_buffer_zappend(req->headers, "Connection: TE, Keep-Alive" EOL);
+ } else {
+ ne_buffer_zappend(req->headers, "Connection: TE" 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, const ne_status *st)
+{
+ return 1;
+}
+
+int ne_accept_2xx(void *userdata, ne_request *req, const ne_status *st)
+{
+ return (st->klass == 2);
+}
+
+/* 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;
+
+ resp->mode = R_CHUNKED;
+}
+
+/* Handler for the "Connection" response header */
+static void connection_hdr_handler(void *userdata, const char *value)
+{
+ ne_request *req = userdata;
+ if (strcasecmp(value, "close") == 0) {
+ req->can_persist = 0;
+ } else if (strcasecmp(value, "Keep-Alive") == 0) {
+ req->can_persist = 1;
+ }
+}
+
+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 *path)
+{
+ ne_request *req = ne_calloc(sizeof *req);
+
+ NE_DEBUG(NE_DBG_HTTP, "Creating request...\n");
+
+ req->session = sess;
+ req->headers = ne_buffer_create();
+
+ /* Add in the fixed headers */
+ add_fixed_headers(req);
+
+ /* Set the standard stuff */
+ 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",
+ clength_hdr_handler, &req->resp);
+ ne_add_response_header_handler(req, "Transfer-Encoding",
+ te_hdr_handler, &req->resp);
+ ne_add_response_header_handler(req, "Connection",
+ connection_hdr_handler, req);
+
+ /* 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;
+
+ NE_DEBUG(NE_DBG_HTTP, "Running request create hooks.\n");
+
+ 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);
+ }
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Request created.\n");
+
+ return req;
+}
+
+static void set_body_size(ne_request *req, size_t size)
+{
+ req->body_size = size;
+ ne_print_request_header(req, "Content-Length", "%" NE_FMT_SIZE_T, size);
+}
+
+void ne_set_request_body_buffer(ne_request *req, const char *buffer,
+ size_t size)
+{
+ req->body.buf.buffer = buffer;
+ req->body_cb = body_string_send;
+ req->body_ud = req;
+ set_body_size(req, size);
+}
+
+void ne_set_request_body_provider(ne_request *req, size_t bodysize,
+ ne_provide_body provider, void *ud)
+{
+ req->body_cb = provider;
+ req->body_ud = ud;
+ set_body_size(req, bodysize);
+}
+
+int ne_set_request_body_fd(ne_request *req, int fd)
+{
+ struct stat bodyst;
+
+ /* Get file length */
+ if (fstat(fd, &bodyst) < 0) {
+ 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_cb = body_fd_send;
+ req->body_ud = req;
+ set_body_size(req, bodyst.st_size);
+ return 0;
+}
+
+void ne_add_request_header(ne_request *req, const char *name,
+ const char *value)
+{
+ ne_buffer_concat(req->headers, name, ": ", value, EOL, NULL);
+}
+
+void ne_print_request_header(ne_request *req, const char *name,
+ const char *format, ...)
+{
+ va_list params;
+ char buf[BUFSIZ];
+
+ va_start(params, format);
+ ne_vsnprintf(buf, sizeof buf, format, params);
+ va_end(params);
+
+ ne_buffer_concat(req->headers, name, ": ", buf, EOL, NULL);
+}
+
+void
+ne_add_response_header_handler(ne_request *req, const char *name,
+ ne_header_handler hdl, void *userdata)
+{
+ struct header_handler *new = ne_calloc(sizeof *new);
+ unsigned int hash;
+ new->name = ne_strdup(name);
+ new->handler = hdl;
+ new->userdata = userdata;
+ 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)
+{
+ struct header_handler *new = ne_calloc(sizeof *new);
+ new->handler = hdl;
+ new->userdata = userdata;
+ new->next = req->header_catchers;
+ req->header_catchers = new;
+}
+
+void ne_add_response_body_reader(ne_request *req, ne_accept_response acpt,
+ ne_block_reader rdr, void *userdata)
+{
+ struct body_reader *new = ne_malloc(sizeof *new);
+ new->accept_response = acpt;
+ new->handler = rdr;
+ new->userdata = userdata;
+ new->next = req->body_readers;
+ req->body_readers = new;
+}
+
+void ne_request_destroy(ne_request *req)
+{
+ struct body_reader *rdr, *next_rdr;
+ struct header_handler *hdlr, *next_hdlr;
+ struct hook *hk, *next_hk;
+ int n;
+
+ ne_free(req->uri);
+ ne_free(req->method);
+
+ for (rdr = req->body_readers; rdr != NULL; rdr = next_rdr) {
+ next_rdr = rdr->next;
+ ne_free(rdr);
+ }
+
+ for (hdlr = req->header_catchers; hdlr != NULL; hdlr = next_hdlr) {
+ next_hdlr = hdlr->next;
+ 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;
+ ne_free(hdlr->name);
+ ne_free(hdlr);
+ }
+ }
+
+ ne_buffer_destroy(req->headers);
+
+ NE_DEBUG(NE_DBG_HTTP, "Running destroy hooks.\n");
+ 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");
+ ne_free(req);
+}
+
+
+/* Reads a block of the response into buffer, which is of size buflen.
+ * Returns number of bytes read, 0 on end-of-response, or NE_* on error.
+ * TODO?: only make one actual read() call in here...
+ */
+static int read_response_block(ne_request *req, struct ne_response *resp,
+ char *buffer, size_t *buflen)
+{
+ 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.
+ * The slight complication is that we have to cope with
+ * partial reads of chunks.
+ * For this reason, resp.chunk_left contains the number of
+ * bytes left to read in the current chunk.
+ */
+ if (resp->chunk_left == 0) {
+ unsigned long int chunk_len;
+ char *ptr;
+ /* We are at the start of a new chunk. */
+ NE_DEBUG(NE_DBG_HTTP, "New chunk.\n");
+ 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 = 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: %lu\n", chunk_len);
+ if (chunk_len == 0) {
+ /* Zero-size chunk == end of response. */
+ NE_DEBUG(NE_DBG_HTTP, "Zero-size chunk.\n");
+ *buflen = 0;
+ return NE_OK;
+ }
+ resp->chunk_left = chunk_len;
+ }
+ willread = 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;
+ }
+ 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. For
+ * interop with SSL servers which perform unclean shutdown, ignore
+ * a truncation if no response body has yet been read. */
+ if (resp->mode == R_TILLEOF &&
+ (readlen == NE_SOCK_CLOSED ||
+ (readlen == NE_SOCK_TRUNC && resp->total == 0))) {
+ NE_DEBUG(NE_DBG_HTTP, "Got EOF.\n");
+ req->can_persist = 0;
+ readlen = 0;
+ } else if (readlen < 0) {
+ return aborted(req, _("Could not read response body"), readlen);
+ } else {
+ NE_DEBUG(NE_DBG_HTTP, "Got %" NE_FMT_SSIZE_T " bytes.\n", readlen);
+ }
+ /* 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 = 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->mode == R_CLENGTH) {
+ resp->left -= readlen;
+ }
+ return NE_OK;
+}
+
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen)
+{
+ struct body_reader *rdr;
+ size_t readlen = buflen;
+
+ if (read_response_block(req, &req->resp, buffer, &readlen))
+ return -1;
+
+ req->resp.total += readlen;
+
+ if (req->session->progress_cb) {
+ req->session->progress_cb(req->session->progress_ud, req->resp.total,
+ (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);
+ }
+
+ return readlen;
+}
+
+/* Build the request string, returning the buffer. */
+static ne_buffer *build_request(ne_request *req)
+{
+ struct hook *hk;
+ ne_buffer *buf = ne_buffer_create();
+
+ /* 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);
+
+ /* Add custom headers: */
+ ne_buffer_append(buf, req->headers->data, ne_buffer_size(req->headers));
+
+#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 (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);
+ }
+
+ ne_buffer_append(buf, "\r\n", 2);
+ return buf;
+}
+
+#ifdef NE_DEBUGGING
+#define DEBUG_DUMP_REQUEST(x) dump_request(x)
+
+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 */
+
+/* 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)--;
+ }
+}
+
+/* For persistent connection handling: if the first socket operation
+ * on an already-open connection fails with an EOF error, then presume
+ * a persistent connection timeout has occurred, and the request
+ * should be retried. The 'retry' flag is zero once a socket
+ * operation has succeeded on the connection. RETRY_RET() crafts the
+ * appropriate return value given a '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;
+
+ 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 (ne_parse_statusline(buffer, status))
+ return aborted(req, _("Could not parse response status line."), 0);
+
+ 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, retry;
+ 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);
+ }
+
+ /* FIXME: probably due to Nagle, the write above may or may not
+ * have been delayed, so retry is left at 1 here. */
+
+ 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;
+ /* 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:
+ * NE_RETRY: Success, read a header into buf.
+ * NE_OK: End of headers reached.
+ * NE_ERROR: Error (session error is set).
+ */
+static int read_message_header(ne_request *req, char *buf, size_t buflen)
+{
+ ssize_t n;
+ ne_socket *sock = req->session->socket;
+
+ n = ne_sock_readline(sock, buf, buflen);
+ if (n <= 0)
+ return aborted(req, _("Error reading response headers"), n);
+ NE_DEBUG(NE_DBG_HTTP, "[hdr] %s", buf);
+
+ strip_eol(buf, &n);
+
+ 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 */
+ SOCK_ERR(req, ne_sock_peek(sock, &ch, 1),
+ _("Error reading response headers"));
+
+ if (ch != ' ' && ch != '\t') {
+ /* No continuation of this header: stop reading. */
+ return NE_RETRY;
+ }
+
+ /* Otherwise, read the next line onto the end of 'buf'. */
+ 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, &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 (n) buf[0] = ' ';
+
+ /* ready for the next header. */
+ buf += n;
+ buflen -= n;
+ }
+
+ ne_set_error(req->session, _("Response header too long"));
+ return NE_ERROR;
+}
+
+/* Apache's default is 100, seems reasonable. */
+#define MAX_HEADER_FIELDS 100
+
+/* Read response headers. Returns NE_* code, sets session error. */
+static int read_response_headers(ne_request *req)
+{
+ char hdr[8192]; /* max header length */
+ int ret, count = 0;
+
+ while ((ret = read_message_header(req, hdr, sizeof hdr)) == NE_RETRY
+ && ++count < MAX_HEADER_FIELDS) {
+ struct header_handler *hdl;
+ char *pnt;
+ unsigned int hash = 0;
+
+ 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);
+ }
+
+ /* 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)
+ 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;
+
+ /* 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->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));
+
+#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, 304 responses have no response body,
+ * regardless of what headers are present. */
+ if (req->method_is_head || st->code==204 || st->code==304)
+ req->resp.mode = R_NO_BODY;
+
+ /* 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, st);
+ }
+
+ req->resp.left = req->resp.length;
+ req->resp.chunk_left = 0;
+
+ return NE_OK;
+}
+
+int ne_end_request(ne_request *req)
+{
+ struct hook *hk;
+ int ret = NE_OK;
+
+ /* Read headers in chunked trailers */
+ if (req->resp.mode == R_CHUNKED)
+ HTTP_ERR(read_response_headers(req));
+
+ NE_DEBUG(NE_DBG_HTTP, "Running post_send hooks\n");
+ 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);
+ }
+
+ /* 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;
+
+ return ret;
+}
+
+int ne_request_dispatch(ne_request *req)
+{
+ int ret;
+
+ /* Loop sending the request:
+ * Retry whilst authentication fails and we supply it. */
+
+ do {
+ ssize_t len;
+
+ HTTP_ERR(ne_begin_request(req));
+
+ do {
+ len = ne_read_response_block(req, req->respbuf,
+ sizeof req->respbuf);
+ } while (len > 0);
+
+ if (len < 0) {
+ return NE_ERROR;
+ }
+
+ ret = ne_end_request(req);
+
+ } while (ret == NE_RETRY);
+
+ NE_DEBUG(NE_DBG_HTTP | NE_DBG_FLUSH,
+ "Request ends, status %d class %dxx, error line:\n%s\n",
+ req->status.code, req->status.klass, req->session->error);
+
+ return ret;
+}
+
+const ne_status *ne_get_status(const ne_request *req)
+{
+ return &req->status;
+}
+
+ne_session *ne_get_session(const ne_request *req)
+{
+ 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;
+ int ret = NE_OK;
+ char ruri[200];
+
+ /* 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->in_connect = 1;
+ ret = ne_request_dispatch(req);
+ sess->in_connect = 0;
+
+ 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
+
+/* 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 *const sess = req->session;
+ int ret;
+
+ if ((sess->socket = ne_sock_create()) == NULL) {
+ ne_set_error(sess, _("Could not create socket"));
+ return NE_ERROR;
+ }
+
+ if (host->current == NULL)
+ host->current = ne_addr_first(host->address);
+
+ 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) {
+ aborted(req, err, ret);
+ return NE_CONNECT;
+ }
+
+ notify_status(sess, ne_conn_connected, sess->proxy.hostport);
+
+ if (sess->rdtimeout)
+ ne_sock_read_timeout(sess->socket, sess->rdtimeout);
+
+ 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;
+
+ 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);
+ }
+#endif
+
+ return ret;
+}
diff --git a/src/ne_request.h b/src/ne_request.h
new file mode 100644
index 0000000..f626402
--- /dev/null
+++ b/src/ne_request.h
@@ -0,0 +1,275 @@
+/*
+ HTTP Request Handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_REQUEST_H
+#define NE_REQUEST_H
+
+#include "ne_utils.h" /* For ne_status */
+#include "ne_string.h" /* For sbuffer */
+#include "ne_session.h"
+
+BEGIN_NEON_DECLS
+
+#define NE_OK (0) /* Success */
+#define NE_ERROR (1) /* Generic error; use ne_get_error(session) for message */
+#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 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);
+
+/* 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. */
+int ne_set_request_body_fd(ne_request *req, int fd);
+
+/* "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 request.
+ * 0 : ignore 'buffer' contents, end of body.
+ * 0 < x <= buflen : buffer contains x bytes of body data. */
+typedef ssize_t (*ne_provide_body)(void *userdata,
+ char *buffer, size_t buflen);
+
+/* 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);
+
+/* Handling response bodies... you provide TWO callbacks:
+ *
+ * 1) 'acceptance' callback: determines whether you want to handle the
+ * response body given the response-status information, e.g., if you
+ * only want 2xx responses, say so here.
+ *
+ * 2) 'reader' callback: passed blocks of the response-body as they
+ * arrive, if the acceptance callback returned non-zero. */
+
+/* 'acceptance' callback type. Return non-zero to accept the response,
+ * else zero to ignore it. */
+typedef int (*ne_accept_response)(
+ void *userdata, ne_request *req, const ne_status *st);
+
+/* An 'acceptance' callback which only accepts 2xx-class responses.
+ * Ignores userdata. */
+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, const ne_status *st);
+
+/* 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
+ * the acceptance + reader callbacks.
+ *
+ * The acceptance callback is called once each time the request is
+ * sent: it may be sent >1 time because of authentication retries etc.
+ * For each time the acceptance callback is called, if it returns
+ * non-zero, blocks of the response body will be passed to the reader
+ * callback as the response is read. After all the response body has
+ * been read, the callback will be called with a 'len' argument of
+ * zero. */
+void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
+ ne_block_reader reader, void *userdata);
+
+/* Handle response headers. Each handler is associated with a specific
+ * header field (indicated by name). The handler is then passed the
+ * value of this header field. */
+
+/* The header handler callback type */
+typedef void (*ne_header_handler)(void *userdata, const char *value);
+
+/* Adds a response header handler for the given request. userdata is passed
+ * as the first argument to the header handler, and the 'value' is the
+ * header field value (i.e. doesn't include the "Header-Name: " part").
+ */
+void ne_add_response_header_handler(ne_request *req, const char *name,
+ ne_header_handler hdl, void *userdata);
+
+/* Add handler which is passed ALL header values regardless of name */
+void ne_add_response_header_catcher(ne_request *req,
+ ne_header_handler hdl, void *userdata);
+
+/* Stock header handlers:
+ * 'duplicate': *(char **)userdata = strdup(value)
+ * 'numeric': *(int *)userdata = atoi(value)
+ * e.g.
+ * int mynum;
+ * ne_add_response_header_handler(myreq, "Content-Length",
+ * ne_handle_numeric_handler, &mynum);
+ * ... arranges mynum to be set to the value of the Content-Length header.
+ */
+void ne_duplicate_header(void *userdata, const char *value);
+void ne_handle_numeric_header(void *userdata, const char *value);
+
+/* Adds a header to the request with given name and value. */
+void ne_add_request_header(ne_request *req, const char *name,
+ const char *value);
+/* Adds a header to the request with given name, using printf-like
+ * format arguments for the value. */
+void ne_print_request_header(ne_request *req, const char *name,
+ const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 3, 4)))
+#endif /* __GNUC__ */
+;
+
+/* ne_request_dispatch: Sends the given request, and reads the
+ * response. Response-Status information can be retrieve with
+ * ne_get_status(req).
+ *
+ * NE_OK if request sent + response read okay.
+ * 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
+ */
+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(const ne_request *req)
+/* Declare this with attribute const, since we often call it >1 times
+ * with the same argument, and it will return the same thing each
+ * time. This lets the compiler optimize away any subsequent calls
+ * (theoretically). */
+#ifdef __GNUC__
+ __attribute__ ((const))
+#endif /* __GNUC__ */
+ ;
+
+/* 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);
+
+/* "Caller-pulls" request interface. This is an ALTERNATIVE interface
+ * to ne_request_dispatch: either use that, or do all this yourself:
+ *
+ * caller must call:
+ * 1. ne_begin_request (fail if returns non-NE_OK)
+ * 2. while(ne_read_response_block(...) > 0) ... loop ...;
+ * (fail if ne_read_response_block returns <0)
+ * 3. ne_end_request
+ *
+ * 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);
+
+/* Read a block of the response. buffer must be at least 128 bytes.
+ * 'buflen' must be length of buffer.
+ *
+ * Returns:
+ * <0 - error, stop reading.
+ * 0 - end of response
+ * >0 - number of bytes read into buffer.
+ */
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);
+
+/**** Request hooks handling *****/
+
+typedef void (*ne_free_hooks)(void *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).
+ */
+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
+
+#endif /* NE_REQUEST_H */
+
diff --git a/src/ne_session.c b/src/ne_session.c
new file mode 100644
index 0000000..fe71079
--- /dev/null
+++ b/src/ne_session.c
@@ -0,0 +1,272 @@
+/*
+ HTTP session handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#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"
+
+/* Destroy a a list of hooks. */
+static void destroy_hooks(struct hook *hooks)
+{
+ struct hook *nexthk;
+
+ while (hooks) {
+ nexthk = hooks->next;
+ ne_free(hooks);
+ hooks = nexthk;
+ }
+}
+
+void ne_session_destroy(ne_session *sess)
+{
+ struct hook *hk;
+
+ NE_DEBUG(NE_DBG_HTTP, "ne_session_destroy called.\n");
+
+ /* 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);
+ }
+
+ 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->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);
+ NE_FREE(sess->proxy.hostname);
+ NE_FREE(sess->scheme);
+ NE_FREE(sess->user_agent);
+
+ if (sess->connected) {
+ ne_close_connection(sess);
+ }
+
+#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->is_http11;
+}
+
+/* Stores the "hostname[:port]" segment */
+static void set_hostport(struct host_info *host, unsigned int defaultport)
+{
+ size_t len = strlen(host->hostname);
+ 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, unsigned int port)
+{
+ NE_FREE(info->hostport);
+ NE_FREE(info->hostname);
+ info->hostname = ne_strdup(hostname);
+ info->port = port;
+}
+
+ne_session *ne_session_create(const char *scheme,
+ const char *hostname, unsigned int port)
+{
+ ne_session *sess = ne_calloc(sizeof *sess);
+
+ NE_DEBUG(NE_DBG_HTTP, "HTTP session to %s://%s:%d begins.\n",
+ scheme, hostname, port);
+
+ strcpy(sess->error, "Unknown error.");
+
+ /* 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);
+
+#ifdef NEON_SSL
+ if (sess->use_ssl) {
+ sess->ssl_context = ne_ssl_context_create();
+ }
+#endif
+
+ sess->scheme = ne_strdup(scheme);
+
+ /* Default expect-100 to OFF. */
+ sess->expect100_works = -1;
+ return sess;
+}
+
+void ne_session_proxy(ne_session *sess, const char *hostname,
+ unsigned int port)
+{
+ sess->use_proxy = 1;
+ set_hostinfo(&sess->proxy, hostname, port);
+}
+
+void ne_set_error(ne_session *sess, const char *format, ...)
+{
+ 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,
+ ne_progress progress, void *userdata)
+{
+ sess->progress_cb = progress;
+ sess->progress_ud = userdata;
+}
+
+void ne_set_status(ne_session *sess,
+ ne_notify_status status, void *userdata)
+{
+ sess->notify_cb = status;
+ sess->notify_ud = userdata;
+}
+
+void ne_set_expect100(ne_session *sess, int use_expect100)
+{
+ if (use_expect100) {
+ sess->expect100_works = 1;
+ } else {
+ sess->expect100_works = -1;
+ }
+}
+
+void ne_set_persist(ne_session *sess, int persist)
+{
+ sess->no_persist = !persist;
+}
+
+void ne_set_read_timeout(ne_session *sess, int timeout)
+{
+ sess->rdtimeout = timeout;
+}
+
+#define AGENT " neon/" NEON_VERSION
+
+void ne_set_useragent(ne_session *sess, const char *token)
+{
+ if (sess->user_agent) ne_free(sess->user_agent);
+ sess->user_agent = ne_malloc(sizeof AGENT + strlen(token));
+ strcat(strcpy(sess->user_agent, token), AGENT);
+}
+
+const char *ne_get_server_hostport(ne_session *sess)
+{
+ return sess->server.hostport;
+}
+
+const char *ne_get_scheme(ne_session *sess)
+{
+ 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 ne_strclean(sess->error);
+}
+
+void ne_close_connection(ne_session *sess)
+{
+ 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;
+}
+
+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
+}
+
+
+
diff --git a/src/ne_session.h b/src/ne_session.h
new file mode 100644
index 0000000..027e69e
--- /dev/null
+++ b/src/ne_session.h
@@ -0,0 +1,195 @@
+/*
+ HTTP session handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_SESSION_H
+#define NE_SESSION_H 1
+
+#include <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;
+
+/* 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 */
+void ne_session_destroy(ne_session *sess);
+
+/* Prematurely force the connection to be closed for the given
+ * session. */
+void ne_close_connection(ne_session *sess);
+
+/* 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
+ * persist: Defaults to ON
+ *
+ * expect100: When set, send the "Expect: 100-continue" request header
+ * with requests with bodies.
+ *
+ * persist: When set, use a persistent connection. (Generally,
+ * you don't want to turn this off.)
+ * */
+void ne_set_expect100(ne_session *sess, int use_expect100);
+void ne_set_persist(ne_session *sess, int persist);
+
+/* 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,
+ 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) */
+ ne_conn_connecting, /* connecting to host (info = hostname) */
+ ne_conn_connected, /* connected to host (info = hostname) */
+ ne_conn_secure /* connection now secure (info = crypto level) */
+} ne_conn_status;
+
+typedef void (*ne_notify_status)(void *userdata,
+ ne_conn_status status,
+ const char *info);
+
+
+/* Set a status notification callback for the session, to report
+ * connection status. */
+void ne_set_status(ne_session *sess,
+ ne_notify_status status, void *userdata);
+
+/* 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".
+ * If this function is not called, the User-Agent header is not sent.
+ * The product string must follow the RFC2616 format, i.e.
+ * product = token ["/" product-version]
+ * product-version = token
+ * where token is any alpha-numeric-y string [a-zA-Z0-9]* */
+void ne_set_useragent(ne_session *sess, const char *product);
+
+/* 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". */
+const char *ne_get_server_hostport(ne_session *sess);
+
+/* 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);
+
+/* 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, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif /* __GNUC__ */
+;
+
+/* Retrieve the error string for the session */
+const char *ne_get_error(ne_session *sess);
+
+END_NEON_DECLS
+
+#endif /* NE_SESSION_H */
diff --git a/src/ne_socket.c b/src/ne_socket.c
new file mode 100644
index 0000000..f67e233
--- /dev/null
+++ b/src/ne_socket.c
@@ -0,0 +1,1040 @@
+/*
+ socket handling routines
+ Copyright (C) 1998-2003, Joe Orton <joe@manyfish.co.uk>,
+ 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
+*/
+
+/*
+ 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
+#include <sys/stat.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <stddef.h>
+#endif
+
+#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_UNISTD_H
+#include <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
+
+#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(AI_ADDRCONFIG) && defined(EAI_BADFLAGS)
+#define USE_ADDRCONFIG
+#elif 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__) && !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 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 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 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
+
+/* 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 ne_socket_s {
+ int fd;
+ char error[200];
+ void *progress_ud;
+ int rdtimeout; /* read timeout. */
+ const struct iofns *ops;
+#ifdef NEON_SSL
+ ne_ssl_socket ssl;
+#endif
+ /* 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;
+};
+
+/* 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
+
+#ifdef NEON_SSL
+static int prng_seeded = 0;
+
+/* Initialize SSL library; returns non-zero on failure. */
+static int init_ssl(void)
+{
+ SSL_load_error_strings();
+ SSL_library_init();
+ PKCS12_PBE_add(); /* ### not sure why this is needed. */
+
+ /* 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;
+
+/* 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) {
+ init_result = -1;
+ return -1;
+ }
+
+#endif
+
+#ifdef NEON_SOCKS
+ SOCKSinit("neon");
+#endif
+
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE)
+ (void) signal(SIGPIPE, SIG_IGN);
+#endif
+
+#ifdef USE_CHECK_IPV6
+ init_ipv6();
+#endif
+
+#ifdef NEON_SSL
+ if (init_ssl()) {
+ NE_DEBUG(NE_DBG_SOCKET, "SSL initialization failed; lacking PRNG?\n");
+ init_result = -1;
+ return -1;
+ }
+ prng_seeded = 1;
+#endif
+
+ init_result = 1;
+ return 0;
+}
+
+void ne_sock_exit(void)
+{
+#ifdef WIN32
+ WSACleanup();
+#endif
+ init_result = 0;
+}
+
+int ne_sock_block(ne_socket *sock, int n)
+{
+ if (sock->bufavail)
+ return 0;
+ return sock->ops->readable(sock, n);
+}
+
+/* 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
+
+ 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;
+ }
+}
+
+ssize_t ne_sock_peek(ne_socket *sock, char *buffer, size_t buflen)
+{
+ ssize_t bytes;
+
+ if (sock->bufavail) {
+ /* just return buffered data. */
+ bytes = sock->bufavail;
+ } else {
+ /* 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;
+ }
+
+ if (buflen > (size_t)bytes)
+ buflen = bytes;
+
+ memcpy(buffer, sock->bufpos, buflen);
+
+ return buflen;
+}
+
+/* Await data on raw fd in socket. */
+static int readable_raw(ne_socket *sock, int secs)
+{
+ int fdno = sock->fd, ret;
+ fd_set rdfds;
+ struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
+
+ /* Init the fd set */
+ FD_ZERO(&rdfds);
+ do {
+ 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;
+}
+
+static ssize_t read_raw(ne_socket *sock, char *buffer, size_t len)
+{
+ 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;
+}
+
+#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)
+{
+ ssize_t wrote;
+
+ do {
+ 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 };
+
+#ifdef NEON_SSL
+/* OpenSSL I/O function implementations. */
+static int readable_ossl(ne_socket *sock, int secs)
+{
+ /* 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)) */
+
+ 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);
+ }
+ } else {
+ ne_snprintf(sock->error, sizeof sock->error,
+ _("SSL error: %s"), ERR_reason_error_string(err));
+ }
+ break;
+ default:
+ ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"),
+ ERR_reason_error_string(ERR_get_error()));
+ break;
+ }
+ return ret;
+}
+
+/* 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)
+{
+ 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;
+}
+
+static ssize_t write_ossl(ne_socket *sock, const char *data, size_t len)
+{
+ 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;
+}
+
+static const struct iofns iofns_ossl = {
+ read_ossl,
+ write_ossl,
+ readable_ossl
+};
+
+#endif /* NEON_SSL */
+
+int ne_sock_fullwrite(ne_socket *sock, const char *data, size_t len)
+{
+ return sock->ops->write(sock, data, len);
+}
+
+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);
+ }
+
+ if (lf)
+ len = lf - sock->bufpos + 1;
+ else
+ len = buflen; /* fall into "line too long" error... */
+
+ if ((len + 1) > buflen) {
+ set_error(sock, _("Line too long"));
+ return NE_SOCK_ERROR;
+ }
+
+ memcpy(buf, sock->bufpos, len);
+ buf[len] = '\0';
+ /* consume the line from buffer: */
+ sock->bufavail -= len;
+ sock->bufpos += len;
+ return len;
+}
+
+ssize_t ne_sock_fullread(ne_socket *sock, char *buffer, size_t buflen)
+{
+ ssize_t len;
+
+ while (buflen > 0) {
+ len = ne_sock_read(sock, buffer, buflen);
+ if (len < 0) return len;
+ buflen -= len;
+ buffer += len;
+ }
+
+ return 0;
+}
+
+#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)
+{
+ 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_ADDRCONFIG /* added in the RFC3493 API */
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_family = AF_UNSPEC;
+ addr->errnum = getaddrinfo(hostname, NULL, &hints, &addr->result);
+ if (addr->errnum != EAI_BADFLAGS)
+ return addr;
+ /* Retry without AI_ADDRCONFIG if this libc doesn't grok it */
+ hints.ai_flags = 0;
+#else
+ hints.ai_family = ipv6_disabled ? AF_INET : AF_UNSPEC;
+#endif
+ addr->errnum = getaddrinfo(hostname, NULL, &hints, &addr->result);
+ }
+#else /* Use gethostbyname() */
+ unsigned long laddr;
+ struct hostent *hp;
+
+ laddr = inet_addr(hostname);
+ if (laddr == INADDR_NONE) {
+ hp = gethostbyname(hostname);
+ if (hp == NULL) {
+#ifdef WIN32
+ addr->errnum = WSAGetLastError();
+#else
+ addr->errnum = h_errno;
+#endif
+ } 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);
+ }
+ } else {
+ addr->addrs = ne_malloc(sizeof *addr->addrs);
+ addr->count = 1;
+ memcpy(addr->addrs, &laddr, sizeof *addr->addrs);
+ }
+#endif
+ return addr;
+}
+
+int ne_addr_result(const ne_sock_addr *addr)
+{
+ return addr->errnum;
+}
+
+const ne_inet_addr *ne_addr_first(ne_sock_addr *addr)
+{
+#ifdef USE_GETADDRINFO
+ addr->cursor = addr->result->ai_next;
+ return addr->result;
+#else
+ addr->cursor = 0;
+ return &addr->addrs[0];
+#endif
+}
+
+const ne_inet_addr *ne_addr_next(ne_sock_addr *addr)
+{
+#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;
+}
+
+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;
+}
+
+char *ne_iaddr_print(const ne_inet_addr *ia, char *buf, size_t bufsiz)
+{
+#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 {
+ 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
+}
+
+ne_socket *ne_sock_create(void)
+{
+ 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;
+}
+
+int ne_sock_connect(ne_socket *sock,
+ const ne_inet_addr *addr, unsigned int port)
+{
+ 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
+ if (fd < 0) {
+ set_strerror(sock, ne_errno);
+ return -1;
+ }
+
+ if (raw_connect(fd, addr, ntohs(port))) {
+ set_strerror(sock, ne_errno);
+ ne_close(fd);
+ return -1;
+ }
+
+ sock->fd = fd;
+ return 0;
+}
+
+ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw)
+{
+ 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
+ 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;
+}
+
+int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2)
+{
+#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 ne_iaddr_free(ne_inet_addr *addr)
+{
+#ifdef USE_GETADDRINFO
+ ne_free(addr->ai_addr);
+#endif
+ ne_free(addr);
+}
+
+int ne_sock_accept(ne_socket *sock, int listener)
+{
+ int fd = accept(listener, NULL, NULL);
+
+ if (fd < 0)
+ return -1;
+
+ sock->fd = fd;
+ return 0;
+}
+
+int ne_sock_fd(const ne_socket *sock)
+{
+ return sock->fd;
+}
+
+void ne_sock_read_timeout(ne_socket *sock, int timeout)
+{
+ sock->rdtimeout = timeout;
+}
+
+#ifdef NEON_SSL
+
+void ne_sock_switch_ssl(ne_socket *sock, void *ssl)
+{
+ sock->ssl.ssl = ssl;
+ sock->ops = &iofns_ossl;
+}
+
+int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx)
+{
+ SSL *ssl;
+ int ret;
+
+ if (!prng_seeded && RAND_status() != 1) {
+ set_error(sock, _("SSL disabled due to lack of entropy"));
+ return NE_SOCK_ERROR;
+ }
+
+ sock->ssl.ssl = ssl = SSL_new(ctx->ctx);
+ if (!ssl) {
+ set_error(sock, _("Could not create SSL structure"));
+ return NE_SOCK_ERROR;
+ }
+
+ SSL_set_app_data(ssl, ctx);
+ SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
+ SSL_set_fd(ssl, sock->fd);
+ sock->ops = &iofns_ossl;
+
+ 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;
+ }
+
+ return 0;
+}
+
+ne_ssl_socket *ne_sock_sslsock(ne_socket *sock)
+{
+ return &sock->ssl;
+}
+
+#endif
+
+const char *ne_sock_error(const ne_socket *sock)
+{
+ return sock->error;
+}
+
+/* Closes given ne_socket */
+int ne_sock_close(ne_socket *sock)
+{
+ int ret;
+#ifdef NEON_SSL
+ if (sock->ssl.ssl) {
+ SSL_shutdown(sock->ssl.ssl);
+ SSL_free(sock->ssl.ssl);
+ }
+#endif
+ if (sock->fd < 0)
+ ret = 0;
+ else
+ ret = ne_close(sock->fd);
+ ne_free(sock);
+ return ret;
+}
+
+/* Returns HOST byte order port of given name */
+int ne_service_lookup(const char *name)
+{
+ struct servent *ent;
+ ent = getservbyname(name, "tcp");
+ if (ent)
+ return ntohs(ent->s_port);
+ return 0;
+}
diff --git a/src/ne_socket.h b/src/ne_socket.h
new file mode 100644
index 0000000..58f45ff
--- /dev/null
+++ b/src/ne_socket.h
@@ -0,0 +1,197 @@
+/*
+ socket handling interface
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_SOCKET_H
+#define NE_SOCKET_H
+
+#include <sys/types.h>
+
+#include "ne_defs.h"
+#include "ne_ssl.h" /* for ne_ssl_context */
+
+BEGIN_NEON_DECLS
+
+/* define ssize_t for Win32 */
+#if defined(WIN32) && !defined(ssize_t)
+#define ssize_t int
+#endif
+
+#define NE_SOCK_ERROR (-1)
+/* Read/Write timed out */
+#define NE_SOCK_TIMEOUT (-2)
+/* Socket was closed */
+#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. */
+
+/* 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);
+
+/* Destroys an address object created by ne_addr_resolve. */
+void ne_addr_destroy(ne_sock_addr *addr);
+
+/* Network address type; IPv4 or IPv6 */
+typedef enum {
+ ne_iaddr_ipv4 = 0,
+ ne_iaddr_ipv6
+} ne_iaddr_type;
+
+/* 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);
+
+/* 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);
+
+/* 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);
+
+/* Free a network address created using ne_iaddr_make. */
+void ne_iaddr_free(ne_inet_addr *addr);
+
+/* Create a TCP socket; returns NULL on error. */
+ne_socket *ne_sock_create(void);
+
+/* 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:
+ * NE_SOCK_* on error,
+ * >0 length of data read into buffer.
+ */
+ssize_t ne_sock_read(ne_socket *sock, char *buffer, size_t count);
+
+/* 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:
+ * NE_SOCK_* on error,
+ * >0 length of data read into buffer.
+ */
+ssize_t ne_sock_peek(ne_socket *sock, char *buffer, size_t count);
+
+/* 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 ne_sock_block(ne_socket *sock, int n);
+
+/* 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)
+ */
+ssize_t ne_sock_readline(ne_socket *sock, char *buffer, size_t len);
+
+/* 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);
+
+/* Accept a connection on listening socket 'fd'. */
+int ne_sock_accept(ne_socket *sock, int fd);
+
+/* Returns the file descriptor used for socket 'sock'. */
+int ne_sock_fd(const ne_socket *sock);
+
+/* Close the socket, and destroy the socket object. Returns non-zero
+ * on error. */
+int ne_sock_close(ne_socket *sock);
+
+/* Return current error string for socket. */
+const char *ne_sock_error(const ne_socket *sock);
+
+/* Set read timeout for socket. */
+void ne_sock_read_timeout(ne_socket *sock, int timeout);
+
+/* 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
+
+#endif /* NE_SOCKET_H */
diff --git a/src/ne_ssl.h b/src/ne_ssl.h
new file mode 100644
index 0000000..959ff7b
--- /dev/null
+++ b/src/ne_ssl.h
@@ -0,0 +1,150 @@
+/*
+ SSL/TLS abstraction layer for 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
+
+*/
+
+/* 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 malloc-allocated 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 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 the client cert has been
+ * decrypted). */
+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
diff --git a/src/ne_string.c b/src/ne_string.c
new file mode 100644
index 0000000..3cddad9
--- /dev/null
+++ b/src/ne_string.c
@@ -0,0 +1,519 @@
+/*
+ String utility functions
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_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)
+{
+ char *ret = *str, *pnt = strchr(*str, separator);
+
+ 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);
+
+ 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;
+ return ret;
+ }
+ }
+
+ /* no separator found: return end of string. */
+ ret = *str;
+ *str = NULL;
+ return ret;
+}
+
+char *ne_shave(char *str, const char *whitespace)
+{
+ char *pnt, *ret = str;
+
+ while (*ret != '\0' && strchr(whitespace, *ret) != NULL) {
+ ret++;
+ }
+
+ /* pnt points at the NUL terminator. */
+ pnt = &ret[strlen(ret)];
+
+ while (pnt > ret && strchr(whitespace, *(pnt-1)) != NULL) {
+ pnt--;
+ }
+
+ *pnt = '\0';
+ return ret;
+}
+
+/* TODO: deprecate all these and use ne_token() instead. */
+
+char **split_string(const char *str, const char separator,
+ const char *quotes, const char *whitespace)
+{
+ return split_string_c(str, separator, quotes, whitespace, NULL);
+}
+
+char **split_string_c(const char *str, const char separator,
+ const char *quotes, const char *whitespace,
+ int *give_count)
+{
+ char **comps;
+ const char *pnt, *quot = NULL,
+ *start, *end; /* The start of the current component */
+ int count, /* The number of components */
+ iswhite, /* is it whitespace */
+ issep, /* is it the separator */
+ curr, /* current component index */
+ length, /* length of component */
+ leading_wspace; /* in leading whitespace still? */
+
+ /* Inefficient, but easier - first off, count the number of
+ * components we have. */
+ count = 1;
+ for (pnt = str; *pnt!='\0'; pnt++) {
+ if (quotes != NULL) {
+ quot = strchr(quotes, *pnt);
+ }
+ if (quot != NULL) {
+ /* We found a quote, so skip till the next quote */
+ for (pnt++; (*pnt!=*quot) && (*pnt!='\0'); pnt++)
+ /* nullop */;
+ } else if (*pnt == separator) {
+ count++;
+ }
+ }
+
+ if (give_count) {
+ /* Write the count */
+ *give_count = count;
+ }
+
+ /* Now, have got the number of components.
+ * Allocate the comps array. +1 for the NULL */
+ comps = ne_malloc(sizeof(char *) * (count + 1));
+
+ comps[count] = NULL;
+
+ quot = end = start = NULL;
+ curr = 0;
+ leading_wspace = 1;
+
+ /* Now fill in the array */
+ for (pnt = str; *pnt != '\0'; pnt++) {
+ /* What is the current character - quote, whitespace, separator? */
+ if (quotes != NULL) {
+ quot = strchr(quotes, *pnt);
+ }
+ iswhite = (whitespace!=NULL) &&
+ (strchr(whitespace, *pnt) != NULL);
+ issep = (*pnt == separator);
+ /* What to do? */
+ if (leading_wspace) {
+ if (quot!=NULL) {
+ /* Quoted bit */
+ start = pnt;
+ length = 1;
+ leading_wspace = 0;
+ } else if (issep) {
+ /* Zero-length component */
+ comps[curr++] = ne_strdup("");
+ } else if (!iswhite) {
+ start = end = pnt;
+ length = 1;
+ leading_wspace = 0;
+ }
+ } else {
+ if (quot!=NULL) {
+ /* Quoted bit */
+ length++;
+ } else if (issep) {
+ /* End of component - enter it into the array */
+ length = (end - start) + 1;
+ comps[curr] = ne_malloc(length+1);
+ memcpy(comps[curr], start, length);
+ comps[curr][length] = '\0';
+ curr++;
+ leading_wspace = 1;
+ } else if (!iswhite) {
+ /* Not whitespace - update end marker */
+ end = pnt;
+ }
+ }
+ if (quot != NULL) {
+ /* Skip to closing quote */
+ for (pnt++; *pnt!=*quot && *pnt != '\0'; ++pnt)
+ /* nullop */;
+ /* Last non-wspace char is closing quote */
+ end = pnt;
+ }
+ }
+ /* Handle final component */
+ if (leading_wspace) {
+ comps[curr] = ne_strdup("");
+ } else {
+ /* End of component - enter it into the array */
+ length = (end - start) + 1;
+ comps[curr] = ne_malloc(length+1);
+ memcpy(comps[curr], start, length);
+ comps[curr][length] = '\0';
+ }
+ return comps;
+}
+
+char **pair_string(const char *str, const char compsep, const char kvsep,
+ const char *quotes, const char *whitespace)
+{
+ char **comps, **pairs, *split;
+ int count = 0, n, length;
+ comps = split_string_c(str, compsep, quotes, whitespace, &count);
+ /* Allocate space for 2* as many components as split_string returned,
+ * +2 for the NULLS. */
+ pairs = ne_malloc((2*count+2) * sizeof(char *));
+ if (pairs == NULL) {
+ return NULL;
+ }
+ for (n = 0; n < count; n++) {
+ /* Find the split */
+ split = strchr(comps[n], kvsep);
+ if (split == NULL) {
+ /* No seperator found */
+ length = strlen(comps[n]);
+ } else {
+ length = split-comps[n];
+ }
+ /* Enter the key into the array */
+ pairs[2*n] = comps[n];
+ /* Null-terminate the key */
+ pairs[2*n][length] = '\0';
+ pairs[2*n+1] = split?(split + 1):NULL;
+ }
+ ne_free(comps);
+ pairs[2*count] = pairs[2*count+1] = NULL;
+ return pairs;
+}
+
+void split_string_free(char **components)
+{
+ char **pnt = components;
+ while (*pnt != NULL) {
+ ne_free(*pnt);
+ pnt++;
+ }
+ ne_free(components);
+}
+
+void pair_string_free(char **pairs)
+{
+ int n;
+ for (n = 0; pairs[n] != NULL; n+=2) {
+ ne_free(pairs[n]);
+ }
+ ne_free(pairs);
+}
+
+void ne_buffer_clear(ne_buffer *buf)
+{
+ memset(buf->data, 0, buf->length);
+ buf->used = 1;
+}
+
+/* Grows for given size, returns 0 on success, -1 on error. */
+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);
+ }
+}
+
+static size_t count_concat(va_list *ap)
+{
+ size_t total = 0;
+ char *next;
+
+ while ((next = va_arg(*ap, char *)) != NULL)
+ total += strlen(next);
+
+ return total;
+}
+
+static void do_concat(char *str, va_list *ap)
+{
+ char *next;
+
+ 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, ...)
+{
+ va_list ap;
+ ssize_t total;
+
+ va_start(ap, buf);
+ total = buf->used + count_concat(&ap);
+ va_end(ap);
+
+ /* Grow the buffer */
+ ne_buffer_grow(buf, total);
+
+ va_start(ap, buf);
+ do_concat(buf->data + buf->used - 1, &ap);
+ va_end(ap);
+
+ buf->used = total;
+ buf->data[total - 1] = '\0';
+}
+
+char *ne_concat(const char *str, ...)
+{
+ va_list ap;
+ size_t total, slen = strlen(str);
+ char *ret;
+
+ va_start(ap, str);
+ total = slen + count_concat(&ap);
+ va_end(ap);
+
+ ret = memcpy(ne_malloc(total + 1), str, slen);
+
+ va_start(ap, str);
+ do_concat(ret + slen, &ap);
+ va_end(ap);
+
+ ret[total] = '\0';
+ return ret;
+}
+
+/* Append zero-terminated string... returns 0 on success or -1 on
+ * realloc failure. */
+void ne_buffer_zappend(ne_buffer *buf, const char *str)
+{
+ ne_buffer_append(buf, str, strlen(str));
+}
+
+void ne_buffer_append(ne_buffer *buf, const char *data, size_t len)
+{
+ ne_buffer_grow(buf, buf->used + len);
+ memcpy(buf->data + buf->used - 1, data, len);
+ buf->used += len;
+ buf->data[buf->used - 1] = '\0';
+}
+
+ne_buffer *ne_buffer_create(void)
+{
+ return ne_buffer_ncreate(512);
+}
+
+ne_buffer *ne_buffer_ncreate(size_t 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;
+}
+
+void ne_buffer_destroy(ne_buffer *buf)
+{
+ ne_free(buf->data);
+ ne_free(buf);
+}
+
+char *ne_buffer_finish(ne_buffer *buf)
+{
+ char *ret = buf->data;
+ ne_free(buf);
+ return ret;
+}
+
+void ne_buffer_altered(ne_buffer *buf)
+{
+ buf->used = strlen(buf->data) + 1;
+}
+
+static const char *b64_alphabet =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/=";
+
+char *ne_base64(const unsigned char *text, size_t inlen)
+{
+ /* 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 */
+
+ 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++) = '=';
+ }
+
+ /* Null-terminate */
+ *point = '\0';
+
+ return buffer;
+}
+
+/* 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))))
+
+size_t ne_unbase64(const char *data, unsigned char **out)
+{
+ size_t inlen = strlen(data);
+ unsigned char *outp;
+ const unsigned char *in;
+
+ if (inlen == 0 || (inlen % 4) != 0) return 0;
+
+ 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;
+ }
+ }
+ }
+
+ 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;
+}
diff --git a/src/ne_string.h b/src/ne_string.h
new file mode 100644
index 0000000..81ede09
--- /dev/null
+++ b/src/ne_string.h
@@ -0,0 +1,137 @@
+/*
+ String utility functions
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_STRING_H
+#define NE_STRING_H
+
+#include "ne_defs.h"
+#include "ne_alloc.h"
+
+#include <stdarg.h>
+
+BEGIN_NEON_DECLS
+
+/* 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);
+
+/* 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);
+char **split_string_c(const char *str, const char seperator,
+ const char *quotes, const char *whitespace, int *count);
+char **pair_string(const char *str, const char compsep, const char kvsep,
+ const char *quotes, const char *whitespace);
+void split_string_free(char **components);
+void pair_string_free(char **pairs);
+/*** END OF OBSOLETE INTERFACES */
+
+/* String buffer handling. (Strings are zero-terminated still). A
+ * string buffer ne_buffer * which grows dynamically with the
+ * string. */
+
+typedef struct {
+ char *data; /* contents: null-terminated string. */
+ size_t used; /* used bytes in buffer */
+ size_t length; /* length of 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 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. */
+ne_buffer *ne_buffer_create(void);
+
+/* 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 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
+ * 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. */
+void ne_buffer_grow(ne_buffer *buf, size_t size);
+
+void ne_buffer_altered(ne_buffer *buf);
+
+/* Destroys a buffer, WITHOUT freeing the data, and returns the
+ * data. */
+char *ne_buffer_finish(ne_buffer *buf);
+
+/* 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
+
+#endif /* NE_STRING_H */
diff --git a/src/ne_stubssl.c b/src/ne_stubssl.c
new file mode 100644
index 0000000..ff71e1e
--- /dev/null
+++ b/src/ne_stubssl.c
@@ -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)
+{}
diff --git a/src/ne_uri.c b/src/ne_uri.c
new file mode 100644
index 0000000..51107d1
--- /dev/null
+++ b/src/ne_uri.c
@@ -0,0 +1,334 @@
+/*
+ HTTP URI handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <ctype.h>
+
+#include "ne_utils.h" /* for 'min' */
+#include "ne_string.h" /* for ne_buffer */
+#include "ne_alloc.h"
+#include "ne_uri.h"
+
+char *ne_path_parent(const char *uri)
+{
+ 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;
+ return ne_strndup(uri, pnt - uri + 1);
+}
+
+int ne_path_has_trailing_slash(const char *uri)
+{
+ size_t len = strlen(uri);
+ return ((len > 0) &&
+ (uri[len-1] == '/'));
+}
+
+unsigned int ne_uri_defaultport(const char *scheme)
+{
+ /* 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 ne_uri_parse(const char *uri, ne_uri *parsed)
+{
+ const char *pnt, *slash, *colon, *atsign, *openbk;
+
+ parsed->port = 0;
+ parsed->host = NULL;
+ parsed->path = NULL;
+ parsed->scheme = NULL;
+ parsed->authinfo = NULL;
+
+ if (uri[0] == '\0') {
+ return -1;
+ }
+
+ pnt = strstr(uri, "://");
+ if (pnt) {
+ parsed->scheme = ne_strndup(uri, pnt - uri);
+ pnt += 3; /* start of hostport segment */
+ } else {
+ pnt = uri;
+ }
+
+ 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;
+ }
+
+ 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) {
+ parsed->host = ne_strdup(pnt);
+ } else {
+ parsed->port = atoi(colon+1);
+ parsed->host = ne_strndup(pnt, colon - pnt);
+ }
+ } else {
+ if (colon == NULL || colon > slash) {
+ /* No port segment */
+ if (slash != uri) {
+ parsed->host = ne_strndup(pnt, slash - pnt);
+ } else {
+ /* No hostname segment. */
+ }
+ } else {
+ /* Port segment */
+ parsed->port = atoi(colon + 1);
+ parsed->host = ne_strndup(pnt, colon - pnt);
+ }
+ parsed->path = ne_strdup(slash);
+ }
+
+ return 0;
+}
+
+void ne_uri_free(ne_uri *u)
+{
+ 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);
+}
+
+char *ne_path_unescape(const char *uri)
+{
+ const char *pnt;
+ char *ret, *retpos, buf[5] = { "0x00\0" };
+ retpos = ret = ne_malloc(strlen(uri) + 1);
+ for (pnt = uri; *pnt != '\0'; pnt++) {
+ if (*pnt == '%') {
+ if (!isxdigit((unsigned char) pnt[1]) ||
+ !isxdigit((unsigned char) pnt[2])) {
+ /* Invalid URI */
+ ne_free(ret);
+ return NULL;
+ }
+ buf[2] = *++pnt; buf[3] = *++pnt; /* bit faster than memcpy */
+ *retpos++ = (char)strtol(buf, NULL, 16);
+ } else {
+ *retpos++ = *pnt;
+ }
+ }
+ *retpos = '\0';
+ return ret;
+}
+
+/* RFC2396 spake:
+ * "Data must be escaped if it does not have a representation
+ * using an unreserved character".
+ */
+
+/* Lookup table: character classes from 2396. (This is overkill) */
+
+#define SP 0 /* space = <US-ASCII coded character 20 hexadecimal> */
+#define CO 0 /* control = <US-ASCII coded characters 00-1F and 7F hexadecimal> */
+#define DE 0 /* delims = "<" | ">" | "#" | "%" | <"> */
+#define UW 0 /* unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" */
+#define MA 1 /* mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" */
+#define AN 2 /* alphanum = alpha | digit */
+#define RE 2 /* reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," */
+
+static const char uri_chars[128] = {
+/* +2 +4 +6 +8 +10 +12 +14 */
+/* 0 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO,
+/* 16 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO,
+/* 32 */ SP, MA, DE, DE, RE, DE, RE, MA, MA, MA, MA, RE, RE, MA, MA, RE,
+/* 48 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, RE, RE, DE, RE, DE, RE,
+/* 64 */ RE, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN,
+/* 80 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, UW, MA,
+/* 96 */ UW, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN,
+/* 112 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, MA, CO
+};
+
+#define ESCAPE(ch) (((const signed char)(ch) < 0 || \
+ uri_chars[(unsigned int)(ch)] == 0))
+
+#undef SP
+#undef CO
+#undef DE
+#undef UW
+#undef MA
+#undef AN
+#undef RE
+
+char *ne_path_escape(const char *abs_path)
+{
+ const char *pnt;
+ char *ret, *retpos;
+ int count = 0;
+ for (pnt = abs_path; *pnt != '\0'; pnt++) {
+ if (ESCAPE(*pnt)) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ return ne_strdup(abs_path);
+ }
+ /* An escaped character is "%xx", i.e., two MORE
+ * characters than the original string */
+ retpos = ret = ne_malloc(strlen(abs_path) + 2*count + 1);
+ for (pnt = abs_path; *pnt != '\0'; pnt++) {
+ if (ESCAPE(*pnt)) {
+ /* Escape it - %<hex><hex> */
+ sprintf(retpos, "%%%02x", (unsigned char) *pnt);
+ retpos += 3;
+ } else {
+ /* It's cool */
+ *retpos++ = *pnt;
+ }
+ }
+ *retpos = '\0';
+ return ret;
+}
+
+#undef ESCAPE
+
+#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 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 = 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))) {
+ /* Compare them, ignoring the trailing slash on the longer
+ * URI */
+ if (strncasecmp(a, b, min(lena, lenb)) == 0)
+ ret = 0;
+ }
+ }
+ 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 ne_path_childof(const char *parent, const char *child)
+{
+ char *root = ne_strdup(child);
+ int ret;
+ if (strlen(parent) >= strlen(child)) {
+ ret = 0;
+ } else {
+ /* root is the first of child, equal to length of parent */
+ root[strlen(parent)] = '\0';
+ ret = (ne_path_compare(parent, root) == 0);
+ }
+ ne_free(root);
+ return ret;
+}
diff --git a/src/ne_uri.h b/src/ne_uri.h
new file mode 100644
index 0000000..75cd58a
--- /dev/null
+++ b/src/ne_uri.h
@@ -0,0 +1,85 @@
+/*
+ HTTP URI handling
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_URI_H
+#define NE_URI_H
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Un-escapes a path. Returns malloc-allocated path on success, or
+ * NULL on an invalid %<HEX><HEX> sequence. */
+char *ne_path_unescape(const char *uri);
+
+/* Escapes the a path segment: returns malloc-allocated string on
+ * success, or NULL on malloc failure. */
+char *ne_path_escape(const char *abs_path);
+
+/* Returns malloc-allocated parent of path, or NULL if path has no
+ * parent (such as "/"). */
+char *ne_path_parent(const char *path);
+
+/* Returns strcmp-like value giving comparison between p1 and p2,
+ * ignoring trailing-slashes. */
+int ne_path_compare(const char *p1, const char *p2);
+
+/* Returns non-zero if child is a child of parent */
+int ne_path_childof(const char *parent, const char *child);
+
+/* Returns non-zero if path has a trailing slash character */
+int ne_path_has_trailing_slash(const char *path);
+
+/* Return the default port for the given scheme, or 0 if none is
+ * known. */
+unsigned int ne_uri_defaultport(const char *scheme);
+
+typedef struct {
+ char *scheme;
+ char *host;
+ unsigned int port;
+ char *path;
+ char *authinfo;
+} ne_uri;
+
+/* Parse absoluteURI 'uri' and place parsed segments in *parsed.
+ * Returns zero on success, non-zero on parse error. Fields of *parsed
+ * are malloc'ed, structure should be free'd with uri_free on
+ * successful return. Any unspecified URI fields are set to NULL or 0
+ * appropriately in *parsed. */
+int ne_uri_parse(const char *uri, ne_uri *parsed);
+
+/* Turns a URI structure back into a string. String is
+ * malloc-allocated, and must be free'd by the caller. */
+char *ne_uri_unparse(const ne_uri *uri);
+
+/* Compares URIs u1 and u2, returns non-zero if they are found to be
+ * non-equal. The sign of the return value is <0 if 'u1' is less than
+ * 'u2', or >0 if 'u2' is greater than 'u1'. */
+int ne_uri_cmp(const ne_uri *u1, const ne_uri *u2);
+
+/* Free URI object. */
+void ne_uri_free(ne_uri *parsed);
+
+END_NEON_DECLS
+
+#endif /* NE_URI_H */
+
diff --git a/src/ne_utils.c b/src/ne_utils.c
new file mode 100644
index 0000000..84f7470
--- /dev/null
+++ b/src/ne_utils.c
@@ -0,0 +1,185 @@
+/*
+ HTTP utility functions
+ 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
+ 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 <ctype.h> /* isdigit() for ne_parse_statusline */
+
+#include "ne_utils.h"
+#include "ne_string.h" /* for ne_strdup */
+#include "ne_dates.h"
+
+#ifdef NEON_SSL
+#include <openssl/opensslv.h>
+#endif
+
+/* libxml2: pick up the version string. */
+#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
+
+int ne_debug_mask = 0;
+FILE *ne_debug_stream = NULL;
+
+void ne_debug_init(FILE *stream, int mask)
+{
+ ne_debug_stream = stream;
+ ne_debug_mask = mask;
+#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) == 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)
+ 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"
+#else
+ "Bundled build"
+#endif
+#ifdef HAVE_EXPAT
+ ", Expat"
+/* 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 NEON_SSL
+#ifdef OPENSSL_VERSION_TEXT
+ ", " OPENSSL_VERSION_TEXT
+#else
+ "OpenSSL (unknown version)"
+#endif /* OPENSSL_VERSION_TEXT */
+#endif
+ "."
+;
+
+const char *ne_version_string(void)
+{
+ return version_string;
+}
+
+int ne_version_match(int major, int 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)
+{
+ const char *part;
+ int major, minor, status_code, klass;
+
+ /* Check they're speaking the right language */
+ status_line = strstr(status_line, "HTTP/");
+ if (status_line == NULL) {
+ return -1;
+ }
+
+ /* And find out which dialect of this peculiar language
+ * they can talk... */
+ major = 0;
+ minor = 0;
+ /* Note, we're good children, and accept leading zero's on the
+ * version numbers */
+ for (part = status_line + 5; *part != '\0' && isdigit(*part); part++) {
+ major = major*10 + (*part-'0');
+ }
+ if (*part != '.') {
+ return -1;
+ }
+ for (part++ ; *part != '\0' && isdigit(*part); part++) {
+ minor = minor*10 + (*part-'0');
+ }
+ if (*part != ' ') {
+ return -1;
+ }
+ /* Skip any spaces */
+ for (; *part == ' ' ; part++) /* noop */;
+ /* Now for the Status-Code. part now points at the first Y in
+ * "HTTP/x.x YYY". We want value of YYY... could use atoi, but
+ * probably quicker this way. */
+ if (!isdigit(part[0]) || !isdigit(part[1]) || !isdigit(part[2]) ||
+ (part[3] != '\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 */;
+
+ /* 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 = ne_strclean(ne_strdup(part));
+ st->code = status_code;
+ st->klass = klass;
+ return 0;
+}
diff --git a/src/ne_utils.h b/src/ne_utils.h
new file mode 100644
index 0000000..28b17c5
--- /dev/null
+++ b/src/ne_utils.h
@@ -0,0 +1,127 @@
+/*
+ HTTP utility functions
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_UTILS_H
+#define NE_UTILS_H
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ne_defs.h"
+
+#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:
+ * "neon 0.2.0: Library build, OpenSSL support"
+ */
+const char *ne_version_string(void);
+
+/* 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);
+
+/* 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
+
+/* CONSIDER: mutt has a nicer way of way of doing debugging output... maybe
+ * switch to like that. */
+
+#ifndef NE_DEBUGGING
+#define NE_DEBUG if (0) ne_debug
+#else /* DEBUGGING */
+#define NE_DEBUG ne_debug
+#endif /* DEBUGGING */
+
+#define NE_DBG_SOCKET (1<<0)
+#define NE_DBG_HTTP (1<<1)
+#define NE_DBG_XML (1<<2)
+#define NE_DBG_HTTPAUTH (1<<3)
+#define NE_DBG_HTTPPLAIN (1<<4)
+#define NE_DBG_LOCKS (1<<5)
+#define NE_DBG_XMLPARSE (1<<6)
+#define NE_DBG_HTTPBODY (1<<7)
+#define NE_DBG_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;
+
+/* Produce debug output if any of channels 'ch' is enabled for
+ * debugging. */
+void ne_debug(int ch, const char *, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif /* __GNUC__ */
+;
+
+/* Storing an HTTP status result */
+typedef struct {
+ int major_version;
+ int minor_version;
+ int code; /* Status-Code value */
+ int klass; /* Class of Status-Code (1-5) */
+ 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. 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.
+ */
+int ne_parse_statusline(const char *status_line, ne_status *s);
+
+END_NEON_DECLS
+
+#endif /* NE_UTILS_H */
diff --git a/src/ne_xml.c b/src/ne_xml.c
new file mode 100644
index 0000000..d55d59c
--- /dev/null
+++ b/src/ne_xml.c
@@ -0,0 +1,605 @@
+/*
+ Higher Level Interface to XML Parsers.
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include "ne_i18n.h"
+
+#include "ne_alloc.h"
+#include "ne_xml.h"
+#include "ne_utils.h"
+#include "ne_string.h"
+
+#if defined(HAVE_EXPAT)
+/* expat support: */
+#ifdef HAVE_XMLPARSE_H
+#include "xmlparse.h"
+#else
+#include <expat.h>
+#endif
+typedef XML_Char ne_xml_char;
+#elif defined(HAVE_LIBXML)
+/* libxml2 support: */
+#include <libxml/xmlversion.h>
+#include <libxml/parser.h>
+typedef xmlChar ne_xml_char;
+
+#else /* not HAVE_LIBXML */
+# error need an XML parser
+#endif /* not HAVE_EXPAT */
+
+/* Approx. one screen of text: */
+#define ERR_SIZE (2048)
+
+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 element {
+ const ne_xml_char *nspace;
+ ne_xml_char *name;
+
+ int state; /* opaque state integer */
+
+ /* Namespaces declared in this element */
+ ne_xml_char *default_ns; /* A default namespace */
+ struct namespace *nspaces; /* List of other namespace scopes */
+
+ struct handler *handler; /* Handler for this element */
+
+ struct element *parent; /* parent element, or NULL */
+};
+
+/* 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 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];
+};
+
+/* 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);
+static const char *resolve_nspace(const struct element *elm,
+ const char *prefix, size_t pfxlen);
+
+/* Linked list of namespace scopes */
+struct namespace {
+ ne_xml_char *name;
+ ne_xml_char *uri;
+ struct namespace *next;
+};
+
+#ifdef HAVE_LIBXML
+
+/* Could be const as far as we care, but libxml doesn't want that */
+static xmlSAXHandler sax_handler = {
+ NULL, /* internalSubset */
+ NULL, /* isStandalone */
+ NULL, /* hasInternalSubset */
+ NULL, /* hasExternalSubset */
+ NULL, /* resolveEntity */
+ NULL, /* getEntity */
+ NULL, /* entityDecl */
+ NULL, /* notationDecl */
+ NULL, /* attributeDecl */
+ NULL, /* elementDecl */
+ NULL, /* unparsedEntityDecl */
+ NULL, /* setDocumentLocator */
+ NULL, /* startDocument */
+ NULL, /* endDocument */
+ start_element, /* startElement */
+ end_element, /* endElement */
+ NULL, /* reference */
+ char_data, /* characters */
+ NULL, /* ignorableWhitespace */
+ NULL, /* processingInstruction */
+ NULL, /* comment */
+ NULL, /* xmlParserWarning */
+ sax_error, /* xmlParserError */
+ sax_error, /* fatal error (never called by libxml2?) */
+ NULL, /* getParameterEntity */
+ char_data /* cdataBlock */
+};
+
+/* 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)
+
+#else
+
+#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)
+{
+ ne_xml_parser *p = userdata;
+ if (encoding) p->encoding = ne_strdup(encoding);
+}
+
+#endif /* HAVE_LIBXML */
+
+int ne_xml_currentline(ne_xml_parser *p)
+{
+#ifdef HAVE_EXPAT
+ return XML_GetCurrentLineNumber(p->parser);
+#else
+ return p->parser->input->line;
+#endif
+}
+
+const char *ne_xml_doc_encoding(const ne_xml_parser *p)
+{
+#ifdef HAVE_LIBXML
+ return p->parser->encoding;
+#else
+ return p->encoding;
+#endif
+}
+
+/* Extract the namespace prefix declarations from 'atts'. */
+static int declare_nspaces(ne_xml_parser *p, struct element *elm,
+ const ne_xml_char **atts)
+{
+ 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;
+}
+
+/* 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;
+ }
+ }
+ return 0;
+}
+
+/* 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;
+
+ if (!p->valid) return;
+
+ if (p->prune) {
+ p->prune++;
+ return;
+ }
+
+ /* Create a new element */
+ elm = ne_calloc(sizeof *elm);
+ elm->parent = p->current;
+ p->current = elm;
+
+ if (declare_nspaces(p, elm, atts) || expand_qname(p, elm, name)) {
+ p->valid = 0;
+ return;
+ }
+
+ /* 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 an element structure. */
+static void destroy_element(struct element *elm)
+{
+ struct namespace *this_ns, *next_ns;
+ ne_free(elm->name);
+ /* Free the namespaces */
+ this_ns = elm->nspaces;
+ while (this_ns != NULL) {
+ next_ns = this_ns->next;
+ ne_free(this_ns->name);
+ ne_free(this_ns->uri);
+ ne_free(this_ns);
+ this_ns = next_ns;
+ };
+ 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;
+ 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 element *elm = p->current;
+
+ if (!p->valid) return;
+
+ 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;
+ }
+
+ 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);
+}
+
+/* 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)
+{
+ 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;
+ }
+ }
+
+ return NULL;
+}
+
+ne_xml_parser *ne_xml_create(void)
+{
+ ne_xml_parser *p = ne_calloc(sizeof *p);
+ /* Initialize other stuff */
+ p->valid = 1;
+ /* Placeholder for the root element */
+ p->current = p->root = ne_calloc(sizeof *p->root);
+ p->root->default_ns = "";
+ p->root->state = 0;
+#ifdef HAVE_EXPAT
+ p->parser = XML_ParserCreate(NULL);
+ if (p->parser == NULL) {
+ abort();
+ }
+ XML_SetElementHandler(p->parser, start_element, end_element);
+ XML_SetCharacterDataHandler(p->parser, char_data);
+ XML_SetUserData(p->parser, (void *) p);
+ 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;
+}
+
+void ne_xml_push_handler(ne_xml_parser *p,
+ ne_xml_startelm_cb *startelm_cb,
+ ne_xml_cdata_cb *cdata_cb,
+ ne_xml_endelm_cb *endelm_cb,
+ void *userdata)
+{
+ struct handler *hand = ne_calloc(sizeof(struct handler));
+
+ hand->startelm_cb = startelm_cb;
+ hand->cdata_cb = cdata_cb;
+ hand->endelm_cb = endelm_cb;
+ hand->userdata = userdata;
+
+ /* 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)
+{
+ ne_xml_parser *p = userdata;
+ /* FIXME: The two XML parsers break all our nice abstraction by
+ * choosing different char *'s. The swine. This cast will come
+ * back and bite us someday, no doubt. */
+ ne_xml_parse(p, block, len);
+}
+
+/* Parse the given block of input of length len */
+void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len)
+{
+ int ret, flag;
+ /* duck out if it's broken */
+ if (!p->valid) {
+ NE_DEBUG(NE_DBG_XML, "Not parsing %" NE_FMT_SIZE_T " bytes.\n",
+ len);
+ return;
+ }
+ if (len == 0) {
+ flag = -1;
+ block = "";
+ NE_DEBUG(NE_DBG_XML, "Got 0-length buffer, end of document.\n");
+ } else {
+ NE_DEBUG(NE_DBG_XML, "Parsing %" NE_FMT_SIZE_T " length buffer.\n",
+ len);
+ flag = 0;
+ }
+ /* Note, don't write a parser error if !p->valid, since an error
+ * will already have been written in that case. */
+#ifdef HAVE_EXPAT
+ ret = XML_Parse(p->parser, block, len, flag);
+ NE_DEBUG(NE_DBG_XMLPARSE, "XML_Parse returned %d\n", ret);
+ if (ret == 0 && p->valid) {
+ 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) {
+ ne_snprintf(p->error, ERR_SIZE, "XML parse error at line %d.",
+ ne_xml_currentline(p));
+ p->valid = 0;
+ }
+#endif
+}
+
+int ne_xml_valid(ne_xml_parser *p)
+{
+ return p->valid;
+}
+
+void ne_xml_destroy(ne_xml_parser *p)
+{
+ struct 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;
+ ne_free(hand);
+ }
+
+ /* 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
+
+ ne_free(p);
+}
+
+void ne_xml_set_error(ne_xml_parser *p, const char *msg)
+{
+ ne_snprintf(p->error, ERR_SIZE, msg);
+}
+
+#ifdef HAVE_LIBXML
+static void sax_error(void *ctx, const char *msg, ...)
+{
+ ne_xml_parser *p = ctx;
+ va_list ap;
+ char buf[1024];
+
+ va_start(ap, msg);
+ ne_vsnprintf(buf, 1024, msg, ap);
+ va_end(ap);
+
+ ne_snprintf(p->error, ERR_SIZE,
+ _("XML parse error at line %d: %s."),
+ p->parser->input->line, buf);
+
+ p->valid = 0;
+}
+#endif
+
+const char *ne_xml_get_error(ne_xml_parser *p)
+{
+ return p->error;
+}
+
+const char *
+ne_xml_get_attr(ne_xml_parser *p, const char **attrs,
+ const char *nspace, const char *name)
+{
+ int n;
+
+ for (n = 0; attrs[n] != NULL; n += 2) {
+ 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;
+}
diff --git a/src/ne_xml.h b/src/ne_xml.h
new file mode 100644
index 0000000..f767652
--- /dev/null
+++ b/src/ne_xml.h
@@ -0,0 +1,140 @@
+/*
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_XML_H
+#define NE_XML_H
+
+#include <sys/types.h> /* for size_t */
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* 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 each new start-element, 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)
+
+/* The startelm 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. */
+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);
+
+typedef struct ne_xml_parser_s ne_xml_parser;
+
+/* Create an XML parser. */
+ne_xml_parser *ne_xml_create(void);
+
+/* 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,
+ 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 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);
+
+/* 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 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 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 */
+void ne_xml_set_error(ne_xml_parser *p, const char *msg);
+
+const char *ne_xml_get_error(ne_xml_parser *p);
+
+/* 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 been
+ * parsed. */
+const char *ne_xml_doc_encoding(const ne_xml_parser *p);
+
+/* A utility interface for mapping {nspace, name} onto an int. */
+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
+
+#endif /* NE_XML_H */
diff --git a/test/.cvsignore b/test/.cvsignore
new file mode 100644
index 0000000..fb288d0
--- /dev/null
+++ b/test/.cvsignore
@@ -0,0 +1,41 @@
+tests
+*-tests
+Makefile
+request
+*.log*
+server
+regress
+compress
+*.gz
+*.tmp
+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
+*.bb
+*.da
+*.bbg
+ca*.pem
+chain.pem
+*.p12
+client.*
+output.pem
diff --git a/test/COPYING b/test/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/test/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/test/ChangeLog b/test/ChangeLog
new file mode 100644
index 0000000..a32791c
--- /dev/null
+++ b/test/ChangeLog
@@ -0,0 +1,1329 @@
+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.
+
+Sun Apr 29 13:43:59 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in (clean): New target. (libtest.a): Depend on libneon
+ to force rebuilds when necessary. (all): Build but don't test.
+
+Sun Apr 29 13:41:13 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c: Add status line with leading garbage.
+
+Sun Apr 29 13:39:53 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (status_lines): Add some tests for invalid status
+ lines too.
+
+Sun Apr 29 13:38:31 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (main): Use basename(argv[0]) as suite name. Fail if no
+ tests are in the functions vector.
+
+Sun Apr 29 11:06:45 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (segv): New function. (main): Add SIGSEGV handler.
+
+Fri Apr 27 00:00:12 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (base64): New test.
+
+Thu Apr 26 22:39:44 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * uri-tests.c (just_hostname, just_path, null_uri): New tests.
+
+Thu Apr 26 22:03:58 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (md5): Test of MD5 functions.
+
+Mon Apr 23 23:08:02 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * http-tests.c (simple_head): Add HEAD test.
+
+Mon Apr 23 22:49:52 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * http-tests.c (simple_get): Check for EOF after reading response
+ body of HTTP/1.0 GET request.
+
+ (null_resource): New function, test for 404 on null resource.
+
+
diff --git a/test/Makefile.in b/test/Makefile.in
new file mode 100644
index 0000000..eb0da59
--- /dev/null
+++ b/test/Makefile.in
@@ -0,0 +1,171 @@
+# Makefile for neon test suite.
+
+SHELL = @SHELL@
+CPPFLAGS = @CPPFLAGS@ -I. -I$(top_srcdir)/src -I$(top_srcdir)/test/common
+CFLAGS = @CFLAGS@ @NEON_CFLAGS@
+LDFLAGS = -L. @LDFLAGS@ -L$(top_srcdir)/src -L../src/.libs
+DEFS = @DEFS@
+
+top_builddir = ..
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+AR = ar
+
+RANLIB = @RANLIB@
+LIBS = -ltest -lneon @NEON_LIBS@
+CC = @CC@
+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
+DAV_TESTS = xml acl props lock
+SSL_TESTS = socket-ssl ssl
+SSL_HELPERS = ca-stamp
+TESTS = @TESTS@
+HDRS = common/tests.h common/child.h utils.h $(top_builddir)/config.h
+VALGRIND = valgrind --gdb-attach=yes --leak-check=yes
+
+LIBTEST = libtest.a
+
+# By default, compile but don't run the tests.
+all: $(TESTS)
+
+clean:
+ rm -f $(TESTS) $(HELPERS) *.o common/*.o libtest.a *.log
+ rm -rf ca
+ rm -f ca-stamp client.key *.csr ssigned.pem wrongcn.pem \
+ server.cert client.cert client.p12
+
+check: $(TESTS) $(HELPERS)
+ @SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh $(TESTS)
+
+grind: $(TESTS) $(HELPERS)
+ @SRCDIR=$(srcdir) HARNESS="$(VALGRIND)" $(SHELL) $(srcdir)/run.sh $(TESTS)
+
+file1.gz: ../NEWS
+ gzip -c --no-name $(top_srcdir)/NEWS > $@
+
+file2.gz: ../NEWS
+ gzip -c --name $(top_srcdir)/NEWS > $@
+
+# gzip file with trailing bytes.
+trailing.gz: ../NEWS
+ gzip -c --no-name $(top_srcdir)/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!' >> $@
+
+# 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.o common/child.o utils.o
+
+$(LIBTEST): $(LIBOBJS) $(HDRS) ../src/@NEON_TARGET@
+ $(AR) cru $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+.c.o:
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+
+uri-tests: uri-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) $(CFLAGS) -o $@ uri-tests.o $(LIBS)
+
+request: request.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ request.o $(LIBS)
+
+string-tests: string-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ string-tests.o $(LIBS)
+
+socket: socket.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ socket.o $(LIBS)
+
+compress: compress.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ compress.o $(LIBS)
+
+acl: acl.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ acl.o $(LIBS)
+
+utils: utils.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ utils.o $(LIBS)
+
+util-tests: util-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ util-tests.o $(LIBS)
+
+auth: auth.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ auth.o $(LIBS)
+
+lock: lock.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ lock.o $(LIBS)
+
+basic: basic.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ basic.o $(LIBS)
+
+# Recompile socket.c with SOCKET_SSL defined
+socket-ssl.o: $(srcdir)/socket.c $(HDRS)
+ $(CC) -DSOCKET_SSL $(CPPFLAGS) $(CFLAGS) -c $(srcdir)/socket.c -o $@
+
+socket-ssl: socket-ssl.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ socket-ssl.o $(LIBS)
+
+ssl: ssl.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ ssl.o $(LIBS)
+
+xml: xml.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ xml.o $(LIBS)
+
+stubs: stubs.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ stubs.o $(LIBS)
+
+redirect: redirect.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ redirect.o $(LIBS)
+
+session: session.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ session.o $(LIBS)
+
+cookies: cookies.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ cookies.o $(LIBS)
+
+props: props.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ props.o $(LIBS)
+
+resolve: resolve.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ resolve.o $(LIBS)
+
+#FOO: FOO.o $(LIBTEST)
+# $(CC) $(LDFLAGS) -o $@ FOO.o $(LIBS)
+
+auth.o: auth.c $(HDRS)
+uri-tests.o: uri-tests.c $(HDRS)
+util-tests.o: util-tests.c $(HDRS)
+string-tests.o: string-tests.c $(HDRS)
+socket.o: socket.c $(HDRS)
+server.o: server.c $(HDRS)
+request.o: request.c $(HDRS)
+regress.o: regress.c $(HDRS)
+compress.o: compress.c $(HDRS)
+acl.o: acl.c $(HDRS)
+utils.o: utils.c $(HDRS)
+stubs.o: stubs.c $(HDRS)
+props.o: props.c $(HDRS)
+session.o: session.c $(HDRS)
+redirect.o: redirect.c $(HDRS)
+basic.o: basic.c $(HDRS)
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..d8f6528
--- /dev/null
+++ b/test/README
@@ -0,0 +1,39 @@
+
+Stupidly Simple Test Suite for neon
+-----------------------------------
+
+The aim of the test suite is two-fold:
+
+ 1. ensure compliance to the relevant RFCs in network behaviour.
+
+ 2. ensure that the promises made by the public API are met
+ by the current implementation.
+
+The file `STATUS' makes an attempt at listing RFC requirements and how
+the test suite tests whether neon meets them or not (it's not finished
+yet).
+
+The test suite is licensed under the GPL.
+
+Important Note About Test Failures
+----------------------------------
+
+Note that a test failure either means a bug in the test or a bug in
+the code itself. On platforms without pipe(), there is a race
+condition in the code which forks a server process: if you get random
+failures on a slow or loaded box, increase the sleep time in
+common/child.c:minisleep().
+
+Extra Stuff
+-----------
+
+server-tests requires that you have a running HTTP server on localhost
+port 80, and you have copied htdocs/* to server-htdocs-root/test/*
+
+Credits
+-------
+
+This test suite is inspired by the Subversion project, discussion on
+the subversion mailing list, and seeing chromatic's talks on XP. The
+presentation is inspired by the standard Perl test suite. Imitation
+is the greatest form of flattery, right?
diff --git a/test/STATUS b/test/STATUS
new file mode 100644
index 0000000..a5483ef
--- /dev/null
+++ b/test/STATUS
@@ -0,0 +1,74 @@
+ -*- text -*-
+
+This document attempts to list RFC requirements and determine whether
+neon meets them, or where they do not apply, etc.
+
+ Yes: test written, succeeds
+ No: test written, but currently fails
+ ???: no test written
+ ---: feature not supported
+ App: this is an application issue not a neon issue
+
+ RFC2616
+ =======
+
+3.1: MUST treat major/minor as separate digits Yes
+3.1: MUST ignore leading zeros Yes
+3.1: MUST only send HTTP/1.1 when appropriate ???
+
+3.2.2: MUST use abs_path of "/" in Request-URI App
+3.2.3: comparisons of host names MUST be case-insensitive Yes
+ comparisons of scheme names MUST be ... Yes
+ comparison of empty abs_path equivalent to "/" No/---
+
+3.3.1: MUST accept three date formats App/Yes [2]
+ MUST only generate RFC1123-style dates App
+
+3.3.1: MUST use GMT for http-dates ???
+ MUST assume GMT when parsing asctime dates ???
+
+3.4.1: MUST respect charset label provided Yes/App
+
+3.5*: content codings App
+
+3.6: MUST requirements for multiple transfer-codings --- [4]
+
+3.6.1: parsing of chunked transfer coding Yes
+ MUST be able to handle "chunked" transfer-coding Yes
+ MUST ignore unknown chunk-extension extensions Yes
+
+3.7: parsing of Content-Type headers Yes
+
+3.7: MUST NOT have LWS between type/subtype in C-T hdr App
+ SHOULD only send parameters to "new HTTP apps" (>1.0?) App
+
+3.7.1: MUST represent HTTP message in canonical form App
+ MUST accept CRLF/CR/LF as line-breaks in text/* media App
+ MUST NOT use only CR or LF in HTTP control structures ???
+ MUST specify charset if not ISO-8859-1 App
+
+3.7.2: multipart types ---
+
+3.8: SHOULD have short product token Yes/App [5]
+ SHOULD use product-version for version identifier Yes/App
+ only product-version differs between versions Yes/App
+
+3.9: Content Negotiation ---/App
+
+3.10: Language Tags ---/App
+
+3.11: Entity Tags ---/App
+
+
+
+
+
+[2]: date parser is provided which handles all three formats, but no
+handling of the Date header is present within neon.
+
+[3]: not sure if neon should be handling of this internally.
+
+[4]: neon only supports using just chunked Transfer-Coding or none.
+
+[5]: these reflect that applications may add their own product tokens
+ alongside neon's.
diff --git a/test/acl.c b/test/acl.c
new file mode 100644
index 0000000..14008b1
--- /dev/null
+++ b/test/acl.c
@@ -0,0 +1,115 @@
+/*
+ Dummy ACL tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "ne_acl.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+#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;
+
+ 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 = {0};
+
+ e.apply = ne_acl_all;
+ e.type = ne_acl_grant;
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+static int deny_all(void)
+{
+ ne_acl_entry e = {0};
+
+ e.apply = ne_acl_all;
+ e.type = ne_acl_deny;
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+static int deny_one(void)
+{
+ ne_acl_entry e = {0};
+
+ e.apply = ne_acl_href;
+ e.type = ne_acl_deny;
+ e.principal = "http://webdav.org/users/joe";
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+static int deny_byprop(void)
+{
+ ne_acl_entry e = {0};
+
+ e.apply = ne_acl_property;
+ e.type = ne_acl_deny;
+ e.principal = "owner";
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+ne_test tests[] = {
+ T(grant_all),
+ T(deny_all),
+ T(deny_one),
+ T(deny_byprop),
+ T(NULL)
+};
+
+#endif /* NEON_NODAV */
diff --git a/test/auth.c b/test/auth.c
new file mode 100644
index 0000000..30fe88e
--- /dev/null
+++ b/test/auth.c
@@ -0,0 +1,315 @@
+/*
+ Authentication tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_auth.h"
+#include "ne_basic.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static const char username[] = "Aladdin", password[] = "open sesame";
+static int auth_failed;
+
+static const char www_wally[] = "WWW-Authenticate: Basic realm=WallyWorld";
+
+static int auth_cb(void *userdata, const char *realm, int tries,
+ char *un, char *pw)
+{
+ strcpy(un, username);
+ strcpy(pw, password);
+ return tries;
+}
+
+static void auth_hdr(char *value)
+{
+#define B "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
+ 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
+}
+
+/* Sends a response with given response-code. If hdr is not NULL,
+ * sends that header string too (appending an EOL). If eoc is
+ * non-zero, request must be last sent down a connection; otherwise,
+ * clength 0 is sent to maintain a persistent connection. */
+static int send_response(ne_socket *sock, const char *hdr, int code, int eoc)
+{
+ char buffer[BUFSIZ];
+
+ sprintf(buffer, "HTTP/1.1 %d Blah Blah" EOL, code);
+
+ if (hdr) {
+ strcat(buffer, hdr);
+ strcat(buffer, EOL);
+ }
+
+ if (eoc) {
+ strcat(buffer, "Connection: close" EOL EOL);
+ } else {
+ strcat(buffer, "Content-Length: 0" EOL EOL);
+ }
+
+ return SEND_STRING(sock, buffer);
+}
+
+/* Server function which sends two responses: first requires auth,
+ * second doesn't. */
+static int auth_serve(ne_socket *sock, void *userdata)
+{
+ auth_failed = 1;
+
+ /* Register globals for discard_request. */
+ got_header = auth_hdr;
+ want_header = "Authorization";
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, auth_failed?500:200, 1);
+
+ return 0;
+}
+
+static int basic(void)
+{
+ ne_session *sess;
+
+ CALL(make_session(&sess, auth_serve, NULL));
+ ne_set_server_auth(sess, auth_cb, NULL);
+
+ CALL(any_2xx_request(sess, "/norman"));
+
+ ne_session_destroy(sess);
+ CALL(await_server());
+ return OK;
+}
+
+static int retry_serve(ne_socket *sock, void *ud)
+{
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ 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;
+}
+
+static int retry_cb(void *userdata, const char *realm, int tries,
+ char *un, char *pw)
+{
+ int *count = userdata;
+
+ /* dummy creds; server ignores them anyway. */
+ strcpy(un, "a");
+ strcpy(pw, "b");
+
+ switch (*count) {
+ case 0:
+ case 1:
+ if (tries == *count) {
+ *count += 1;
+ return 0;
+ } else {
+ t_context("On request #%d, got attempt #%d", *count, tries);
+ *count = -1;
+ return 1;
+ }
+ break;
+ case 2:
+ case 3:
+ /* server fails a subsequent request, check that tries has
+ * reset to zero. */
+ if (tries == 0) {
+ *count += 1;
+ return 0;
+ } else {
+ t_context("On retry after failure #%d, tries was %d",
+ *count, tries);
+ *count = -1;
+ return 1;
+ }
+ break;
+ 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;
+ }
+ return 1;
+}
+
+/* Test that auth retries are working correctly. */
+static int retries(void)
+{
+ ne_session *sess;
+ int count = 0;
+
+ CALL(make_session(&sess, 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"));
+
+ /* auth_cb will have set up context. */
+ CALL(count != 2);
+
+ /* this request will be 401'ed once, then succeed. */
+ ONREQ(any_request(sess, "/foo"));
+
+ /* auth_cb will have set up context. */
+ CALL(count != 3);
+
+ /* some 20x requests. */
+ ONREQ(any_request(sess, "/foo"));
+ ONREQ(any_request(sess, "/foo"));
+
+ /* this request will be 401'ed once, then succeed. */
+ ONREQ(any_request(sess, "/foo"));
+
+ /* auth_cb will have set up context. */
+ CALL(count != 4);
+
+ /* 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());
+
+ 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. */
+
+/* negotiation: within a single header, multiple headers.
+ * check digest has precedence */
+
+/* test auth-int, auth-int FAILURE. chunk trailers/non-trailer */
+
+/* test logout */
+
+/* proxy auth, proxy AND origin */
+
+ne_test tests[] = {
+ T(lookup_localhost),
+ T(basic),
+ T(retries),
+ T(forget_regress),
+ T(tunnel_regress),
+ T(NULL)
+};
diff --git a/test/basic.c b/test/basic.c
new file mode 100644
index 0000000..15f5c8a
--- /dev/null
+++ b/test/basic.c
@@ -0,0 +1,228 @@
+/*
+ Tests for high-level HTTP interface (ne_basic.h)
+ 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
+ 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 <fcntl.h>
+
+#include "ne_basic.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int content_type(void)
+{
+ int n;
+ static const struct {
+ const char *value, *type, *subtype, *charset;
+ } ctypes[] = {
+ { "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
+ * that some servers do it anyway. */
+ { "text/xml; charset=utf-8", TXU },
+ { "text/xml; charset=utf-8; foo=bar", TXU },
+ { "text/xml;charset=utf-8", TXU },
+ { "text/xml ;charset=utf-8", TXU },
+ { "text/xml;charset=utf-8;foo=bar", TXU },
+ { "text/xml; foo=bar; charset=utf-8", TXU },
+ { "text/xml; foo=bar; charset=utf-8; bar=foo", TXU },
+ { "text/xml; charset=\"utf-8\"", TXU },
+ { "text/xml; charset='utf-8'", TXU },
+ { "text/xml; foo=bar; charset=\"utf-8\"; bar=foo", TXU },
+#undef TXU
+ /* badly quoted charset should come out as NULL */
+ { "foo/lemon; charset=\"utf-8", "foo", "lemon", NULL },
+ { NULL }
+ };
+
+ for (n = 0; ctypes[n].value != NULL; n++) {
+ ne_content_type ct = {0};
+
+ ne_content_type_handler(&ct, ctypes[n].value);
+
+ ONV(strcmp(ct.type, ctypes[n].type),
+ ("for `%s': type was `%s'", ctypes[n].value, ct.type));
+
+ ONV(strcmp(ct.subtype, ctypes[n].subtype),
+ ("for `%s': subtype was `%s'", ctypes[n].value, ct.subtype));
+
+ ONV(ctypes[n].charset && ct.charset == NULL,
+ ("for `%s': charset unset", ctypes[n].value));
+
+ ONV(ctypes[n].charset == NULL && ct.charset != NULL,
+ ("for `%s': unexpected charset `%s'", ctypes[n].value,
+ ct.charset));
+
+ ONV(ctypes[n].charset && ct.charset &&
+ strcmp(ctypes[n].charset, ct.charset),
+ ("for `%s': charset was `%s'", ctypes[n].value, ct.charset));
+
+ NE_FREE(ct.value);
+ }
+
+ return OK;
+}
+
+/* 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
+ ONN(fail, ret == NE_OK);
+ } 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");
+}
+
+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/test/common/ChangeLog b/test/common/ChangeLog
new file mode 100644
index 0000000..11d9840
--- /dev/null
+++ b/test/common/ChangeLog
@@ -0,0 +1,222 @@
+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
+ segfault nicely.
+
+Mon Aug 27 00:30:20 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * child.c (discard_request): New function, from request.c in
+ neon/test.
+
+Wed Aug 8 22:09:21 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c, test.h: Only define test_argc/argv once.
+
+Mon Jul 16 16:30:28 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (warning): Take printf-style arguments list.
+
+Mon Jul 16 16:16:08 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (main): Cope with skipped tests properly.
+
+Mon Jul 16 16:00:59 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (warning): New function. (main): Cope with warnings.
+
+Sun Jul 8 16:09:33 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (main): Export argc/argv as test_argc, test_argv.
+
+
diff --git a/test/common/README b/test/common/README
new file mode 100644
index 0000000..a910a23
--- /dev/null
+++ b/test/common/README
@@ -0,0 +1,4 @@
+
+Simple test framework for neon; licensed under the GNU GPL.
+
+Copyright (C) 2001-2002 Joe Orton
diff --git a/test/common/child.c b/test/common/child.c
new file mode 100644
index 0000000..84f580e
--- /dev/null
+++ b/test/common/child.c
@@ -0,0 +1,454 @@
+/*
+ Framework for testing with a server process
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/stat.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <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;
+
+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.
+ * Otherwise use boring sleep()s trying to avoid the race condition
+ * between listen() and connect() in the two processes. */
+#ifdef HAVE_PIPE
+#define USE_PIPE 1
+#endif
+
+int lookup_localhost(void)
+{
+ /* 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;
+}
+
+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 saddr = {0};
+ int val = 1;
+
+ setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int));
+
+ saddr.sin_addr = addr;
+ saddr.sin_port = htons(port);
+ saddr.sin_family = AF_INET;
+
+ if (bind(ls, (struct sockaddr *)&saddr, sizeof(saddr))) {
+ printf("bind failed: %s\n", strerror(errno));
+ return -1;
+ }
+ if (listen(ls, 5)) {
+ printf("listen failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return ls;
+}
+
+void minisleep(void)
+{
+#ifdef HAVE_USLEEP
+ usleep(500);
+#else
+ sleep(1);
+#endif
+}
+
+/* This runs as the child process. */
+static int server_child(int readyfd, struct in_addr addr, int port,
+ server_fn callback, void *userdata)
+{
+ ne_socket *s = ne_sock_create();
+ int ret, listener;
+
+ in_child();
+
+ 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);
+
+ ne_sock_close(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)) {
+ perror("spawn_server: pipe");
+ return FAIL;
+ }
+#else
+ /* avoid using uninitialized variable. */
+ fds[0] = fds[1] = 0;
+#endif
+
+ child = fork();
+
+ ONN("fork server", child == -1);
+
+ if (child == 0) {
+ /* this is the child. */
+ int ret;
+
+ ret = server_child(fds[1], addr, port, fn, ud);
+
+#ifdef USE_PIPE
+ close(fds[0]);
+ close(fds[1]);
+#endif
+
+ /* print the error out otherwise it gets lost. */
+ if (ret) {
+ printf("server child failed: %s\n", test_context);
+ }
+
+ /* and quit the child. */
+ exit(ret);
+ } else {
+ char ch;
+
+#ifdef USE_PIPE
+ if (read(fds[0], &ch, 1) < 0)
+ perror("parent read");
+
+ close(fds[0]);
+ close(fds[1]);
+#else
+ minisleep();
+#endif
+
+ return OK;
+ }
+}
+
+int 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);
+ ne_sock_close(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);
+
+ /* so that we aren't reaped by mistake. */
+ child = 0;
+
+ ONN("error from server process", WEXITSTATUS(status));
+
+ return OK;
+}
+
+int reap_server(void)
+{
+ int status;
+
+ if (child != 0) {
+ (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;
+}
diff --git a/test/common/child.h b/test/common/child.h
new file mode 100644
index 0000000..9c506b9
--- /dev/null
+++ b/test/common/child.h
@@ -0,0 +1,113 @@
+/*
+ Framework for testing with a server process
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CHILD_H
+#define CHILD_H 1
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h> /* for strlen() */
+#endif
+
+#include "ne_socket.h"
+
+/* Test which does DNS lookup on "localhost": this must be the first
+ * named test. */
+int lookup_localhost(void);
+
+/* Test which looks up real local hostname. */
+int lookup_hostname(void);
+
+/* set to local hostname if lookup_hostname succeeds. */
+extern char *local_hostname;
+
+/* Callback for spawn_server. */
+typedef int (*server_fn)(ne_socket *sock, void *userdata);
+
+/* Spawns server child process:
+ * - forks child process.
+ * - child process listens on localhost at given port.
+ * - when you connect to it, 'fn' is run...
+ * fn is passed the client/server socket as first argument,
+ * and userdata as second.
+ * - the socket is closed when 'fn' returns, so don't close in in 'fn'.
+ */
+int spawn_server(int port, server_fn fn, void *userdata);
+
+/* Like spawn_server; if bind_local is non-zero, binds server to
+ * localhost, otherwise, binds server to real local hostname. (must
+ * have called lookup_localhost or lookup_hostname as approprate
+ * beforehand). */
+int spawn_server_addr(int bind_local, int port, server_fn fn, void *userdata);
+
+/* Like spawn server except will continue accepting connections and
+ * processing requests, up to 'n' times. If 'n' is reached, then the
+ * child process exits with a failure status. */
+int spawn_server_repeat(int port, server_fn fn, void *userdata, int n);
+
+/* Blocks until child process exits, and gives return code of 'fn'. */
+int await_server(void);
+
+/* Kills child process. */
+int reap_server(void);
+
+/* Returns non-zero if server process has already died. */
+int dead_server(void);
+
+/* If discard_request comes across a header called 'want_header', it
+ * will call got_header passing the header field value. */
+extern const char *want_header;
+typedef void (*got_header_fn)(char *value);
+extern got_header_fn got_header;
+
+/* Send string to child; ne_sock_fullwrite with debugging. */
+ssize_t server_send(ne_socket *sock, const char *data, size_t len);
+
+/* Utility macro: send given string down socket. */
+#define SEND_STRING(sock, str) server_send((sock), (str), strlen((str)))
+
+/* Utility function: discard request. Sets context on error. */
+int discard_request(ne_socket *sock);
+
+/* Utility function: discard request body. Sets context on error. */
+int discard_body(ne_socket *sock);
+
+struct serve_file_args {
+ const char *fname;
+ const char *headers;
+ int chunks;
+};
+
+/* Utility function: callback for spawn_server: pass pointer to
+ * serve_file_args as userdata, and args->fname is served as a 200
+ * request. If args->headers is non-NULL, it must be a set of
+ * CRLF-terminated lines which is added in to the response headers.
+ * If args->chunks is non-zero, the file is delivered using chunks of
+ * that size. */
+int serve_file(ne_socket *sock, void *ud);
+
+/* set to value of C-L header by discard_request. */
+extern int clength;
+
+/* Sleep for a short time. */
+void minisleep(void);
+
+#endif /* CHILD_H */
diff --git a/test/common/run.sh b/test/common/run.sh
new file mode 100755
index 0000000..01ed5a9
--- /dev/null
+++ b/test/common/run.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+rm -f debug.log
+rm -f child.log
+
+# for shared builds.
+LD_LIBRARY_PATH=../src/.libs:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+
+for f in $*; do
+ if ./$f; then
+ :
+ else
+ echo FAILURE
+ exit 1
+ fi
+done
+
+exit 0
diff --git a/test/common/tests.c b/test/common/tests.c
new file mode 100644
index 0000000..2ff6b5f
--- /dev/null
+++ b/test/common/tests.c
@@ -0,0 +1,318 @@
+/*
+ Stupidly simple test framework
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <sys/signal.h>
+
+#include <stdio.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ne_utils.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#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 */
+
+static int use_colour = 0;
+
+/* 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)
+
+#define NOCOL COL("00")
+
+void t_context(const char *context, ...)
+{
+ va_list ap;
+ va_start(ap, context);
+ ne_vsnprintf(test_context, BUFSIZ, context, ap);
+ va_end(ap);
+ have_context = 1;
+}
+
+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))
+
+static void child_segv(int signo)
+{
+ signal(SIGSEGV, SIG_DFL);
+ W("(possible segfault in child, not dumping core)\n");
+ 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 for test %s ****\n", test_name);
+ signal(SIGSEGV, child_segv);
+}
+
+int main(int argc, char *argv[])
+{
+ int n;
+ static const char dots[] = "......................";
+
+ /* get basename(argv[0]) */
+ test_suite = strrchr(argv[0], '/');
+ if (test_suite == NULL) {
+ test_suite = argv[0];
+ } else {
+ test_suite++;
+ }
+
+#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", 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", 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 (ne_sock_init()) {
+ COL("43;01"); printf("WARNING:"); NOCOL;
+ printf(" Socket library initalization failed.\n");
+ }
+
+ printf("-> running `%s':\n", test_suite);
+
+ 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: %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);
+ ne_alloc_dump(debug);
+ 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:
+ 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:
+ COL("41;37;01"); printf("FAIL"); NOCOL;
+ if (have_context) {
+ printf(" (%s)", test_context);
+ }
+ 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();
+ }
+
+ /* 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));
+ fails = 1;
+ }
+
+ if (fclose(child_debug)) {
+ fprintf(stderr, "Error closing child.log: %s\n", strerror(errno));
+ fails = 1;
+ }
+
+ ne_sock_exit();
+
+ return fails;
+}
+
diff --git a/test/common/tests.h b/test/common/tests.h
new file mode 100644
index 0000000..72e8275
--- /dev/null
+++ b/test/common/tests.h
@@ -0,0 +1,125 @@
+/*
+ Stupidly simple test framework
+ 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
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef TESTS_H
+#define TESTS_H 1
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <stdio.h>
+
+#define OK 0
+#define FAIL 1
+#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);
+
+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 */
+
+/* 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 }
+
+/* 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);
+
+/* 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 */
diff --git a/test/compress.c b/test/compress.c
new file mode 100644
index 0000000..720ebe0
--- /dev/null
+++ b/test/compress.c
@@ -0,0 +1,216 @@
+/*
+ tests for compressed response handling.
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+
+#include "ne_compress.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int failed;
+
+static char *newsfn = "../NEWS";
+
+struct body {
+ const char *str;
+ size_t len;
+};
+
+static int init(void)
+{
+ if (test_argc > 1) {
+ newsfn = ne_concat(test_argv[1], "/../NEWS", NULL);
+ }
+ return lookup_localhost();
+}
+
+static void reader(void *ud, const char *block, size_t len)
+{
+ struct body *b = ud;
+
+ if (failed || len > b->len || memcmp(b->str, block, len) != 0) {
+ failed = 1;
+ } else {
+ b->str += len;
+ b->len -= len;
+ }
+}
+
+static int file2buf(int fd, ne_buffer *buf)
+{
+ char buffer[BUFSIZ];
+ ssize_t n;
+
+ while ((n = read(fd, buffer, BUFSIZ)) > 0) {
+ ne_buffer_append(buf, buffer, n);
+ }
+
+ return 0;
+}
+
+static int do_fetch(const char *realfn, const char *gzipfn,
+ int chunked, int expect_fail)
+{
+ ne_session *sess;
+ ne_request *req;
+ int fd;
+ ne_buffer *buf = ne_buffer_create();
+ struct serve_file_args sfargs;
+ ne_decompress *dc;
+ struct body body;
+
+ fd = open(realfn, O_RDONLY);
+ ONN("failed to open file", fd < 0);
+ file2buf(fd, buf);
+ (void) close(fd);
+
+ body.str = buf->data;
+ body.len = buf->used - 1;
+
+ failed = 0;
+
+ if (gzipfn) {
+ sfargs.fname = gzipfn;
+ sfargs.headers = "Content-Encoding: gzip\r\n";
+ } else {
+ sfargs.fname = realfn;
+ sfargs.headers = NULL;
+ }
+ sfargs.chunks = chunked;
+
+ CALL(make_session(&sess, serve_file, &sfargs));
+
+ req = ne_request_create(sess, "GET", "/");
+ dc = ne_decompress_reader(req, ne_accept_2xx, reader, &body);
+
+ ONREQ(ne_request_dispatch(req));
+
+ 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);
+ ne_buffer_destroy(buf);
+
+ CALL(await_server());
+
+ ONN("inflated response compare", failed);
+ if (!expect_fail)
+ ONN("inflated response truncated", body.len != 0);
+
+ 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 not_compressed(void)
+{
+ return fetch(newsfn, NULL, 0);
+}
+
+static int simple(void)
+{
+ return fetch(newsfn, "file1.gz", 0);
+}
+
+/* file1.gz has an embedded filename. */
+static int withname(void)
+{
+ return fetch(newsfn, "file2.gz", 0);
+}
+
+/* deliver various different sizes of chunks: tests the various
+ * decoding cases. */
+static int chunked_1b_wn(void)
+{
+ return fetch(newsfn, "file2.gz", 1);
+}
+
+static int chunked_1b(void)
+{
+ return fetch(newsfn, "file1.gz", 1);
+}
+
+static int chunked_12b(void)
+{
+ return fetch(newsfn, "file2.gz", 12);
+}
+
+static int chunked_20b(void)
+{
+ return fetch(newsfn, "file2.gz", 20);
+}
+
+static int chunked_10b(void)
+{
+ return fetch(newsfn, "file1.gz", 10);
+}
+
+static int chunked_10b_wn(void)
+{
+ return fetch(newsfn, "file2.gz", 10);
+}
+
+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);
+}
+
+static int fail_bad_csum(void)
+{
+ return do_fetch(newsfn, "badcsum.gz", 0, 1);
+}
+
+ne_test tests[] = {
+ T_LEAKY(init),
+ T(not_compressed),
+ T(simple),
+ T(withname),
+ T(fail_trailing),
+ T(fail_bad_csum),
+ T(fail_truncate),
+ T(chunked_1b),
+ T(chunked_1b_wn),
+ T(chunked_12b),
+ T(chunked_20b),
+ T(chunked_10b),
+ T(chunked_10b_wn),
+ T(NULL)
+};
diff --git a/test/cookies.c b/test/cookies.c
new file mode 100644
index 0000000..706ca7e
--- /dev/null
+++ b/test/cookies.c
@@ -0,0 +1,140 @@
+/*
+ Test for cookies interface (ne_cookies.h)
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_cookies.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int serve_cookie(ne_socket *sock, void *ud)
+{
+ const char *hdr = ud;
+ char buf[BUFSIZ];
+
+ CALL(discard_request(sock));
+
+ ne_snprintf(buf, BUFSIZ, "HTTP/1.0 200 Okey Dokey\r\n"
+ "Connection: close\r\n" "%s\r\n\r\n", hdr);
+
+ SEND_STRING(sock, buf);
+
+ return OK;
+}
+
+static int fetch_cookie(const char *hdr, const char *path,
+ ne_cookie_cache *jar)
+{
+ ne_session *sess;
+
+ CALL(make_session(&sess, serve_cookie, (void *)hdr));
+
+ ne_cookie_register(sess, jar);
+
+ CALL(any_request(sess, path));
+
+ ne_session_destroy(sess);
+ CALL(await_server());
+
+ return OK;
+}
+
+static int parsing(void)
+{
+ static const struct {
+ const char *hdr, *name, *value;
+ } cookies[] = {
+ { "Set-Cookie: alpha=bar", "alpha", "bar" },
+ { "Set-Cookie2: alpha=bar", "alpha", "bar" },
+ { "Set-Cookie: beta = bar", "beta", "bar" },
+ { "Set-Cookie: delta = bar; norman=fish", "delta", "bar" },
+ { NULL, NULL, NULL }
+ };
+ int n;
+
+ for (n = 0; cookies[n].hdr != NULL; n++) {
+ ne_cookie_cache jar = {0};
+ ne_cookie *ck;
+
+ CALL(fetch_cookie(cookies[n].hdr, "/foo", &jar));
+
+ ck = jar.cookies;
+ ONV(ck == NULL, ("%d: cookie jar was empty!", n));
+
+ ONV(strcmp(ck->name, cookies[n].name) ||
+ strcmp(ck->value, cookies[n].value),
+ ("%d: was [%s]=[%s]!", n, ck->name, ck->value));
+ }
+
+ return OK;
+}
+
+static int rejects(void)
+{
+ static const struct {
+ const char *hdr, *path;
+ } resps[] = {
+ /* names prefixed with $ are illegal */
+ { "Set-Cookie2: $foo=bar, Version=1", "/foo" },
+#define FOOBAR "Set-Cookie2: foo=bar, Version=1"
+ /* Path is not prefix of Request-URI */
+ { FOOBAR ", Path=/bar", "/foo" },
+ /* Domain must have embedded dots. */
+ { FOOBAR ", Domain=fish", "/foo" },
+ /* Domain must domain-match request-host */
+ { FOOBAR ", Domain=other.host.com", "/foo" },
+ /* Port not named in Port list */
+ { FOOBAR ", Port=\"12\"", "/foo" },
+ { NULL, NULL }
+ };
+ int n;
+
+ for (n = 0; resps[n].hdr != NULL; n++) {
+ ne_cookie_cache jar = {0};
+
+ CALL(fetch_cookie(resps[n].hdr, resps[n].path, &jar));
+
+ ONV(jar.cookies != NULL,
+ ("cookie was returned for `%s'", resps[n].hdr));
+ }
+
+ return OK;
+}
+
+ne_test tests[] = {
+ T(lookup_localhost),
+ T(parsing),
+ T(rejects),
+ T(NULL)
+};
+
diff --git a/test/expired.pem b/test/expired.pem
new file mode 100644
index 0000000..0062cc8
--- /dev/null
+++ b/test/expired.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMB4XDTAyMDEyMTIwMzkwNFoXDTAyMDEzMTIwMzkwNFowgaExCzAJBgNVBAYT
+AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
+MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
+ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
+ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
+oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
+AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
+VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
+Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
+IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
+ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQBDSFbe
+9EjP+IyZ4vhJSk66gLSN8CnafoGm5JHpNOHy5gWLh7j0a/dxWRd4gpoBYBB6Y9rO
+YV6Eq3njdj0gu+NN
+-----END CERTIFICATE-----
diff --git a/test/htdocs/plain b/test/htdocs/plain
new file mode 100644
index 0000000..bce1946
--- /dev/null
+++ b/test/htdocs/plain
@@ -0,0 +1 @@
+Test file.
diff --git a/test/lock.c b/test/lock.c
new file mode 100644
index 0000000..5fb80d4
--- /dev/null
+++ b/test/lock.c
@@ -0,0 +1,540 @@
+/*
+ lock tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_locks.h"
+#include "ne_socket.h"
+#include "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,
+ int depth,
+ const char *owner,
+ 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>%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;
+ char *resp, *rbody = lock_response(ne_lockscope_exclusive, 0, "me",
+ 6500, "opaquelocktoken:foo");
+ struct ne_lock *lock = ne_lock_create();
+
+ 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);
+
+ CALL(make_session(&sess, single_serve_string, resp));
+ ne_free(resp);
+
+ ne_fill_server_uri(sess, &lock->uri);
+ lock->uri.path = ne_strdup("/foo");
+ lock->timeout = 5;
+
+ ONREQ(ne_lock(sess, lock));
+
+ ONN("lock timeout ignored in response",
+ 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(fail_discover),
+ T(NULL)
+};
+
diff --git a/test/makekeys.sh b/test/makekeys.sh
new file mode 100755
index 0000000..59a1e51
--- /dev/null
+++ b/test/makekeys.sh
@@ -0,0 +1,151 @@
+#!/bin/sh
+# Helper script to create CA and server certificates.
+
+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 900"
+
+REQDN=reqDN
+export REQDN
+
+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
+
+${MKCERT} -key ca/key.pem -out ca/cert.pem <<EOF
+US
+California
+Oakland
+Neosign
+Random Dept
+nowhere.example.com
+neon@webdav.org
+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
+${State}
+${Locality}
+${Org}
+${OU}
+${CN}
+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 altname.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 "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
+
+### 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
+
+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
+ csr_fields "Wildcard Cert Dept" "*.${domain}" | \
+ ${REQ} -new -key ${srcdir}/server.key -out wildcard.csr
+ ${CA} -days 900 -in wildcard.csr -out wildcard.cert
+fi
+
+csr_fields "Neon Client Cert" ignored.example.com | \
+${REQ} -new -key client.key -out client.csr
+
+### 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
+
+REQDN=reqDN.justEmail
+echo blah@example.com | ${REQ} -new -key ${srcdir}/server.key -out justmail.csr
+
+### don't put ${REQ} invocations after here
+
+for f in server client twocn caseless cnfirst missingcn justmail; do
+ ${CA} -days 900 -in ${f}.csr -out ${f}.cert
+done
+
+${CA} -extensions altExt -days 900 -in altname.csr -out altname.cert
+${CA} -extensions altExt2 -days 900 -in altname2.csr -out altname2.cert
+${CA} -extensions altExt3 -days 900 -in altname3.csr -out altname3.cert
+${CA} -extensions altExt4 -days 900 -in altname4.csr -out altname4.cert
+
+# generate a PKCS12 cert from the client cert: -passOUT because it's the
+# passphrase on the OUTPUT cert, confusing...
+echo foobar | ${OPENSSL} pkcs12 -export -passout stdin \
+ -name "Just A Neon Client Cert" \
+ -in client.cert -inkey client.key -out client.p12
+
+# generate a PKCS12 cert with no password
+echo | ${OPENSSL} pkcs12 -export -passout stdin \
+ -name "An Unencrypted Neon Client Cert" \
+ -in client.cert -inkey client.key -out unclient.p12
+
+# generate a PKCS12 cert with no friendly name
+echo | ${OPENSSL} pkcs12 -export -passout stdin \
+ -in client.cert -inkey client.key -out noclient.p12
+
+### a file containing a complete chain
+
+cat ca/cert.pem server.cert > chain.pem
diff --git a/test/notvalid.pem b/test/notvalid.pem
new file mode 100644
index 0000000..42008b3
--- /dev/null
+++ b/test/notvalid.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMB4XDTIzMTIyNzIwNDAyOVoXDTIzMTIyODIwNDAyOVowgaExCzAJBgNVBAYT
+AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
+MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
+ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
+ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
+oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
+AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
+VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
+Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
+IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
+ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQA80TYV
+2F4QLveuldmxGoIOq5hHGxCR6aVsdtm4PGY49R5/ObCAgdWw/JV/Tc448JAz5QvU
+ahr1x9kA4Vo5NZ4q
+-----END CERTIFICATE-----
diff --git a/test/openssl.conf b/test/openssl.conf
new file mode 100644
index 0000000..9180b49
--- /dev/null
+++ b/test/openssl.conf
@@ -0,0 +1,77 @@
+[ca]
+default_ca = neonca
+
+[neonca]
+dir = ./ca
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/cert.pem
+serial = $dir/serial
+private_key = $dir/key.pem
+policy = policy_any
+default_md = md5
+x509_extensions = issuedExt
+
+[policy_any]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = optional
+emailAddress = optional
+
+[req]
+distinguished_name = $ENV::REQDN
+x509_extensions = caExt
+
+[caExt]
+basicConstraints = CA:true
+
+[issuedExt]
+basicConstraints = CA:false
+
+# subjectAltName extension sections
+[altExt]
+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
+
+[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
diff --git a/test/props.c b/test/props.c
new file mode 100644
index 0000000..61d3c22
--- /dev/null
+++ b/test/props.c
@@ -0,0 +1,508 @@
+/*
+ Tests for property handling
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_props.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static const ne_propname p_alpha = {"DAV:", "alpha"},
+ p_beta = {"http://webdav.org/random/namespace", "beta"},
+ p_delta = {NULL, "delta"};
+
+/* Tests little except that ne_proppatch() doesn't segfault. */
+static int patch_simple(void)
+{
+ ne_session *sess;
+ ne_proppatch_operation ops[] = {
+ { &p_alpha, ne_propset, "fish" },
+ { &p_beta, ne_propremove, NULL },
+ { NULL, ne_propset, NULL }
+ };
+
+ CALL(make_session(&sess, single_serve_string,
+ "HTTP/1.1 200 Goferit\r\n"
+ "Connection: close\r\n\r\n"));
+ ONREQ(ne_proppatch(sess, "/fish", ops));
+ ne_session_destroy(sess);
+ return await_server();
+}
+
+#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>",
+ 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 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(NULL)
+};
+
diff --git a/test/redirect.c b/test/redirect.c
new file mode 100644
index 0000000..80b23bb
--- /dev/null
+++ b/test/redirect.c
@@ -0,0 +1,189 @@
+/*
+ Tests for 3xx redirect interface (ne_redirect.h)
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_redirect.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+struct redir_args {
+ int code;
+ const char *dest;
+ const char *path;
+};
+
+static int serve_redir(ne_socket *sock, void *ud)
+{
+ struct redir_args *args = ud;
+ char buf[BUFSIZ];
+
+ CALL(discard_request(sock));
+
+ ne_snprintf(buf, BUFSIZ,
+ "HTTP/1.0 %d Get Ye Away\r\n"
+ "Content-Length: 0\r\n"
+ "Location: %s\r\n\n",
+ args->code, args->dest);
+
+ SEND_STRING(sock, buf);
+
+ return OK;
+}
+
+/* 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;
+ const ne_uri *loc;
+ char *unp;
+
+ CALL(make_session(&sess, serve_redir, args));
+ ne_redirect_register(sess);
+
+ CALL(process_redir(sess, args->path, &loc));
+ ONN("redirect location was NULL", loc == NULL);
+
+ 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[] = {
+ {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));
+
+ return OK;
+}
+
+/* check that a non-absoluteURI is qualified properly */
+static int non_absolute(void)
+{
+ 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
+ * check, so it's kind of redundant. Mozilla takes this approach. */
+static int fail_loop(void)
+{
+ ne_session *sess;
+
+ CALL(make_session(&sess, serve_redir, "http://localhost:7777/foo/bar"));
+
+ ne_redirect_register(sess);
+
+ ONN("followed looping redirect",
+ any_request(sess, "/foo/bar") != NE_ERROR);
+
+ ne_session_destroy(sess);
+ return OK;
+}
+#endif
+
+/* 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(non_absolute),
+ T(relative_1),
+ T(relative_2),
+ T(no_redirect),
+ T(NULL)
+};
+
diff --git a/test/request.c b/test/request.c
new file mode 100644
index 0000000..ed3029f
--- /dev/null
+++ b/test/request.c
@@ -0,0 +1,1649 @@
+/*
+ HTTP request handling tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <time.h> /* for time() */
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static char buffer[BUFSIZ];
+
+static ne_session *def_sess;
+static ne_request *def_req;
+
+static int prepare_request(server_fn fn, void *ud)
+{
+ static char uri[100];
+
+ def_sess = ne_session_create("http", "localhost", 7777);
+
+ sprintf(uri, "/test%d", test_num);
+
+ 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)
+{
+ ne_buffer *buf = ud;
+ ne_buffer_append(buf, data, len);
+}
+
+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", "/");
+ ne_buffer *buf = userdata;
+
+ ne_add_response_body_reader(r, ne_accept_2xx, collector, buf);
+
+ 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)
+{
+ ne_request *req = cb(sess, userdata);
+
+ ON(req == NULL);
+
+ ONREQ(ne_request_dispatch(req));
+
+ 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("http", "localhost", 7777);
+ ne_buffer *buf = ne_buffer_create();
+
+ ON(sess == NULL || buf == NULL);
+ ON(spawn_server(7777, fn, userdata));
+
+ 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,
+ RESP200
+ "Connection: close\r\n"
+ "\r\n"
+ "a");
+}
+
+static int single_get_clength(void)
+{
+ return expect_response("a", single_serve_string,
+ RESP200
+ "Content-Length: 1\r\n"
+ "\r\n"
+ "a"
+ "bbbbbbbbasdasd");
+}
+
+static int single_get_chunked(void)
+{
+ return expect_response("a", single_serve_string,
+ RESP200 TE_CHUNKED
+ "\r\n"
+ "1\r\n"
+ "a\r\n"
+ "0\r\n" "\r\n"
+ "g;lkjalskdjalksjd");
+}
+
+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_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 chunks(void)
+{
+ /* lots of little chunks. */
+ 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_numeric(void)
+{
+ /* leading zero's */
+ 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_extensions(void)
+{
+ /* chunk-extensions. */
+ 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_trailers(void)
+{
+ /* trailers. */
+ 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 te_over_clength(void)
+{
+ /* T-E dominates over C-L. */
+ return expect_response("abcde", single_serve_string,
+ RESP200 TE_CHUNKED
+ "Content-Length: 300\r\n"
+ "\r\n"
+ ABCDE_CHUNKS);
+}
+
+/* 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);
+}
+
+/* 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)
+{
+ return expect_no_body("HEAD", "HTTP/1.1 204 Not Modified\r\n"
+ TE_CHUNKED "\r\n");
+}
+
+static int serve_twice(ne_socket *sock, void *userdata)
+{
+ const char *resp = userdata;
+
+ CALL(discard_request(sock));
+ SEND_STRING(sock, resp);
+
+ CALL(discard_request(sock));
+ SEND_STRING(sock, resp);
+
+ return OK;
+}
+
+/* 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("http", "localhost", 7777);
+ ne_buffer *buf = ne_buffer_create();
+
+ 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));
+
+ /* Run it again. */
+ ne_buffer_clear(buf);
+ CALL(run_request(sess, 200, construct_get, buf));
+
+ ON(await_server());
+
+ ONV(strcmp(buf->data, body),
+ ("response #2 mismatch: [%s] not [%s]", buf->data, body));
+
+ ne_session_destroy(sess);
+ ne_buffer_destroy(buf);
+
+ return OK;
+}
+
+static int persist_http11(void)
+{
+ return test_persist(RESP200 "Content-Length: 5\r\n\r\n" "abcde",
+ "abcde");
+}
+
+static int persist_chunked(void)
+{
+ return test_persist(RESP200 TE_CHUNKED "\r\n" ABCDE_CHUNKS,
+ "abcde");
+}
+
+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));
+ CALL(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n"));
+ /* real request/response. */
+ CALL(discard_request(sock));
+ CALL(SEND_STRING(sock, resp));
+
+ 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;
+}
+
+/* This failed with neon <0.22. */
+static int fail_eof_continued(void)
+{
+ return fail_early_eof("HTTP/1.1 100 OK\r\n\r\n");
+}
+
+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));
+
+ 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;
+}
+
+/* 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)
+{
+ 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));
+
+ CALL(any_2xx_request(sess, "/first"));
+ minisleep();
+ CALL(any_2xx_request_body(sess, "/second"));
+
+ ONN("server died prematurely?", dead_server());
+ reap_server();
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+/* TODO: add a ptimeout_reset too, if an RST can be reliably generated
+ * mid-connection. */
+
+/* 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;
+
+ ON(sess == NULL || buf == NULL);
+
+ 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++) {
+
+ ONV(run_request(sess, 200, construct_get, buf),
+ ("%d of %d, request failed: %s", n, args.count,
+ ne_get_error(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;
+}
+
+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_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)
+};
diff --git a/test/resolve.c b/test/resolve.c
new file mode 100644
index 0000000..bd0789e
--- /dev/null
+++ b/test/resolve.c
@@ -0,0 +1,59 @@
+/*
+ Test program for the neon resolver interface
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "ne_socket.h"
+
+int main(int argc, char **argv)
+{
+ ne_sock_addr *addr;
+ char buf[256];
+ int ret = 0;
+
+ if (argc < 2) {
+ printf("Usage: %s hostname\n", argv[0]);
+ return 1;
+ }
+
+ if (ne_sock_init()) {
+ printf("%s: Failed to initialize socket library.\n", argv[0]);
+ 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 = 2;
+ } else {
+ 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_iaddr_print(ia, buf, sizeof buf));
+ }
+ putchar('\n');
+ }
+ ne_addr_destroy(addr);
+
+ return ret;
+}
diff --git a/test/run.sh b/test/run.sh
new file mode 100644
index 0000000..7d72749
--- /dev/null
+++ b/test/run.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+rm -f debug.log
+rm -f child.log
+
+# for shared builds.
+LD_LIBRARY_PATH=../src/.libs:$LD_LIBRARY_PATH
+
+# enable an safety-checking malloc in glibc which will abort() if
+# heap corruption is detected.
+MALLOC_CHECK_=2
+
+export LD_LIBRARY_PATH MALLOC_CHECK_
+
+for f in $*; do
+ if ${HARNESS} ./$f ${SRCDIR}; then
+ :
+ else
+ echo FAILURE
+ [ -z "$CARRYON" ] && exit 1
+ fi
+done
+
+exit 0
diff --git a/test/server.key b/test/server.key
new file mode 100644
index 0000000..cdfb91b
--- /dev/null
+++ b/test/server.key
@@ -0,0 +1,9 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP+psm5EGZGmGJGvSD
+sk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAQJAJBhYdoVAqNqEVu8rKB3C
+F4kcqLUlYBDVAL+ZM4QlwgWncAKk2C53BwH4PVWIIfyysleyt3bTAtqg/tgMNM06
+AQIhAP1HKbuppa+UY4rNP4Xcyj5BrCU4wVz77sg/ygW+mWIhAiEA9eKcUnnaIpig
+hlWtx9qz++85/JtahA85j6T48v0hBM0CIQCa8ByUg2wq45CdSX+xiOZjfVMslfKb
+yjZBY9xW9UjpYQIgdy9j5JqKANEIpnTran95VLot2mMXagHTPeySe331PlUCIQD0
+rL1AXeIR3Vd4D8dgab/FVbg4i94qBiY0731nyPJRoQ==
+-----END RSA PRIVATE KEY-----
diff --git a/test/session.c b/test/session.c
new file mode 100644
index 0000000..6fe04bd
--- /dev/null
+++ b/test/session.c
@@ -0,0 +1,158 @@
+/*
+ Tests for session handling
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_session.h"
+
+#include "tests.h"
+
+/* should be in a 'session.c'? */
+static int fill_uri(void)
+{
+ ne_uri uri = {0};
+ ne_session *sess = ne_session_create("http", "localhost", 7777);
+
+ ne_fill_server_uri(sess, &uri);
+
+ ONN("hostname mis-match", strcmp(uri.host, "localhost"));
+ ONN("port mis-match", uri.port != 7777);
+ ONN("scheme mis-match", strcmp(uri.scheme, "http"));
+
+ ne_session_destroy(sess);
+ ne_uri_free(&uri);
+
+ return OK;
+}
+
+static int match_hostport(const char *scheme, const char *hostname, int port,
+ const char *hostport)
+{
+ ne_session *sess = ne_session_create(scheme, hostname, port);
+ const char *hp = ne_get_server_hostport(sess);
+ ONV(strcmp(hp, hostport),
+ ("hostport incorrect for %s: `%s' not `%s'", scheme, hp, hostport));
+ ne_session_destroy(sess);
+ return OK;
+}
+
+static int hostports(void)
+{
+ static const struct {
+ const char *scheme, *hostname;
+ int port;
+ const char *hostport;
+ } hps[] = {
+ { "http", "host.name", 80, "host.name" },
+ { "http", "host.name", 555, "host.name:555" },
+ { "http", "host.name", 443, "host.name:443" },
+ { "https", "host.name", 80, "host.name:80" },
+ { "https", "host.name", 443, "host.name" },
+ { "https", "host.name", 700, "host.name:700" },
+ { NULL }
+ };
+ int n;
+
+ for (n = 0; hps[n].scheme; n++) {
+ CALL(match_hostport(hps[n].scheme, hps[n].hostname,
+ hps[n].port, hps[n].hostport));
+ }
+
+ return OK;
+}
+
+
+/* Check that ne_set_error is passing through to printf correctly. */
+static int errors(void)
+{
+ ne_session *sess = ne_session_create("http", "foo.com", 80);
+
+#define EXPECT "foo, hello world, 100, bar!"
+
+ ne_set_error(sess, "foo, %s, %d, bar!", "hello world", 100);
+
+ ONV(strcmp(ne_get_error(sess), EXPECT),
+ ("session error was `%s' not `%s'",
+ ne_get_error(sess), EXPECT));
+#undef EXPECT
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+#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(privates),
+ T(get_scheme),
+ T(NULL)
+};
+
diff --git a/test/skeleton.c b/test/skeleton.c
new file mode 100644
index 0000000..9574bdb
--- /dev/null
+++ b/test/skeleton.c
@@ -0,0 +1,51 @@
+/*
+ neon test suite
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int foo(void)
+{
+ /* This is a skeleton test suite file. */
+ return OK;
+}
+
+ne_test tests[] = {
+ T(foo), /* test functions here */
+
+ /* end of test functions. */
+ T(NULL)
+};
+
diff --git a/test/socket.c b/test/socket.c
new file mode 100644
index 0000000..26b107b
--- /dev/null
+++ b/test/socket.c
@@ -0,0 +1,918 @@
+/*
+ Socket handling tests
+ 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
+ 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.
+
+*/
+
+/* 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"
+
+
+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)
+{
+ 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 */
+ return lookup_localhost();
+}
+
+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 = 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;
+}
+
+static int just_connect(void)
+{
+ ne_socket *sock;
+
+ 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;
+}
+
+/* Exect a read() to return EOF */
+static int expect_close(ne_socket *sock)
+{
+ ssize_t n = ne_sock_read(sock, buffer, 1);
+ 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 %" NE_FMT_SSIZE_T " not closure", n));
+ return OK;
+}
+
+/* Test that just does a connect then a close. */
+static int read_close(void)
+{
+ ne_socket *sock;
+
+ CALL(begin(&sock, serve_close, NULL));
+ CALL(expect_close(sock));
+ ONN("close failed", ne_sock_close(sock));
+ return await_server();
+}
+
+/* Test that just does a connect then a close (but gets the close via
+ * ne_sock_peek). */
+static int peek_close(void)
+{
+ ne_socket *sock;
+
+ CALL(begin(&sock, serve_close, NULL));
+ CALL(expect_peek_close(sock));
+ ONN("close failed", ne_sock_close(sock));
+ return await_server();
+}
+
+
+struct string {
+ char *data;
+ size_t len;
+};
+
+static int serve_string(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;
+}
+
+static int serve_string_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;
+}
+
+/* Don't change this string. */
+#define STR "Hello, World."
+
+/* do a sock_peek() on sock for 'len' bytes, and expect 'str'. */
+static int peek_expect(ne_socket *sock, const char *str, size_t len)
+{
+ ssize_t ret = ne_sock_peek(sock, buffer, len);
+ ONV((ssize_t)len != ret,
+ ("peek got %" NE_FMT_SSIZE_T " bytes not %" NE_FMT_SIZE_T, ret, len));
+ ONV(memcmp(str, buffer, len),
+ ("peek mismatch: `%.*s' not `%.*s'",
+ (int)len, buffer, (int)len, str));
+ return OK;
+}
+
+/* do a sock_read() on sock for 'len' bytes, and expect 'str'. */
+static int read_expect(ne_socket *sock, const char *str, size_t len)
+{
+ ssize_t ret = ne_sock_read(sock, buffer, len);
+ ONV((ssize_t)len != ret,
+ ("peek got %" NE_FMT_SSIZE_T " bytes not %" NE_FMT_SIZE_T, ret, len));
+ ONV(memcmp(str, buffer, len),
+ ("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;
+ DECL(hello, STR);
+
+ CALL(begin(&sock, serve_string, &hello));
+ CALL(read_expect(sock, STR, strlen(STR)));
+ CALL(expect_close(sock));
+ CALL(good_close(sock));
+
+ return await_server();
+}
+
+/* Test a simple peek. */
+static int single_peek(void)
+{
+ ne_socket *sock;
+ DECL(hello, STR);
+
+ CALL(begin(&sock, serve_string, &hello));
+ CALL(peek_expect(sock, STR, strlen(STR)));
+
+ return finish(sock, 0);
+}
+
+/* Test lots of 1-byte reads. */
+static int small_reads(void)
+{
+ ne_socket *sock;
+ char *pnt;
+ DECL(hello, STR);
+
+ CALL(begin(&sock, serve_string, &hello));
+
+ /* read the string byte-by-byte. */
+ for (pnt = hello.data; *pnt; pnt++) {
+ CALL(read_expect(sock, pnt, 1));
+ }
+
+ 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;
+ DECL(hello, STR);
+
+ CALL(begin(&sock, serve_string, &hello));
+
+ PEEK("Hello");
+ PEEK("Hell");
+ PEEK(STR);
+ READ("He");
+ PEEK("llo, ");
+ READ("l");
+ PEEK("lo, World.");
+ READ("lo, Worl");
+ PEEK("d."); PEEK("d");
+ READ("d.");
+
+ return finish(sock, 1);
+}
+
+/* Read more bytes than were written. */
+static int larger_read(void)
+{
+ ne_socket *sock;
+ ssize_t nb;
+ DECL(hello, STR);
+
+ CALL(begin(&sock, serve_string, &hello));
+
+ nb = ne_sock_read(sock, buffer, hello.len + 10);
+ ONV(nb != (ssize_t)hello.len,
+ ("read gave too many bytes (%" NE_FMT_SSIZE_T ")", nb));
+ ONN("read gave wrong data", memcmp(buffer, hello.data, hello.len));
+
+ 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_string, &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_string, &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_string, &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_string, &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_string, &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_string_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));
+ 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());
+ ret = ne_sock_fullwrite(sock, "a", 1);
+ 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);
+ 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);
+ ONV(ret != NE_SOCK_RESET, ("read got %" NE_FMT_SSIZE_T " not reset", ret));
+ return good_close(sock);
+}
+#endif
+
+ne_test tests[] = {
+ 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),
+ T(single_peek),
+ 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)
+};
diff --git a/test/ssl.c b/test/ssl.c
new file mode 100644
index 0000000..7fc94e4
--- /dev/null
+++ b/test/ssl.c
@@ -0,0 +1,1476 @@
+/*
+ neon test suite
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <sys/stat.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_auth.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+#ifndef NEON_SSL
+/* this file shouldn't be built if SSL is not enabled. */
+#error SSL not supported
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))
+
+#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 SSL_CTX *server_ctx = NULL;
+
+static char *srcdir = ".";
+
+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)
+{
+ size_t len = strlen(buf);
+
+ ONV(SSL_write(s, buf, len) != (int)len,
+ ("SSL_write failed: %s", ERROR_SSL_STRING));
+
+ return OK;
+}
+
+/* Do an SSL response over socket given context; returning ssl session
+ * structure in *sess if sess is non-NULL. */
+static int do_ssl_response(ne_socket *sock, SSL_CTX *ctx, SSL_SESSION **sess,
+ const char *resp, int unclean)
+{
+ 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);
+ char buf[BUFSIZ];
+ SSL *ssl = SSL_new(ctx);
+
+ ONN("SSL_new failed", ssl == NULL);
+
+ SSL_set_bio(ssl, bio, bio);
+
+ ONV(SSL_accept(ssl) != 1,
+ ("SSL_accept failed: %s", ERROR_SSL_STRING));
+
+ 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);
+
+ ONN("request over SSL contained Proxy-Authorization header",
+ strstr(buf, "Proxy-Authorization:") != NULL);
+
+ CALL(s_strwrite(ssl, resp));
+
+ /* copy out the session if requested. */
+ if (sess) {
+ *sess = SSL_get1_session(ssl);
+ }
+
+ if (!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);
+ }
+
+ return 0;
+}
+
+#define DEF_RESP "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"
+
+/* Standard server callback to send an HTTP response; SSL negotiated
+ * using certificate passed as userdata. */
+static int serve_ssl(ne_socket *sock, void *ud)
+{
+ const char *cert = ud;
+
+ NE_DEBUG(NE_DBG_HTTP, "using server cert %s\n", cert);
+
+ ONN("failed to load certificate",
+ !SSL_CTX_use_certificate_file(server_ctx, cert, SSL_FILETYPE_PEM));
+
+ CALL(do_ssl_response(sock, server_ctx, NULL, DEF_RESP, 0));
+
+ return OK;
+}
+
+static int serve_response_unclean(ne_socket *sock, void *ud)
+{
+ const char *resp = ud;
+
+ ONN("failed to load certificate",
+ !SSL_CTX_use_certificate_file(server_ctx,
+ SERVER_CERT, SSL_FILETYPE_PEM));
+
+ CALL(do_ssl_response(sock, server_ctx, NULL, resp, 1));
+
+ return OK;
+}
+
+/* Server function which requires the use of a client cert.
+ * 'userdata' must be the name of the file giving acceptable CA
+ * certificates. */
+static int serve_ccert(ne_socket *sock, void *ud)
+{
+ const char *calist = ud;
+
+ ONN("failed to load certificate",
+ !SSL_CTX_use_certificate_file(server_ctx, SERVER_CERT, SSL_FILETYPE_PEM));
+
+ /* require a client cert. */
+ SSL_CTX_set_verify(server_ctx, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+
+ /* load the CA used to verify the client cert. */
+ ONN("failed to load CA cert",
+ SSL_CTX_load_verify_locations(server_ctx, CA_CERT, NULL) != 1);
+
+ if (calist) {
+ /* send acceptable CA cert list to the client */
+ SSL_CTX_set_client_CA_list(server_ctx, SSL_load_client_CA_file(calist));
+ }
+
+ CALL(do_ssl_response(sock, server_ctx, NULL, DEF_RESP, 0));
+
+ return OK;
+}
+
+/* serve_ssl wrapper which ignores server failure and always succeeds */
+static int fail_serve(ne_socket *sock, void *ud)
+{
+ serve_ssl(sock, ud);
+ return OK;
+}
+
+/* Wrapper for serve_ssl which registers the verify location, so that
+ * the CA cert will be sent along with the server cert itself in the
+ * certificate exchange. */
+static int serve_ssl_chained(ne_socket *sock, void *ud)
+{
+ SSL_CTX_load_verify_locations(server_ctx, "ca/cert.pem", NULL);
+ return serve_ssl(sock, ud);
+}
+
+#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)
+{
+ char *server_key;
+
+ /* 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;
+ }
+
+ server_ctx = SSL_CTX_new(SSLv23_server_method());
+ if (server_ctx == NULL) {
+ t_context("could not create SSL_CTX: %s", ERROR_SSL_STRING);
+ return FAILHARD;
+ } else if (!SSL_CTX_use_PrivateKey_file(server_ctx, server_key,
+ SSL_FILETYPE_PEM)) {
+ t_context("failed to load private key: %s", ERROR_SSL_STRING);
+ 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(char *cert)
+{
+ ne_session *sess = DEFSESS;
+ /* no verify callback needed. */
+ CALL(any_ssl_request(sess, serve_ssl, cert, CA_CERT, NULL, NULL));
+ ne_session_destroy(sess);
+ return OK;
+}
+
+static int simple(void)
+{
+ return accept_signed_cert(SERVER_CERT);
+}
+
+/* Serves using HTTP/1.0 get-till-EOF semantics. */
+static int serve_eof(ne_socket *sock, void *ud)
+{
+ const char *cert = ud;
+
+ NE_DEBUG(NE_DBG_HTTP, "using server cert %s\n", cert);
+
+ ONN("failed to load certificate",
+ !SSL_CTX_use_certificate_file(server_ctx, cert, SSL_FILETYPE_PEM));
+
+ CALL(do_ssl_response(sock, server_ctx, NULL,
+ "HTTP/1.0 200 OK\r\n"
+ "Connection: close\r\n"
+ "\r\n"
+ "This is a response body, like it or not.",
+ 0));
+
+ return OK;
+}
+
+/* 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;
+
+ CALL(any_ssl_request(sess, serve_response_unclean,
+ "HTTP/1.0 200 OK\r\n" "\r\n",
+ CA_CERT, NULL, NULL));
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+static int fail_truncated_eof(void)
+{
+ ne_session *sess = DEFSESS;
+ int ret;
+
+ ne_ssl_trust_cert(sess, def_ca_cert);
+ CALL(spawn_server(7777, serve_response_unclean,
+ "HTTP/1.0 200 OK\r\n" "\r\n"
+ "This is some content\n"
+ "Followed by a truncation attack!\n"));
+
+ ret = any_request(sess, "/foo");
+ CALL(await_server());
+
+ ONV(ret != NE_ERROR,
+ ("request failed with %d not error: `%s'", ret, ne_get_error(sess)));
+ 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());
+
+ ONN("request did not fail", ret != NE_ERROR);
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+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;
+
+ PRECOND(wildcard_ok);
+
+ sess = ne_session_create("https", local_hostname, 7777);
+
+ CALL(any_ssl_request(sess, serve_ssl,
+ "wildcard.cert", 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("altname.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");
+}
+
+/* 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;
+
+ /* don't give a CA cert; should force the verify callback to be
+ * used. */
+ CALL(any_ssl_request(sess, serve_ssl, SERVER_CERT, 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;
+
+ /* don't give a CA cert; should force the verify callback to be
+ * used. */
+ CALL(any_ssl_request(sess, serve_ssl_chained, SERVER_CERT, 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;
+
+ CALL(any_ssl_request(sess, serve_ssl, SERVER_CERT, 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;
+
+ /* force verify cert. */
+ ret = any_ssl_request(sess, serve_ssl, SERVER_CERT, NULL, count_vfy,
+ &count);
+
+ CALL(spawn_server(7777, serve_ssl, SERVER_CERT));
+ ret = any_request(sess, "/foo2");
+ CALL(await_server());
+
+ 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. */
+ ONN(msg, ret == NE_OK);
+
+ 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;
+}
+
+struct scache_args {
+ SSL_CTX *ctx;
+ char *cert;
+ int count;
+ SSL_SESSION *sess;
+};
+
+/* FIXME: factor out shared code with serve_ssl */
+static int serve_scache(ne_socket *sock, void *ud)
+{
+ struct scache_args *args = ud;
+ SSL_SESSION *sess;
+
+ if (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(args->ctx, SSL_SESS_CACHE_SERVER);
+
+ ONN("failed to load certificate",
+ !SSL_CTX_use_certificate_file(args->ctx,
+ args->cert, SSL_FILETYPE_PEM));
+ }
+
+ args->count++;
+
+ CALL(do_ssl_response(sock, args->ctx, &sess, DEF_RESP, 0));
+
+ /* dump session to child.log for debugging. */
+ SSL_SESSION_print_fp(ne_debug_stream, sess);
+
+ if (args->count == 1) {
+ /* save the session. */
+ args->sess = 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->sess, sess));
+ SSL_SESSION_free(args->sess);
+ SSL_SESSION_free(sess);
+ }
+
+ return 0;
+}
+
+/* Test that the SSL session is cached across connections. */
+static int session_cache(void)
+{
+ struct scache_args args;
+ ne_session *sess = ne_session_create("https", "localhost", 7777);
+
+ args.ctx = server_ctx;
+ args.count = 0;
+ args.cert = SERVER_CERT;
+
+ ne_ssl_trust_cert(sess, def_ca_cert);
+
+ /* have spawned server listen for several connections. */
+ CALL(spawn_server_repeat(7777, serve_scache, &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;
+
+ 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, serve_ccert, NULL, 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;
+
+ PRECOND(def_cli_cert);
+
+ ne_ssl_provide_clicert(sess, cc_check_dnames, &check);
+
+ CALL(any_ssl_request(sess, serve_ccert, "calist.pem", 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;
+
+ PRECOND(def_cli_cert);
+
+ ne_ssl_set_clicert(sess, def_cli_cert);
+ CALL(any_ssl_request(sess, serve_ccert, NULL, 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;
+
+ 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, serve_ccert, NULL, CA_CERT, NULL, NULL));
+
+ ne_ssl_clicert_free(ccert);
+ ne_session_destroy(sess);
+ return OK;
+}
+
+static int serve_tunnel(ne_socket *sock, void *ud)
+{
+ CALL(discard_request(sock));
+
+ SEND_STRING(sock, "HTTP/1.1 200 OK\r\nServer: Fish\r\n\r\n");
+ return serve_ssl(sock, ud);
+}
+
+/* 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);
+ ne_session_proxy(sess, "localhost", 7777);
+
+ ONN("server cert verification didn't fail",
+ any_ssl_request(sess, serve_tunnel, SERVER_CERT, 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);
+ ne_session_proxy(sess, "localhost", 7777);
+
+ /* CA cert is trusted, so no verify callback should be needed. */
+ CALL(any_ssl_request(sess, serve_tunnel, SERVER_CERT, CA_CERT,
+ NULL, NULL));
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+/* 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)
+{
+ if (st->code == 407) {
+ NE_DEBUG(NE_DBG_HTTP, "Got 407, awaiting server...\n");
+ CALL(await_server());
+ NE_DEBUG(NE_DBG_HTTP, "Spawning proper tunnel server...\n");
+ CALL(spawn_server(7777, serve_tunnel, SERVER_CERT));
+ 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 0;
+}
+
+/* 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;
+
+ ne_session_proxy(sess, "localhost", 7777);
+ ne_hook_post_send(sess, apt_post_send, NULL);
+ ne_set_proxy_auth(sess, apt_creds, NULL);
+
+ 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"));
+
+ /* trust the CA */
+ ne_ssl_trust_cert(sess, def_ca_cert);
+ /* run the dreaded request. */
+ ret = any_request(sess, "/foobar");
+ CALL(await_server());
+ ONREQ(ret);
+
+ ne_session_destroy(sess);
+ return 0;
+}
+
+/* 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" },
+ { "altname.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;
+}
+
+/* tests for ne_ssl_readable_dname */
+static int dname_readable(void)
+{
+ ne_ssl_certificate *cert;
+
+ cert = ne_ssl_cert_read("justmail.cert");
+ ONN("could not load justmail.cert", cert == NULL);
+
+ CALL(check_cert_dnames(cert, "blah@example.com", NULL));
+ 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;
+
+ ONREQ(any_ssl_request(sess, serve_ssl, "ssigned.pem", 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, serve_ssl, "ssigned.pem", 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_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_eof),
+ T(empty_truncated_eof),
+ T(fail_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(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(NULL)
+};
diff --git a/test/string-tests.c b/test/string-tests.c
new file mode 100644
index 0000000..93c8508
--- /dev/null
+++ b/test/string-tests.c
@@ -0,0 +1,492 @@
+/*
+ String handling tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h> /* for the ENOENT definitions in str_errors */
+#endif
+
+#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) {
+ 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);
+ 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 buf_concat2(void)
+{
+#define RES "alphabetagammadeltaepsilonetatheta"
+ ne_buffer *s = ne_buffer_create();
+ ON(s == NULL);
+ 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) {
+ ne_buffer *s = ne_buffer_create();
+ ON(s == NULL);
+ 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) {
+ ne_buffer *s = ne_buffer_create();
+ char *d;
+ ON(s == NULL);
+ ne_buffer_zappend(s, "abcdefg");
+ d = s->data;
+ ON(d == NULL);
+ d[2] = '\0';
+ 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, dlen, decoded, 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;
+}
+
+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)
+};
+
diff --git a/test/stubs.c b/test/stubs.c
new file mode 100644
index 0000000..494f8f0
--- /dev/null
+++ b/test/stubs.c
@@ -0,0 +1,171 @@
+/*
+ neon test suite
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** These tests show that the stub functions produce appropriate
+ * results to provide ABI-compatibility when a particular feature is
+ * not supported by the library.
+ **/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_compress.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+#if defined(NEON_ZLIB) && defined(NEON_SSL)
+#define NO_TESTS 1
+#endif
+
+#ifndef NEON_ZLIB
+static int sd_result = OK;
+
+static void sd_reader(void *ud, const char *block, size_t len)
+{
+ const char *expect = ud;
+ if (strncmp(expect, block, len) != 0) {
+ sd_result = FAIL;
+ t_context("decompress reader got bad data");
+ }
+}
+
+static int stub_decompress(void)
+{
+ ne_session *sess;
+ ne_decompress *dc;
+ ne_request *req;
+ int ret;
+
+ CALL(make_session(&sess, single_serve_string,
+ "HTTP/1.1 200 OK" EOL
+ "Connection: close" EOL EOL
+ "abcde"));
+
+ req = ne_request_create(sess, "GET", "/foo");
+
+ dc = ne_decompress_reader(req, ne_accept_2xx, sd_reader, "abcde");
+
+ ret = ne_request_dispatch(req);
+
+ CALL(await_server());
+
+ ONREQ(ret);
+
+ ONN("decompress_destroy failed", ne_decompress_destroy(dc));
+
+ ne_request_destroy(req);
+ ne_session_destroy(sess);
+
+ /* This is a skeleton test suite file. */
+ return sd_result;
+}
+#endif
+
+#ifndef NEON_SSL
+static int stub_ssl(void)
+{
+ ne_session *sess = ne_session_create("https", "localhost", 7777);
+ ne_ssl_certificate *cert;
+ ne_ssl_client_cert *cc;
+
+ /* these should all fail when SSL is not supported. */
+ 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;
+}
+#endif
+
+#ifdef NO_TESTS
+static int null_test(void) { return OK; }
+#endif
+
+ne_test tests[] = {
+#ifndef NEON_ZLIB
+ T(stub_decompress),
+#endif
+#ifndef NEON_SSL
+ T(stub_ssl),
+#endif
+/* to prevent failure when SSL and zlib are supported. */
+#ifdef NO_TESTS
+ T(null_test),
+#endif
+ T(NULL)
+};
+
diff --git a/test/uri-tests.c b/test/uri-tests.c
new file mode 100644
index 0000000..ce35f22
--- /dev/null
+++ b/test/uri-tests.c
@@ -0,0 +1,377 @@
+/*
+ URI tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_uri.h"
+#include "ne_alloc.h"
+
+#include "tests.h"
+
+static int simple(void)
+{
+ 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);
+ ON(p.authinfo != NULL);
+ ne_uri_free(&p);
+ return 0;
+}
+
+static int simple_ssl(void)
+{
+ ne_uri p = {0};
+ ON(ne_uri_parse("https://webdav.org/", &p));
+ ON(strcmp(p.scheme, "https"));
+ ON(p.port);
+ ne_uri_free(&p);
+ return OK;
+}
+
+static int no_path(void)
+{
+ ne_uri p = {0};
+ ON(ne_uri_parse("https://webdav.org", &p));
+ ON(strcmp(p.path, "/"));
+ ne_uri_free(&p);
+ return OK;
+}
+
+#define STR "/a¹²³¼½/"
+static int escapes(void)
+{
+ char *un, *esc;
+ esc = ne_path_escape(STR);
+ ON(esc == NULL);
+ un = ne_path_unescape(esc);
+ ON(un == NULL);
+ ON(strcmp(un, STR));
+ 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)
+{
+ 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 compares(void)
+{
+ 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;
+}
+
+/* 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)
+{
+ 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(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(ne_path_has_trailing_slash("/a/") == 0);
+ ON(ne_path_has_trailing_slash("/a") != 0);
+ {
+ /* check the uri == "" case. */
+ char *foo = "/";
+ ON(ne_path_has_trailing_slash(&foo[1]));
+ }
+ return OK;
+}
+
+static int just_hostname(void)
+{
+ 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)
+{
+ ne_uri p = {0};
+ ON(ne_uri_parse("/argh", &p));
+ ON(strcmp(p.path, "/argh"));
+ ne_uri_free(&p);
+ return 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 *exp = &uritests[n];
+ ONV(ne_uri_parse(exp->uri, &res) != 0,
+ ("%s: parse failed", exp->uri));
+ ONV(res.port != exp->port,
+ ("%s: parsed port was %d not %d", exp->uri, res.port, exp->port));
+ ONCMP(exp->scheme, res.scheme, exp->uri, "scheme");
+ ONCMP(exp->host, res.host, exp->uri, "host");
+ ONV(strcmp(res.path, exp->path),
+ ("%s: parsed path was %s not %s", exp->uri, res.path, exp->path));
+ ONCMP(exp->authinfo, res.authinfo, exp->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;
+}
+
+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)
+};
diff --git a/test/util-tests.c b/test/util-tests.c
new file mode 100644
index 0000000..3a774bc
--- /dev/null
+++ b/test/util-tests.c
@@ -0,0 +1,256 @@
+/*
+ utils tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_utils.h"
+#include "ne_md5.h"
+#include "ne_alloc.h"
+#include "ne_dates.h"
+#include "ne_string.h"
+
+#include "tests.h"
+
+static const struct {
+ const char *status;
+ int major, minor, code;
+ const char *rp;
+} accept_sl[] = {
+ /* These are really valid. */
+ { "HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+ { "HTTP/1.1000 200 OK", 1, 1000, 200, "OK" },
+ { "HTTP/1000.1000 200 OK", 1000, 1000, 200, "OK" },
+ { "HTTP/00001.1 200 OK", 1, 1, 200, "OK" },
+ { "HTTP/1.00001 200 OK", 1, 1, 200, "OK" },
+ { "HTTP/99.99 999 99999", 99, 99, 999, "99999" },
+ { "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 }
+};
+
+static const char *bad_sl[] = {
+ "",
+ "HTTP/1.1 1000 OK",
+ "HTTP/1.1 1000",
+ "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)
+{
+ ne_status s;
+ int n;
+
+ for (n = 0; accept_sl[n].status != NULL; n++) {
+ 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++) {
+ 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(digest_md5("", 0, buf), ascii);
+ ONN("MD5(null)", strcmp(ascii, "d41d8cd98f00b204e9800998ecf8427e"));
+
+ ne_md5_to_ascii(digest_md5("foobar", 7, buf), ascii);
+ ONN("MD5(foobar)", strcmp(ascii, "b4258860eea29e875e2ee4019763b2bb"));
+
+ /* $ 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 },
+ /* 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 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);
+
+ 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;
+}
+
+ne_test tests[] = {
+ T(status_lines),
+ T(md5),
+ T(md5_alignment),
+ T(parse_dates),
+ T(versioning),
+ T(version_string),
+ T(support),
+ T(NULL)
+};
diff --git a/test/utils.c b/test/utils.c
new file mode 100644
index 0000000..47e1410
--- /dev/null
+++ b/test/utils.c
@@ -0,0 +1,107 @@
+/*
+ Utility functions for HTTP client tests
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* 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 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;
+ return serve_response(s, str);
+}
+
+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;
+
+ for (n = 0; n < args->count; n++) {
+ NE_DEBUG(NE_DBG_HTTP, "Serving response %d\n", n);
+ CALL(serve_response(s, args->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;
+}
+
diff --git a/test/utils.h b/test/utils.h
new file mode 100644
index 0000000..55c61ab
--- /dev/null
+++ b/test/utils.h
@@ -0,0 +1,57 @@
+/*
+ neon-specific test utils
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef UTILS_H
+#define UTILS_H 1
+
+#include "ne_request.h"
+
+#define ONREQ(x) do { int _ret = (x); if (_ret) { 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);
+
+#endif /* UTILS_H */
diff --git a/test/xml.c b/test/xml.c
new file mode 100644
index 0000000..9d76215
--- /dev/null
+++ b/test/xml.c
@@ -0,0 +1,444 @@
+/*
+ neon test suite
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_xml.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+/* 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;
+
+ 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;
+}
+
+/* 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;
+
+ if (state != expected)
+ ne_buffer_concat(buf, "wrong state in endelm of ", name, NULL);
+
+ return 0;
+}
+
+/* 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();
+
+ 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);
+
+ 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)));
+
+ 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);
+
+ return OK;
+}
+
+static int matches(void)
+{
+#define PFX "<?xml version='1.0'?>\r\n"
+#define E(ns, n) "<{" ns "}" n "></{" ns "}" n ">"
+ static const struct {
+ const char *in, *out;
+ enum match_type invalid;
+ } ms[] = {
+
+ /*** Simplest tests ***/
+ { PFX "<hello/>", "<{}hello></{}hello>"},
+ { PFX "<hello foo='bar'/>",
+ "<{}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>\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>"},
+ { 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++) {
+ 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'/>",
+ 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;
+
+ 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)
+};
+