summaryrefslogtreecommitdiff
path: root/neon
diff options
context:
space:
mode:
authorjbj <devnull@localhost>2004-10-29 13:57:41 +0000
committerjbj <devnull@localhost>2004-10-29 13:57:41 +0000
commit69227ecd1bf64421da5ee99c14cf1613e1114787 (patch)
treeababad53ef07514d61ef30f018c84eee59589cd7 /neon
parente603ae4198c394996d56a34bc69c92a1b16dfddb (diff)
downloadlibrpm-tizen-69227ecd1bf64421da5ee99c14cf1613e1114787.tar.gz
librpm-tizen-69227ecd1bf64421da5ee99c14cf1613e1114787.tar.bz2
librpm-tizen-69227ecd1bf64421da5ee99c14cf1613e1114787.zip
Update to neon trunk.
CVS patchset: 7523 CVS date: 2004/10/29 13:57:41
Diffstat (limited to 'neon')
-rwxr-xr-xneon/.release.sh6
-rw-r--r--neon/.version2
-rw-r--r--neon/AUTHORS7
-rw-r--r--neon/BUGS21
-rw-r--r--neon/ChangeLog52
-rw-r--r--neon/INSTALL.win32154
-rw-r--r--neon/Makefile.in2
-rw-r--r--neon/NEWS19
-rw-r--r--neon/README36
-rw-r--r--neon/THANKS62
-rw-r--r--neon/TODO4
-rw-r--r--neon/autogen.sh4
-rw-r--r--neon/config.hw.in41
-rw-r--r--neon/configure.in75
-rw-r--r--neon/doc/biblio.xml9
-rw-r--r--neon/doc/date.xml2
-rw-r--r--neon/doc/manual.xml2
-rw-r--r--neon/doc/ref/clicert.xml6
-rw-r--r--neon/doc/ref/iaddr.xml15
-rw-r--r--neon/doc/ref/sslca.xml81
-rw-r--r--neon/doc/ref/xml.xml56
-rw-r--r--neon/doc/using.xml29
-rw-r--r--neon/doc/version.xml2
-rw-r--r--neon/macros/ChangeLog107
-rw-r--r--neon/macros/neon-test.m44
-rw-r--r--neon/macros/neon-xml-parser.m46
-rw-r--r--neon/macros/neon.m4320
-rw-r--r--neon/neon-config.in16
-rw-r--r--neon/neon.mak7
-rw-r--r--neon/src/ChangeLog400
-rw-r--r--neon/src/Makefile.in18
-rw-r--r--neon/src/ne_207.c10
-rw-r--r--neon/src/ne_207.h6
-rw-r--r--neon/src/ne_acl.c5
-rw-r--r--neon/src/ne_alloc.h12
-rw-r--r--neon/src/ne_auth.c373
-rw-r--r--neon/src/ne_basic.c112
-rw-r--r--neon/src/ne_basic.h90
-rw-r--r--neon/src/ne_compress.c143
-rw-r--r--neon/src/ne_compress.h12
-rw-r--r--neon/src/ne_dates.c27
-rw-r--r--neon/src/ne_defs.h8
-rw-r--r--neon/src/ne_gnutls.c853
-rw-r--r--neon/src/ne_locks.c11
-rw-r--r--neon/src/ne_openssl.c95
-rw-r--r--neon/src/ne_private.h20
-rw-r--r--neon/src/ne_privssl.h35
-rw-r--r--neon/src/ne_props.c23
-rw-r--r--neon/src/ne_request.c332
-rw-r--r--neon/src/ne_request.h54
-rw-r--r--neon/src/ne_session.c44
-rw-r--r--neon/src/ne_session.h26
-rw-r--r--neon/src/ne_socket.c371
-rw-r--r--neon/src/ne_socket.h21
-rw-r--r--neon/src/ne_ssl.h25
-rw-r--r--neon/src/ne_string.c198
-rw-r--r--neon/src/ne_string.h22
-rw-r--r--neon/src/ne_stubssl.c14
-rw-r--r--neon/src/ne_uri.c8
-rw-r--r--neon/src/ne_utils.c63
-rw-r--r--neon/src/ne_utils.h26
-rw-r--r--neon/src/ne_xml.c137
-rw-r--r--neon/src/ne_xml.h37
-rw-r--r--neon/test/ChangeLog117
-rw-r--r--neon/test/Makefile.in47
-rw-r--r--neon/test/acl.c14
-rw-r--r--neon/test/auth.c85
-rw-r--r--neon/test/basic.c4
-rw-r--r--neon/test/ca1.pem15
-rw-r--r--neon/test/ca2.pem15
-rw-r--r--neon/test/ca3.pem14
-rw-r--r--neon/test/ca4.pem14
-rw-r--r--neon/test/calist.pem58
-rw-r--r--neon/test/chain.pem56
-rw-r--r--neon/test/common/ChangeLog8
-rw-r--r--neon/test/common/child.c13
-rw-r--r--neon/test/common/child.h74
-rw-r--r--neon/test/common/tests.c20
-rw-r--r--neon/test/compress.c63
-rw-r--r--neon/test/largefile.c190
-rwxr-xr-xneon/test/makekeys.sh18
-rw-r--r--neon/test/openssl.conf14
-rw-r--r--neon/test/props.c52
-rw-r--r--neon/test/request.c187
-rwxr-xr-xneon/test/run.sh7
-rw-r--r--neon/test/socket.c98
-rw-r--r--neon/test/ssl.c48
-rw-r--r--neon/test/string-tests.c38
-rw-r--r--neon/test/stubs.c10
-rw-r--r--neon/test/util-tests.c26
-rw-r--r--neon/test/utils.c18
-rw-r--r--neon/test/utils.h8
-rw-r--r--neon/test/xml.c85
93 files changed, 4700 insertions, 1494 deletions
diff --git a/neon/.release.sh b/neon/.release.sh
index a278217a2..2a540ae5e 100755
--- a/neon/.release.sh
+++ b/neon/.release.sh
@@ -7,12 +7,6 @@ minor=`echo $1 | awk -F. '{print $2;}'`
rel=`echo $1 | awk -F. '{print $3;}'`
version=$1
-# check that release version matches build version!
-grep ^NEON_VERSION_MAJOR=$major\$ macros/neon.m4 || exit 1
-grep ^NEON_VERSION_MINOR=$minor\$ macros/neon.m4 || exit 1
-grep ^NEON_VERSION_RELEASE=$rel\$ macros/neon.m4 || exit 1
-grep '^NEON_VERSION_TAG=$' macros/neon.m4 || exit 1
-
for f in config.hw; do
in=$f.in
out=$f
diff --git a/neon/.version b/neon/.version
index c9731ff41..cb676de10 100644
--- a/neon/.version
+++ b/neon/.version
@@ -1 +1 @@
-0.24.7
+0.0.0-dev
diff --git a/neon/AUTHORS b/neon/AUTHORS
index f64fe1852..660afedc7 100644
--- a/neon/AUTHORS
+++ b/neon/AUTHORS
@@ -1 +1,6 @@
-Joe Orton <joe@orton.demon.co.uk>
+neon is Copyright (C) 1999-2004 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.
+Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org>
diff --git a/neon/BUGS b/neon/BUGS
index 2ae9ca5f3..f8e5d72d3 100644
--- a/neon/BUGS
+++ b/neon/BUGS
@@ -2,6 +2,8 @@
Known problems/bugs in neon -*- text -*-
---------------------------
+* look at escaping logic again w.r.t. ?, # characters?
+
* 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
@@ -10,19 +12,32 @@ Known problems/bugs in neon -*- text -*-
* ne_lock_discover does not handle multiple (shared) locks on
a single resource.
+* ne_lock_refresh does not update the passed-in lock structure.
+
* SSL session caching issues; only cache for clean shutdowns, and
only cache on shutdown, since the SSL_SESSION may change during
- an ne_session.
+ an ne_session?
* what is passed as 'path' to req create hook: auth needs Request-URI;
how does that interact with proxies? also they will be passed NULL
for a CONNECT request, or "*" possibly as well.
-* expect100 support is broken.
-
* It would be nice to fail with a friendly error message if a client
cert is requested by the srever but one is not provided. Currently,
returning -1 from the provide_client_cert function would allow that
(as it forces the SSL handshake to fail), but that would prevent
opportunistic use of client certificates, of the "SSLVerifyClient
optional" variety.
+
+* D.J. Heap has a proxy which returns a 401 in response to a CONNECT;
+relax the ne_auth rules to allow this since it's unambiguous.
+
+* Error handling from ne__pull_request_body/send_request_body is
+buggy: socket errors are not distinguished from body provider errors;
+the connection must be closed in ne_request after a body provider
+error.
+
+* Check whether the following always return UTF-8-encoded strings:
+ - ne_ssl_clicert_name
+ - ne_ssl_cert_identity
+
diff --git a/neon/ChangeLog b/neon/ChangeLog
index bb1a69104..c1e1bd05b 100644
--- a/neon/ChangeLog
+++ b/neon/ChangeLog
@@ -1,15 +1,59 @@
Mon May 17 21:25:44 2004 Joe Orton <joe@manyfish.co.uk>
- * neon.mak: Fix handling of paths with spaces, and simplify (Jon
- Foster <jon@jon-foster.co.uk>).
+ * neon.mak: Fix for handling of paths with spaces, and
+ simplify (Jon Foster <jon@jon-foster.co.uk>).
Thu May 13 11:42:07 2004 Joe Orton <joe@manyfish.co.uk>
* configure.in: Don't rely on echo -n in ne_version.
-Fri Mar 26 12:07:04 2004 Joe Orton <joe@manyfish.co.uk>
+Sun Feb 22 20:29:04 2004 Joe Orton <joe@manyfish.co.uk>
- * Makefile.in (distclean): Clean more.
+ * configure.in: Move memleak.h include to end of config.h.
+
+Sun Feb 22 18:44:55 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * configure.in: Fix to run DAV tests when an XML parser is
+ configured.
+
+Sat Jan 24 17:50:52 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * configure.in: AC_DEFINE _GNU_SOURCE again so that it's used
+ during compiler checks.
+
+Sat Jan 24 17:33:54 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * configure.in: Cleanup; move all AC_SUBSTs together; use a single
+ AH_TOP for config.h.in header; require autoconf 2.58.
+
+Thu Jan 1 17:40:34 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon-config.in: Handle 'lfs' feature.
+
+Sat Nov 15 09:42:40 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * neon-config.in: Print help output on stderr for unknown arguments.
+ (usage): Update help output for new NE_FLAG_ substitutions.
+
+Sat Nov 15 09:24:49 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * configure.in: Update for use latest autoconf best-practice:
+ s/AC_HELP_STRING/AS_HELP_STRING.
+
+Fri Nov 14 11:28:24 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * configure.in, neon-config.in: Use new NE_FLAG substitutions
+ for feature detection.
+
+Fri Nov 14 09:08:10 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * configure.in: Add -export-symbols-regex to NEON_LINK_FLAGS to
+ prevent export of ne__ symbols where possible.
+
+Sun Oct 26 12:42:15 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * neon-config.in: Fix to exit with failure for an unrecognized
+ option.
Sat Oct 25 10:37:59 2003 Joe Orton <joe@manyfish.co.uk>
diff --git a/neon/INSTALL.win32 b/neon/INSTALL.win32
index 04e161189..4b10550e0 100644
--- a/neon/INSTALL.win32
+++ b/neon/INSTALL.win32
@@ -1,14 +1,140 @@
-
-To install neon on Windows you need expat-lite already installed on your system.
-It can be taken from the Apache distribution or downloaded from the expat website.
-Now simply point make to the expat sources:
-
- nmake /f neon.mak EXPAT_SRC=\path\to\expat-lite
-
-This should work with Microsoft VC++ 5 and 6.
-
-After compiling the Release subdirectory contains neons.lib, against which you can
-link your program. When you run your program make sure the XMLPARSE.DLL from
-expat is accessable, i.e. is in your PATH.
-
-
+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 IPv6 support
+____________________________
+
+To build neon with support for IPv6, use parameter ENABLE_IPV6.
+
+ nmake /f neon.mak ENABLE_IPV6=yes
+
+This requires a copy of the Platform SDK which contains the IPv6
+headers and libraries.
+
+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/neon/Makefile.in b/neon/Makefile.in
index ae8a535e3..d0acd6826 100644
--- a/neon/Makefile.in
+++ b/neon/Makefile.in
@@ -99,7 +99,7 @@ clean:
cd test && $(MAKE) clean
distclean: clean
- rm -rf Makefile config.h config.status libtool config.log config.cache neon-config autom4te.cache neon.pc test/Makefile test/common/Makefile src/Makefile
+ rm -rf Makefile config.h neon.pc config.status src/Makefile libtool config.log config.cache neon-config autom4te*.cache test/Makefile
again: clean
diff --git a/neon/NEWS b/neon/NEWS
index dbc5713d8..905d1bb81 100644
--- a/neon/NEWS
+++ b/neon/NEWS
@@ -1,3 +1,22 @@
+Changes in release 0.25.0:
+* New feature detection interface, ne_has_feature():
+ - replaces ne_supports_ssl(); NEON_SSL is no longer defined by neon-config.
+* ne_set_request_body_fd takes offset and length arguments and returns void.
+* ne_set_request_body_provider takes an off_t length argument.
+* Support for non-ASCII hostnames following the IDNA spec, using GNU libidn:
+ - if enabled, ne_session_create can be passed a UTF-8 encoded hostname.
+* ne_xml_failed() replaces ne_xml_valid(), with different return value logic.
+* Support for >2Gb request/response bodies on 32-bit Unixes with LFS support:
+ - new ne_set_request_body_fd64() call for using an fd opened using O_LARGEFILE
+ - new ne_set_request_body_provider64() which takes an off64_t length argument
+* ne_xml interface now rejects more invalid XML element names (e.g. "foo::bar").
+* Add ne_set_addrlist() interface to bypass normal DNS resolution.
+* Use a per-request flag to enable "Expect: 100-continue" support:
+ - ne_set_request_expect100() replaces ne_set_expect100()
+* Fix handling of multiple Authentication challenges per request.
+* Win32: Fix timezone handling (Jiang Lei).
+* Win32: Add IPv6 support using ENABLE_IPV6 flag (Kai Sommerfeld).
+
Changes in release 0.24.7:
* Compression interface fixes:
- fix issues handling content decoding and request retries from
diff --git a/neon/README b/neon/README
index a2a3478c8..d43f4714e 100644
--- a/neon/README
+++ b/neon/README
@@ -1,5 +1,14 @@
-neon is an HTTP and WebDAV client library.
+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/
+
+PLEASE NOTE: The neon API is subject to backwards-incompatible change
+over minor releases (0.23.x -> 0.24.x) until the 1.0.0 release, but
+maintains source and binary backwards compatibility through patch
+releases (0.24.0 -> 0.24.7).
Current features:
@@ -9,17 +18,32 @@ Current features:
- 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 parser
+ - 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.
+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 less restrictive license, see
+each file for details. The test suite is licensed under the GNU GPL;
+see test/COPYING for full details.
-Joe Orton
-<joe@orton.demon.co.uk>
+neon is Copyright (C) 1999-2004 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.
+Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org>
diff --git a/neon/THANKS b/neon/THANKS
index 404db9e19..281d27069 100644
--- a/neon/THANKS
+++ b/neon/THANKS
@@ -1,52 +1,14 @@
-In alphabetical order:
+Thanks go to the following people for contributing to neon development
+with code, patches, or good bug reports or suggestions.
-Arun Garg <arung@pspl.co.in>
-Blair Zajac <blair@orcaware.com>
-Branko Èibej <brane@xbc.nu>
-Daniel Berlin <dberlin@dberlin.org>
-David Sloat <d.sloat@f5.com>
-David Reid <dreid@jetnet.co.uk>
-Dirk Bergstrom <dirk@juniper.net>
-Gerald Richter <richter@ecos.de>
-Greg Stein <gstein@lyra.org>
-Gregor Bornemann <Gregor.Bornemann@germany.sun.com>
-Jeff Johnson <jbj@redhat.com>
-Jeremy Elson <jelson@circlemud.org>
-Jim Whitehead <ejw@cse.ucsc.edu>
-Johan Lindh <johan@linkdata.se>
-Justin Erenkrantz <jerenkrantz@apache.org>
-Kai Sommerfeld <kai.sommerfeld@germany.sun.com>
-Keith Wannamaker <keith@wannamaker.org>
-Lee Mallabone <lee0@callnetuk.com>
-Magnus Sirwiö <sirwio@hotmail.com>
-Markus Mueller <markus-m.mueller@ubs.com>
-Max Bowsher <maxb@ukf.net>
-Michael Sobolev <mss@despair.spb.ru>
-Mike Rosellini <m@icopyright.com>
-Mo DeJong <mdejong@cygnus.com>
-Noriaki Takamiya <takamiya@po.ntts.co.jp>
-Olof Oberg <mill@pedgr571.sn.umu.se>
-Pawel Golaszewski <blues@ds.pg.gda.pl>
-Peter Boos <PediB@colorfullife.com>
-Peter Moulder <pjm@bofh.asn.au>
-rado <dzusto@yahoo.com>
-Risko Gergely <risko@risko.hu>
-Rodney Dawes <dobey@ximian.com>
-Sam TH <sam@uchicago.edu>
-Sander Alberink <sander.alberink@cmg.nl>
-Sander Striker <striker@apache.org>
-Stefan Esser <s.esser@e-matters.de>
-Shane Mayer <shanemayer42@yahoo.com>
-Taisuke Yamada <tai@iij.ad.jp>
-Teng Xu <txu@soe.ucsc.edu>
-Tom Bednarz <tombednarz@hotmail.com>
-Tom Lee <i_am_gnomey@hotmail.com>
-Torsten Kalix <torsten.kalix@bredex.de>
-Wilfredo Sánchez <wsanchez@mit.edu>
+Arun Garg, Blair Zajac, Branko Èibej, Daniel Berlin, David Sloat,
+David Reid, Dirk Bergstrom, Ulrich Drepper, Gerald Richter, Greg
+Stein, Gregor Bornemann, Jeff Johnson, Jeremy Elson, Jim Whitehead,
+Johan Lindh, Justin Erenkrantz, Kai Sommerfeld, Keith Wannamaker, Lee
+Mallabone, Magnus Sirwiö, Markus Mueller, Max Bowsher, Michael
+Sobolev, Mike Rosellini, Mo DeJong, Noriaki Takamiya, Olof Oberg,
+Pawel Golaszewski, Peter Boos, Peter Moulder, rado, Risko Gergely,
+Rodney Dawes, Sam TH, Sander Alberink, Sander Striker, Stefan Esser,
+Shane Mayer, Taisuke Yamada, Teng Xu, Tom Bednarz, Tom Lee, Tommi
+Komulainen, Torsten Kalix, Wilfredo Sánchez, Daniel Veillard,
-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/neon/TODO b/neon/TODO
index fcce1d31a..f7ef7b466 100644
--- a/neon/TODO
+++ b/neon/TODO
@@ -32,7 +32,7 @@ For one-point-oh
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.
+64. Add options to only enable SSLv2 support, etc.
Longer term
-----------
@@ -116,3 +116,5 @@ Longer term
57. Add function to map of status-code values to i18n-ized reason
phrase.
+65. Add ne_uri_copy function and use it in ne_lock_copy. (patch
+ sent to neon@webdav.org)
diff --git a/neon/autogen.sh b/neon/autogen.sh
index cc95493b8..471327a1b 100644
--- a/neon/autogen.sh
+++ b/neon/autogen.sh
@@ -11,12 +11,12 @@ if test ! -f .version; then
echo -n 0.0.0-dev > doc/version.xml
fi
set -e
+echo -n "libtoolize... "
+${LIBTOOLIZE:-libtoolize} --copy --force >/dev/null
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.
diff --git a/neon/config.hw.in b/neon/config.hw.in
index 3d86b2768..7b4ab639f 100644
--- a/neon/config.hw.in
+++ b/neon/config.hw.in
@@ -1,6 +1,7 @@
/*
Win32 config.h
Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com>
+ Copyright (C) 2002, 2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -18,25 +19,31 @@
MA 02111-1307, USA
*/
-#ifdef _WIN32
-#define WIN32
-#endif
+#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_STRING_H
-#define HAVE_EXPAT
-#define HAVE_OLD_EXPAT
-#define HAVE_MEMCPY
+#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_LIMITS_H
+
+#define HAVE_MEMCPY
+#define HAVE_SETSOCKOPT
+
+#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
@@ -44,10 +51,10 @@
#define strcasecmp strcmpi
#define strncasecmp strnicmp
#define ssize_t int
-#define inline __inline
-#define off_t _off_t
+#define inline __inline
+#define off_t _off_t
#include <io.h>
#define read _read
-
+
#endif
diff --git a/neon/configure.in b/neon/configure.in
index 0ba04ce81..1b2e0f623 100644
--- a/neon/configure.in
+++ b/neon/configure.in
@@ -1,6 +1,4 @@
-dnl Require autoconf 2.53 for an AC_C_BIGENDIAN which supports
-dnl cross-compiling.
-AC_PREREQ(2.53)
+AC_PREREQ(2.58) dnl 2.58 required for AS_HELP_STRING
dnl Extract the version (sans LF) from .version, created at release-time.
m4_define(ne_version, [m4_translit(m4_include(.version), [
@@ -8,7 +6,7 @@ m4_define(ne_version, [m4_translit(m4_include(.version), [
AC_INIT(neon, ne_version, [neon@webdav.org])
-AC_COPYRIGHT([Copyright (c) 2000, 2001, 2002 Joe Orton
+AC_COPYRIGHT([Copyright 2000-2004 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])
@@ -21,16 +19,22 @@ NEON_WITH_LIBS
# 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_DEFINE([_GNU_SOURCE], 1, [Always defined to enable GNU extensions])
+
+dnl Place straight into config.h.in:
+AH_TOP([
+/* Only defined when neon source directory built as a standalone library */
+#define NEON_IS_LIBRARY 1])
+
+AH_BOTTOM([
+/* Enable leak-tracking versions of ne_*alloc when NEON_MEMLEAK is enabled */
+#ifdef NEON_MEMLEAK
+# include "memleak.h"
+#endif])
AC_PROG_INSTALL
@@ -43,11 +47,8 @@ AC_PROG_LIBTOOL
AC_EXEEXT
-top_builddir=`pwd`
-AC_SUBST(top_builddir)
-
-AC_ARG_ENABLE(webdav,
-AC_HELP_STRING([--disable-webdav], [disable WebDAV support]))
+AC_ARG_ENABLE(webdav,
+ AS_HELP_STRING([--disable-webdav],[disable WebDAV support]))
if test "$enable_webdav" = "no"; then
NEON_WITHOUT_WEBDAV
@@ -60,7 +61,6 @@ 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
@@ -68,6 +68,10 @@ NEON_VERSIONS
# Pass the interface version on to libtool when linking libneon.la
NEON_LINK_FLAGS="-version-info ${NEON_INTERFACE_VERSION}"
+# Library-internal symbols are in the ne__ namespace: tell libtool
+# to not export these from the built library if possible.
+NEON_LINK_FLAGS="$NEON_LINK_FLAGS -export-symbols-regex '^ne_[[^_]]'"
+
# Checks to compile test suite
NEON_TEST
@@ -82,20 +86,11 @@ 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)
+CPPFLAGS="$CPPFLAGS -I\${top_builddir}"
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])
+ AS_HELP_STRING([--enable-memleak],
+ [for test builds only: enable memory leak checking]))
if test "$enable_memleak" = "yes"; then
CPPFLAGS="$CPPFLAGS -DNEON_MEMLEAK -I\$(top_srcdir)/src"
@@ -106,7 +101,7 @@ fi
# Enable tests for optional features
TESTS="\$(BASIC_TESTS)"
HELPERS=""
-if test "$NEON_SUPPORTS_SSL" = "yes"; then
+if test $NE_FLAG_SSL = yes; then
# Only enable SSL tests if an openssl binary is found (needed to make
# certs etc).
AC_PATH_PROG(OPENSSL, openssl, notfound)
@@ -117,19 +112,25 @@ if test "$NEON_SUPPORTS_SSL" = "yes"; then
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
+if test $NE_FLAG_ZLIB = yes; then
TESTS="$TESTS \$(ZLIB_TESTS)"
HELPERS="$HELPERS \$(ZLIB_HELPERS)"
fi
-AC_SUBST(HELPERS)
-AC_SUBST(TESTS)
+if test x$enable_webdav != xno; then
+ TESTS="$TESTS \$(DAV_TESTS)"
+fi
AC_CONFIG_FILES([neon-config], [chmod +x neon-config])
AC_CONFIG_FILES([Makefile src/Makefile test/Makefile neon.pc])
+AC_SUBST(NEON_VERSION)
+AC_SUBST(NEON_BUILD_BUNDLED)
+AC_SUBST(top_builddir)
+AC_SUBST(user_LDFLAGS)
+AC_SUBST(HELPERS)
+AC_SUBST(TESTS)
+AC_SUBST(ALLOW_INSTALL)
+
AC_OUTPUT
# for VPATH builds:
@@ -140,8 +141,8 @@ 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}
+ SSL library: ${ne_SSL_message}
+ zlib support: ${ne_ZLIB_message}
Build libraries: Shared=${enable_shared}, Static=${enable_static}
Now run 'make' to compile the neon library.
diff --git a/neon/doc/biblio.xml b/neon/doc/biblio.xml
index cda37a76c..43574b838 100644
--- a/neon/doc/biblio.xml
+++ b/neon/doc/biblio.xml
@@ -10,12 +10,11 @@
<pubdate>March 2001</pubdate>
</biblioentry>
-<biblioentry id="bib.xsltrec">
+<biblioentry id="bib.xmlnames">
<abbrev>REC-XML-names</abbrev>
- <editor><firstname>James</firstname><surname>Clark</surname></editor>
- <title><ulink url="http://www.w3.org/TR/xslt">XSL Transformations
- (XSLT) Version 1.0</ulink></title> <publishername>W3C
- Recommendation</publishername> <pubdate>16 November 1999</pubdate>
+ <corpauthor>World Wide Web Consortium</corpauthor>
+ <title><ulink url="http://www.w3.org/TR/REC-xml-names">Namespaces in XML</ulink></title>
+ <pubdate>January 1999</pubdate>
</biblioentry>
<biblioentry id="bib.rfc2616">
diff --git a/neon/doc/date.xml b/neon/doc/date.xml
index 4a8caa9a9..1e8d8d669 100644
--- a/neon/doc/date.xml
+++ b/neon/doc/date.xml
@@ -1 +1 @@
- 5 July 2004 \ No newline at end of file
+29 October 2004 \ No newline at end of file
diff --git a/neon/doc/manual.xml b/neon/doc/manual.xml
index 06e7bca22..f665abd71 100644
--- a/neon/doc/manual.xml
+++ b/neon/doc/manual.xml
@@ -69,6 +69,7 @@
<!ENTITY refresolve SYSTEM "ref/resolve.xml">
<!ENTITY refiaddr SYSTEM "ref/iaddr.xml">
<!ENTITY refclicert SYSTEM "ref/clicert.xml">
+<!ENTITY refxml SYSTEM "ref/xml.xml">
]>
@@ -170,6 +171,7 @@ ignoring the WebDAV support if desired.</para>
&refstatus; <!-- ne_status -->
&reftok; <!-- ne_token -->
&refvers; <!-- ne_version_match -->
+ &refxml; <!-- ne_xml_parser -->
<!-- REFEND -->
<!-- ******************************************************************* -->
diff --git a/neon/doc/ref/clicert.xml b/neon/doc/ref/clicert.xml
index 49f95cfbb..67833aa77 100644
--- a/neon/doc/ref/clicert.xml
+++ b/neon/doc/ref/clicert.xml
@@ -62,9 +62,9 @@
<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
+ <type>ne_ssl_client_cert</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
+ An <type>ne_ssl_client_cert</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>
@@ -124,7 +124,7 @@
<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;
+ <programlisting>ne_ssl_client_cert *ccert;
ccert = ne_ssl_clicert_read("/path/to/client.p12");
diff --git a/neon/doc/ref/iaddr.xml b/neon/doc/ref/iaddr.xml
index cf64a13c1..a4fba59b7 100644
--- a/neon/doc/ref/iaddr.xml
+++ b/neon/doc/ref/iaddr.xml
@@ -9,6 +9,7 @@
<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_typeof">ne_iaddr_typeof</refname>
<refname id="ne_iaddr_free">ne_iaddr_free</refname>
<refpurpose>functions to manipulate and compare network addresses</refpurpose>
</refnamediv>
@@ -32,8 +33,8 @@ typedef enum {
<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>
+ <paramdef>const ne_inet_addr *<parameter>ia1</parameter></paramdef>
+ <paramdef>const ne_inet_addr *<parameter>ia2</parameter></paramdef>
</funcprototype>
<funcprototype>
@@ -44,8 +45,13 @@ typedef enum {
</funcprototype>
<funcprototype>
+ <funcdef>ne_iaddr_type <function>ne_iaddr_typeof</function></funcdef>
+ <paramdef>const ne_inet_addr *<parameter>ia</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>void <function>ne_iaddr_free</function></funcdef>
- <paramdef>const ne_inet_addr *<parameter>addr</parameter></paramdef>
+ <paramdef>const ne_inet_addr *<parameter>ia</parameter></paramdef>
</funcprototype>
</funcsynopsis>
@@ -76,6 +82,9 @@ typedef enum {
buffer, for instance the string
<literal>"127.0.0.1"</literal>.</para>
+ <para><function>ne_iaddr_type</function> returns the type of the
+ given network address.</para>
+
<para><function>ne_iaddr_free</function> releases the memory
associated with a network address object.</para>
diff --git a/neon/doc/ref/sslca.xml b/neon/doc/ref/sslca.xml
new file mode 100644
index 000000000..c68739d7a
--- /dev/null
+++ b/neon/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/neon/doc/ref/xml.xml b/neon/doc/ref/xml.xml
new file mode 100644
index 000000000..e61a4515c
--- /dev/null
+++ b/neon/doc/ref/xml.xml
@@ -0,0 +1,56 @@
+<refentry id="refxml">
+
+ <refmeta>
+ <refentrytitle>ne_xml_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_xml_create">ne_xml_create</refname>
+ <refname id="ne_xml_destroy">ne_xml_destroy</refname>
+ <refpurpose>create and destroy an XML parser</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_xml.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>ne_xml_parser *<function>ne_xml_create</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_xml_destroy</function></funcdef>
+ <paramdef>ne_xml_parser *<parameter>parser</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_xml_create</function> function creates an
+ XML parser object, which can be used for parsing XML documents
+ using stacked SAX handlers.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>ne_xml_create</function> returns a pointer to an
+ XML parser object, and never &null;</para> </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para>XXX</para>
+ </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/using.xml b/neon/doc/using.xml
index efb53d199..e88c9efb4 100644
--- a/neon/doc/using.xml
+++ b/neon/doc/using.xml
@@ -73,11 +73,11 @@
</sect1>
<sect1 id="compliance">
- <title>Protocol compliance</title>
+ <title>Standards 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
+ <para>&neon; is intended to be compliant with the IETF and W3C
+ standards which it implements, with a few exceptions due to
+ practical necessity or interoperability issues. These
exceptions are documented in this section.</para>
<sect2><title>RFC 2518, HTTP Extensions for Distributed Authoring&mdash;WebDAV</title>
@@ -116,4 +116,25 @@
quotes&mdash;this is not known to cause any problems with
other server implementations.</para></sect2>
+ <sect2>
+ <title>Namespaces in XML</title>
+
+ <para>The &neon; XML parser interface will accept and parse
+ without error some XML documents which are well-formed
+ according to the XML specification but do not conform to the
+ "Namespaces in XML" specification <xref
+ linkend="bib.xmlnames"/>. Specifically: the restrictions on
+ the first character of the <literal>NCName</literal> rule are
+ not all implemented; &neon; will allow any
+ <literal>CombiningChar</literal>, <literal>Extender</literal>
+ and some characters from the <literal>Digit</literal> class in
+ this position.</para> </sect2>
+
+ <!-- a few RFC2818/3280 issues: rules about when to cache
+ sessions in the face of unclean shutdown are strict, neon is
+ probably not compliant: document or fix. Likewise SSL
+ shutdown issues in general. Cert hostname checks allow
+ wildcard "*." syntax which is less than 2818 but more than
+ 3280 requires. -->
+
</sect1>
diff --git a/neon/doc/version.xml b/neon/doc/version.xml
index 1380da071..3b2e7a037 100644
--- a/neon/doc/version.xml
+++ b/neon/doc/version.xml
@@ -1 +1 @@
-0.24.7 \ No newline at end of file
+0.0.0-dev \ No newline at end of file
diff --git a/neon/macros/ChangeLog b/neon/macros/ChangeLog
index 24e0824de..ad265430b 100644
--- a/neon/macros/ChangeLog
+++ b/neon/macros/ChangeLog
@@ -1,13 +1,24 @@
+Sun Sep 12 18:38:13 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NEON_USE_EXTERNAL): Check for IDNA, LFS, SOCKS
+ support.
+ (NEON_SOCKS): Use common feature code for SOCKSv5 support.
+
+Fri Sep 10 20:52:54 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (LIBNEON_SOURCE_CHECKS): Safer autoheader template for
+ declaring stpcpy as necessary for bundled neon builds.
+ (NEON_WARNINGS): Drop -Winline.
+
+Wed Aug 25 19:44:26 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for poll.
+
Sat Jul 3 11:39:01 2004 Joe Orton <joe@manyfish.co.uk>
* neon.m4 (LIBNEON_SOURCE_CHECKS): Pick up gethostbyname in
-lsocket for QNX.
-Wed May 19 08:36:44 2004 Joe Orton <joe@manyfish.co.uk>
-
- * neon.m4 (LIBNEON_SOURCE_CHECKS): Declare stpcpy on modern
- "Linux-like" AIXes.
-
Fri Apr 16 11:43:10 2004 Joe Orton <joe@manyfish.co.uk>
* neon-xml-parser.m4 (NEON_XML_PARSER): If built using libtool,
@@ -21,6 +32,87 @@ Tue Apr 13 20:51:59 2004 Joe Orton <joe@manyfish.co.uk>
* neon.m4 (NEON_GSSAPI): Check for presence of
gssapi/gssapi_generic.h.
+Wed Apr 7 13:16:33 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NE_LARGEFILE): Check for strtoq.
+
+Mon Mar 15 19:59:36 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (LIBNEON_SOURCE_CHECKS): Be safer around getaddrinfo
+ blacklist for HP-UX and reference why it's needed.
+
+Sun Mar 7 11:15:44 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (LIBNEON_SOURCE_CHECKS): Use NE_LARGEFILE in-place
+ rather than AC_REQUIRE'ing it.
+ (NE_LARGEFILE): Add NE_LFS to CPPFLAGS for use in bundled builds.
+
+Mon Feb 23 23:02:54 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NE_SNPRINTF): Define HAVE_TRIO if trio is used.
+
+Mon Feb 23 00:22:39 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (LIBNEON_SOURCE_CHECKS): Give INCLUDES argument to
+ AC_CHECK_HEADERS; prevent warning from cpp test for netinet/in.h
+ on some platforms.
+
+Sun Feb 22 17:52:42 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NE_SEARCH_LIBS): Fix to run actions-if-found if
+ function is found without needing additional libraries.
+ (LIBNEON_SOURCE_CHECKS): Only check for gethostbyname if
+ getaddrinfo is not found. Disable getaddrinfo on HP-UX 11.[01]*
+ here rather than ne_socket.c.
+
+Sat Jan 24 17:49:50 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (LIBNEON_SOURCE_CHECKS): Also check for __tm_gmtoff in
+ struct tm.
+
+Sat Jan 24 17:16:48 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4: Remove -ansi-pedantic and -Wimplicit-prototypes for gcc
+ 3.4 compatibility (thanks to Olaf Hering).
+
+Sat Jan 3 14:11:14 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon-test.m4: Check for stdint.h.
+
+Sat Jan 3 13:17:21 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NE_LARGEFILE): Add NE_LFS to neon-config --cflags
+ output.
+
+Thu Jan 1 18:42:56 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NEON_FORMAT): Use C99 'll' rather than non-standard 'q'
+ length modifier.
+
+Thu Jan 1 17:36:39 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NE_LARGEFILE): New macro.
+ (LIBNEON_SOURCE_CHECKS): Call it.
+
+Sat Nov 15 09:25:43 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4, neon-xml-parser.m4: Update for latest autoconf
+ best-practice: s/AC_HELP_STRING/AS_HELP_STRING, replace AC_TRY_RUN
+ with AC_RUN_IFELSE, AC_TRY_LINK_FUNC with AC_LINK_IFELSE,
+ AC_TRY_COMPILE with AC_COMPILE_IFELSE, remove AC_LANG_C and
+ AC_PROG_CC_STDC,
+
+Fri Nov 14 13:12:10 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NEON_LIBIDN): New macro.
+ (LIBNEON_SOURCE_CHECKS): Use NEON_LIBIDN.
+
+Fri Nov 14 11:28:58 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (NE_ENABLE_SUPPORT, NE_DISABLE_SUPPORT): New macros.
+ Use throughout to flag support or lack of support for optional
+ features.
+
Thu Nov 13 20:25:28 2003 Joe Orton <joe@manyfish.co.uk>
* neon.m4 (LIBNEON_SOURCE_CHECKS): Check for gethostbyname in
@@ -293,6 +385,11 @@ Sun May 19 20:23:55 2002 Joe Orton <joe@manyfish.co.uk>
* neon.m4 (NEON_WARNINGS): Remove with_warnings variable;
simplify.
+Wed May 19 08:36:44 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * neon.m4 (LIBNEON_SOURCE_CHECKS): Declare stpcpy on modern
+ "Linux-like" AIXes.
+
Sun May 19 09:35:08 2002 Joe Orton <joe@manyfish.co.uk>
* neon.m4 (NE_FIND_AR): Fix $PATH handling on some Linux
diff --git a/neon/macros/neon-test.m4 b/neon/macros/neon-test.m4
index 7a9fa23bc..aec38dd96 100644
--- a/neon/macros/neon-test.m4
+++ b/neon/macros/neon-test.m4
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2002 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*-
+# Copyright (C) 2001-2004 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*-
#
# This file is free software; you may copy and/or distribute it with
# or without modifications, as long as this notice is preserved.
@@ -29,7 +29,7 @@ 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_HEADERS(sys/time.h stdint.h)
AC_CHECK_FUNCS(pipe isatty usleep shutdown)
diff --git a/neon/macros/neon-xml-parser.m4 b/neon/macros/neon-xml-parser.m4
index 38a804e16..7fb033b4b 100644
--- a/neon/macros/neon-xml-parser.m4
+++ b/neon/macros/neon-xml-parser.m4
@@ -86,14 +86,14 @@ 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]))
+AS_HELP_STRING([--with-libxml2], [force use of libxml 2.x]))
AC_ARG_WITH([expat],
-AC_HELP_STRING([--with-expat], [force use of expat]))
+AS_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]),,
+AS_HELP_STRING([--with-included-expat], [use bundled expat sources]),,
with_included_expat=no)],
with_included_expat=no)
diff --git a/neon/macros/neon.m4 b/neon/macros/neon.m4
index 0d7401476..6baef5157 100644
--- a/neon/macros/neon.m4
+++ b/neon/macros/neon.m4
@@ -1,4 +1,5 @@
# Copyright (C) 1998-2004 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*-
+# Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org>
#
# This file is free software; you may copy and/or distribute it with
# or without modifications, as long as this notice is preserved.
@@ -88,7 +89,7 @@ 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]),
+AS_HELP_STRING([--with-included-neon], [force use of included neon library]),
[neon_force_included="$withval"], [neon_force_included="no"])
NEON_COMMON
@@ -121,9 +122,9 @@ AC_DEFUN([NEON_VERSIONS], [
# Define the current versions.
NEON_VERSION_MAJOR=0
-NEON_VERSION_MINOR=24
-NEON_VERSION_RELEASE=7
-NEON_VERSION_TAG=
+NEON_VERSION_MINOR=25
+NEON_VERSION_RELEASE=0
+NEON_VERSION_TAG=-dev
NEON_VERSION="${NEON_VERSION_MAJOR}.${NEON_VERSION_MINOR}.${NEON_VERSION_RELEASE}${NEON_VERSION_TAG}"
@@ -173,15 +174,16 @@ else
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]))
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[#include <ne_utils.h>]], [[ne_version_match(0, 0);]])],
+ [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))
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([[#include <ne_utils.h>]],
+ [[return ne_version_match($neon_require_major, $neon_require_minor);]])],
+ [ne_cv_lib_neonver=yes], [ne_cv_lib_neonver=no], [ne_cv_lib_neonver=no])])
fi
ne_goodver=$ne_cv_lib_neonver
LIBS=$ne_save_LIBS
@@ -195,17 +197,38 @@ else
$2
fi])
-dnl NEON_CHECK_SUPPORT(feature, var)
+dnl NEON_CHECK_SUPPORT(feature, var, name)
AC_DEFUN([NEON_CHECK_SUPPORT], [
if $NEON_CONFIG --support $1 >/dev/null; then
- neon_$1_message="supported by neon"
- $2=yes
+ NE_ENABLE_SUPPORT($2, [$3 is supported by neon])
else
- neon_$1_message="not supported by neon"
- $2=no
+ NE_DISABLE_SUPPORT($2, [$3 is not supported by neon])
fi
])
+dnl enable support for feature $1 with define $2, message $2
+AC_DEFUN([NE_ENABLE_SUPPORT], [
+NE_FLAG_$1=yes
+AC_SUBST(NE_FLAG_$1)
+AC_DEFINE([NE_HAVE_]$1, 1, [Defined if $1 is supported])
+m4_if([$2], [],
+ [ne_$1_message="support enabled"
+ AC_MSG_NOTICE([$1 support is enabled])],
+ [ne_$1_message="$2"
+ AC_MSG_NOTICE([$2])])
+])
+
+dnl Disable support for feature $1 with define $1, message $3
+AC_DEFUN([NE_DISABLE_SUPPORT], [
+NE_FLAG_$1=no
+AC_SUBST(NE_FLAG_$1)
+m4_if([$2], [],
+ [ne_$1_message="not supported"
+ AC_MSG_NOTICE([$1 support is not enabled])],
+ [ne_$1_message="$2"
+ AC_MSG_NOTICE([$2])])
+])
+
AC_DEFUN([NEON_USE_EXTERNAL], [
# Configure to use an external neon, given a neon-config script
# found at $NEON_CONFIG.
@@ -215,8 +238,12 @@ NEON_CHECK_VERSION([
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_CHECK_SUPPORT([ssl], [SSL], [SSL])
+ NEON_CHECK_SUPPORT([zlib], [ZLIB], [zlib])
+ NEON_CHECK_SUPPORT([idna], [IDNA], [IDNA])
+ NEON_CHECK_SUPPORT([ipv6], [IPV6], [IPv6])
+ NEON_CHECK_SUPPORT([lfs], [LFS], [LFS])
+ NEON_CHECK_SUPPORT([socks], [SOCKS], [SOCKSv5])
neon_got_library=yes
], [neon_got_library=no])
])
@@ -311,21 +338,27 @@ 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"], [
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([], [[$1();]])],
+ [ne_cv_libsfor_$1="none needed"], [
ne_sl_save_LIBS=$LIBS
ne_cv_libsfor_$1="not found"
for lib in $2; do
LIBS="$ne_sl_save_LIBS -l$lib $NEON_LIBS"
- AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib"; break])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [[$1();]])],
+ [ne_cv_libsfor_$1="-l$lib"; break])
m4_if($3, [], [], dnl If $3 is specified, then...
[LIBS="$ne_sl_save_LIBS -l$lib $3 $NEON_LIBS"
- AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib $3"; break])])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [[$1();]])],
+ [ne_cv_libsfor_$1="-l$lib $3"; break])])
done
LIBS=$ne_sl_save_LIBS])])
if test "$ne_cv_libsfor_$1" = "not found"; then
- m4_if($4, [], [AC_MSG_ERROR([could not find library containing $1])], [$4])
-elif test "$ne_cv_libsfor_$1" != "none needed"; then
+ m4_if([$4], [], [AC_MSG_ERROR([could not find library containing $1])], [$4])
+elif test "$ne_cv_libsfor_$1" = "none needed"; then
+ m4_if([$5], [], [:], [$5])
+else
NEON_LIBS="$ne_cv_libsfor_$1 $NEON_LIBS"
$5
fi])
@@ -333,23 +366,18 @@ 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]),
+AC_ARG_WITH(zlib, AS_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"])
+ NE_ENABLE_SUPPORT(ZLIB, [zlib support enabled, using -lz])
+ ], [NE_DISABLE_SUPPORT(ZLIB, [zlib library not found])])
+ ], [NE_DISABLE_SUPPORT(ZLIB, [zlib header not found])])
else
- neon_zlib_message="zlib disabled"
+ NE_DISABLE_SUPPORT(ZLIB, [zlib not enabled])
fi
])
@@ -372,8 +400,6 @@ AC_DEFUN([NEON_COMMON_CHECKS], [
# 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])
@@ -403,8 +429,8 @@ if test "$GCC" = "yes"; then
# 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);],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#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
@@ -413,6 +439,39 @@ else
fi
])
+dnl Check for LFS support
+AC_DEFUN([NE_LARGEFILE], [
+dnl Need the size of off_t
+AC_REQUIRE([NEON_COMMON_CHECKS])
+
+if test -z "$ac_cv_sizeof_off_t"; then
+ NE_DISABLE_SUPPORT(LFS, [LFS support omitted: off_t size unknown!])
+elif test $ac_cv_sizeof_off_t != 4; then
+ NE_DISABLE_SUPPORT(LFS, [LFS support unnecessary, off_t is not 32-bit])
+ AC_CHECK_FUNCS([strtoll strtoq], [break])
+elif test -z "$ac_cv_sizeof_long_long"; then
+ NE_DISABLE_SUPPORT(LFS, [LFS support omitted: long long size unknown])
+elif test $ac_cv_sizeof_long_long != 8; then
+ NE_DISABLE_SUPPORT(LFS, [LFS support omitted: long long not 64-bit])
+else
+ ne_save_CPPFLAGS=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE64_SOURCE"
+ AC_CHECK_TYPE(off64_t, [
+ NEON_FORMAT(off64_t)
+ ne_lfsok=no
+ AC_CHECK_FUNCS([strtoll strtoq], [ne_lfsok=yes; break])
+ AC_CHECK_FUNCS([lseek64 fstat64], [], [ne_lfsok=no; break])
+ if test x$ne_lfsok = xyes; then
+ NE_ENABLE_SUPPORT(LFS, [LFS (large file) support enabled])
+ NEON_CFLAGS="$NEON_CFLAGS -D_LARGEFILE64_SOURCE -DNE_LFS"
+ ne_save_CPPFLAGS="$CPPFLAGS -DNE_LFS"
+ else
+ NE_DISABLE_SUPPORT(LFS,
+ [LFS support omitted: 64-bit support functions not found])
+ fi], [NE_DISABLE_SUPPORT(LFS, [LFS support omitted: off64_t type not found])])
+ CPPFLAGS=$ne_save_CPPFLAGS
+fi])
+
dnl NEON_FORMAT(TYPE[, HEADERS[, [SPECIFIER]])
dnl
dnl This macro finds out which modifier is needed to create a
@@ -436,11 +495,11 @@ 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>
+ dnl obscured for m4 quoting: "for str in d ld lld; do"
+ for str in ne_spec l]ne_spec[ ll]ne_spec[; do
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
$2
-#include <stdio.h>], [$1 i = 1; printf("%$str", i);],
+#include <stdio.h>]], [[$1 i = 1; printf("%$str", i);]])],
[ne_cv_fmt_$1=$str; break])
done
CPPFLAGS=$oflags
@@ -481,47 +540,74 @@ AC_REQUIRE([AC_C_BIGENDIAN])
dnl Is strerror_r present; if so, which variant
AC_REQUIRE([AC_FUNC_STRERROR_R])
-AC_CHECK_HEADERS([strings.h sys/time.h limits.h sys/select.h arpa/inet.h \
- signal.h sys/socket.h netinet/in.h netinet/tcp.h netdb.h])
+AC_CHECK_HEADERS([sys/time.h limits.h sys/select.h arpa/inet.h \
+ signal.h sys/socket.h netinet/in.h netinet/tcp.h netdb.h sys/poll.h],,,
+[AC_INCLUDES_DEFAULT
+/* netinet/tcp.h requires netinet/in.h on some platforms. */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif])
AC_REQUIRE([NE_SNPRINTF])
+dnl Check for large file support
+NE_LARGEFILE
+
AC_REPLACE_FUNCS(strcasecmp)
-AC_CHECK_FUNCS(signal setvbuf setsockopt stpcpy)
+AC_CHECK_FUNCS(signal setvbuf setsockopt stpcpy poll)
+
+if test "x${ac_cv_func_poll}${ac_cv_header_sys_poll_h}y" = "xyesyesy"; then
+ AC_DEFINE([NE_USE_POLL], 1, [Define if poll() should be used])
+fi
if test "$ac_cv_func_stpcpy" = "yes"; then
AC_CHECK_DECLS(stpcpy)
fi
# Modern AIXes with the "Linux-like" libc have an undeclared stpcpy
-AH_BOTTOM([#if defined(HAVE_STPCPY) && !HAVE_DECL_STPCPY && !defined(stpcpy)
+AH_BOTTOM([#if defined(HAVE_STPCPY) && defined(HAVE_DECL_STPCPY) && !HAVE_DECL_STPCPY && !defined(stpcpy)
char *stpcpy(char *, const char *);
#endif])
+# Enable getaddrinfo support if it, gai_strerror and inet_ntop are
+# all available. Solaris etc hide things in -lsocket, use that too.
+
# Unixware 7 can only link gethostbyname with -lnsl -lsocket
# Pick up -lsocket first, then the gethostbyname check will work.
# QNX has gethostbyname in -lsocket. BeOS only has it in -lbind.
-NE_SEARCH_LIBS(socket, socket inet)
+# CygWin/Winsock2 has it in -lws2_32, allegedly.
+NE_SEARCH_LIBS(socket, socket inet ws2_32)
NE_SEARCH_LIBS(gethostbyname, socket nsl bind)
-# Enable getaddrinfo() support only if all the necessary functions
-# are found.
-ne_enable_gai=yes
-NE_CHECK_FUNCS(getaddrinfo gai_strerror inet_ntop,,[ne_enable_gai=no; break])
+NE_SEARCH_LIBS(getaddrinfo, nsl,,
+ [ne_enable_gai=no],
+ [# HP-UX boxes commonly get into a state where getaddrinfo is present
+ # but borked: http://marc.theaimsgroup.com/?l=apr-dev&m=107730955207120&w=2
+ case x`uname -sr 2>/dev/null`y in
+ xHP-UX*11.[[01]]*y)
+ AC_MSG_NOTICE([getaddrinfo support disabled on HP-UX 11.0x/11.1x]) ;;
+ *)
+ ne_enable_gai=yes
+ NE_CHECK_FUNCS(gai_strerror inet_ntop,,[ne_enable_gai=no; break]) ;;
+ esac
+])
+
if test $ne_enable_gai = yes; then
+ NE_ENABLE_SUPPORT(IPV6, [IPv6 support is enabled])
AC_DEFINE(USE_GETADDRINFO, 1, [Define if getaddrinfo() should be used])
AC_CACHE_CHECK([for working AI_ADDRCONFIG], [ne_cv_gai_addrconfig], [
AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <netdb.h>],
[struct addrinfo hints = {0}, *result;
hints.ai_flags = AI_ADDRCONFIG;
if (getaddrinfo("localhost", NULL, &hints, &result) != 0) return 1;])],
- ne_cv_gai_addrconfig=yes, ne_cv_gai_addrconfig=no)])
+ ne_cv_gai_addrconfig=yes, ne_cv_gai_addrconfig=no, ne_cv_gai_addrconfig=no)])
if test $ne_cv_gai_addrconfig = yes; then
AC_DEFINE(USE_GAI_ADDRCONFIG, 1, [Define if getaddrinfo supports AI_ADDRCONFIG])
fi
else
# Checks for non-getaddrinfo() based resolver interfaces.
+ NE_SEARCH_LIBS(gethostbyname, nsl bind)
NE_SEARCH_LIBS(hstrerror, resolv,,[:])
NE_CHECK_FUNCS(hstrerror)
# Older Unixes don't declare h_errno.
@@ -529,16 +615,16 @@ else
#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>])
+AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.__tm_gmtoff],,,
+ [#include <time.h>])
-ifdef([neon_no_zlib], [
- neon_zlib_message="zlib disabled"
- NEON_SUPPORTS_ZLIB=no
-], [
- NEON_ZLIB()
-])
+if test $ac_cv_member_struct_tm_tm_gmtoff$ac_cv_member_struct_tm___tm_gmtoff = nono; then
+ AC_MSG_WARN([no timezone handling in date parsing on this platform])
+fi
+
+ifdef([neon_no_zlib],
+ [NE_DISABLE_SUPPORT(ZLIB, [zlib not supported])],
+ [NEON_ZLIB()])
# Conditionally enable ACL support
AC_MSG_CHECKING([whether to enable ACL support in neon])
@@ -551,6 +637,7 @@ fi
NEON_SSL()
NEON_SOCKS()
+NEON_LIBIDN()
NEON_GSSAPI()
AC_SUBST(NEON_CFLAGS)
@@ -601,27 +688,17 @@ 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)
+done
-dnl Did they want DAV support?
+# Was DAV support explicitly turned off?
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])
+ NE_DISABLE_SUPPORT(DAV, [WebDAV support is not enabled])
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)
-
+ NE_ENABLE_SUPPORT(DAV, [WebDAV support is enabled])
fi
AC_SUBST(NEON_TARGET)
@@ -629,7 +706,6 @@ AC_SUBST(NEON_OBJEXT)
AC_SUBST(NEONOBJS)
AC_SUBST(NEON_EXTRAOBJS)
AC_SUBST(NEON_LINK_FLAGS)
-AC_SUBST(NEON_SUPPORTS_DAV)
])
@@ -677,16 +753,16 @@ AC_CHECK_FUNCS(snprintf vsnprintf,,[
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_DEFINE(HAVE_TRIO, 1, [Use trio printf replacement library])],
[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 Usage: NE_CHECK_OPENSSLVER(variable, version-string, version-hex)
dnl Define 'variable' to 'yes' if OpenSSL version is >= version-hex
-AC_DEFUN([NE_CHECK_SSLVER], [
+AC_DEFUN([NE_CHECK_OPENSSLVER], [
AC_CACHE_CHECK([OpenSSL version is >= $2], $1, [
AC_EGREP_CPP(good, [#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= $3
@@ -719,17 +795,22 @@ else
fi
fi])
-dnl Check for OpenSSL
+dnl Check for an SSL library (GNU TLS or OpenSSL)
AC_DEFUN([NEON_SSL], [
-AC_ARG_WITH(ssl, [AC_HELP_STRING([--with-ssl], [enable OpenSSL support])])
+AC_ARG_WITH(ssl,
+ AS_HELP_STRING([--with-ssl=openssl|gnutls],
+ [enable SSL support (default OpenSSL)]))
AC_ARG_WITH(egd,
[[ --with-egd[=PATH] enable EGD support [using EGD socket at PATH]]])
case $with_ssl in
-yes)
-
+/*)
+ AC_MSG_NOTICE([to use SSL libraries in non-standard locations, try --with-ssl --with-libs=$with_ssl])
+ AC_MSG_ERROR([--with-ssl does not take a path argument])
+ ;;
+yes|openssl)
NE_PKG_CONFIG(NE_SSL, openssl,
[AC_MSG_NOTICE(using SSL library configuration from pkg-config)
CPPFLAGS="$CPPFLAGS ${NE_SSL_CFLAGS}"
@@ -742,17 +823,17 @@ yes)
[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)
+ NE_CHECK_OPENSSLVER(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)"
+ NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using 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)
+ NE_CHECK_OPENSSLVER(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)"
+ NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using OpenSSL (0.9.6 or later)])
case "$with_egd" in
yes|no) ne_cv_lib_sslegd=$with_egd ;;
@@ -774,22 +855,63 @@ yes)
fi
fi
- NEON_SUPPORTS_SSL=yes
- NEON_CFLAGS="$NEON_CFLAGS -DNEON_SSL"
+ AC_DEFINE([HAVE_OPENSSL], 1, [Define if OpenSSL support is enabled])
NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_openssl"
;;
+gnutls)
+ AC_PATH_PROG(GNUTLS_CONFIG, libgnutls-config, no)
+
+ if test "$GNUTLS_CONFIG" = "no"; then
+ AC_MSG_ERROR([could not find libgnutls-config in \$PATH])
+ fi
+
+ ne_gnutls_ver=`$GNUTLS_CONFIG --version`
+ case $ne_gnutls_ver in
+ 1.*) ;;
+ *) AC_MSG_ERROR([GNU TLS major version "$ne_gnutls_ver" not supported]) ;;
+ esac
+
+ CPPFLAGS="$CPPFLAGS `$GNUTLS_CONFIG --cflags`"
+
+ AC_CHECK_HEADER([gnutls/gnutls.h],,
+ [AC_MSG_ERROR([could not find gnutls/gnutls.h in include path])])
+
+ NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using GnuTLS $ne_gnutls_ver])
+ NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_gnutls"
+ NEON_LIBS="$NEON_LIBS `$GNUTLS_CONFIG --libs`"
+ AC_DEFINE([HAVE_GNUTLS], 1, [Define if GnuTLS support is enabled])
+ ;;
*) # Default to off; only create crypto-enabled binaries if requested.
- neon_ssl_message="No SSL support"
- NEON_SUPPORTS_SSL=no
+ NE_DISABLE_SUPPORT(SSL, [SSL support is not enabled])
NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_stubssl"
;;
esac
AC_SUBST(NEON_SUPPORTS_SSL)
])
+dnl Check for GNU libidn
+AC_DEFUN([NEON_LIBIDN], [
+AC_ARG_WITH(libidn, AS_HELP_STRING(--without-libidn, disable IDNA support))
+if test "$with_libidn" != "no"; then
+ ne_use_idna=no
+ AC_CHECK_HEADER(idna.h,
+ [NE_SEARCH_LIBS(idna_to_ascii_8z,idn,,,[ne_use_idna=yes])])
+ if test $ne_use_idna = yes; then
+ NE_ENABLE_SUPPORT(IDNA, [IDNA support enabled using GNU libidn])
+ else
+ NE_DISABLE_SUPPORT(IDNA,
+ [IDNA support not enabled; GNU libidn >=0.2.0 required])
+ fi
+fi])
+
dnl Check for Kerberos installation
AC_DEFUN([NEON_GSSAPI], [
-AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin)
+AC_ARG_WITH(gssapi, AS_HELP_STRING(--without-gssapi, disable GSSAPI support))
+if test "$with_gssapi" != "no"; then
+ AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin)
+else
+ KRB5_CONFIG=none
+fi
if test "x$KRB5_CONFIG" != "xnone"; then
ne_save_CPPFLAGS=$CPPFLAGS
ne_save_LIBS=$NEON_LIBS
@@ -803,7 +925,7 @@ if test "x$KRB5_CONFIG" != "xnone"; then
AC_MSG_NOTICE([GSSAPI authentication support enabled])
AC_DEFINE(HAVE_GSSAPI, 1, [Define if GSSAPI support is enabled])
AC_CHECK_HEADERS(gssapi/gssapi_generic.h)
- # MIT Kerberos lacks GSS_C_NT_HOSTBASED_SERVICE
+ # Older versions of MIT Kerberos lack GSS_C_NT_HOSTBASED_SERVICE
AC_CHECK_DECL([GSS_C_NT_HOSTBASED_SERVICE],,
[AC_DEFINE([GSS_C_NT_HOSTBASED_SERVICE], gss_nt_service_name,
[Define if GSS_C_NT_HOSTBASED_SERVICE is not defined otherwise])],
@@ -825,12 +947,12 @@ 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]))
+AS_HELP_STRING(--enable-warnings, [enable compiler warnings]))
if test "$enable_warnings" = "yes"; then
case $GCC:`uname` in
yes:*)
- CFLAGS="$CFLAGS -Wall -ansi-pedantic -Wmissing-declarations -Winline -Wshadow -Wreturn-type -Wsign-compare -Wundef -Wpointer-arith -Wcast-align -Wbad-function-cast -Wimplicit-prototypes -Wformat-security"
+ CFLAGS="$CFLAGS -Wall -Wmissing-declarations -Wshadow -Wreturn-type -Wsign-compare -Wundef -Wpointer-arith -Wcast-align -Wbad-function-cast -Wformat-security"
if test -z "$with_ssl" -o "$with_ssl" = "no"; then
# OpenSSL headers fail strict prototypes checks
CFLAGS="$CFLAGS -Wstrict-prototypes"
@@ -852,7 +974,7 @@ dnl
AC_DEFUN([NEON_DEBUG], [
AC_ARG_ENABLE(debug,
-AC_HELP_STRING(--disable-debug,[disable runtime debugging messages]))
+AS_HELP_STRING(--disable-debug,[disable runtime debugging messages]))
# default is to enable debugging
case $enable_debug in
@@ -867,21 +989,21 @@ esac])
dnl Macro to optionally enable socks support
AC_DEFUN([NEON_SOCKS], [
-AC_ARG_WITH([socks], AC_HELP_STRING([--with-socks],[use SOCKSv5 library]))
+AC_ARG_WITH([socks], AS_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_CHECK_LIB(socks5, connect, [:],
[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"
+ NE_ENABLE_SUPPORT(SOCKS, [SOCKSv5 support is enabled])
NEON_LIBS="$NEON_LIBS -lsocks5"
LIBS=$ne_save_LIBS
-
+else
+ NE_DISABLE_SUPPORT(SOCKS, [SOCKSv5 support is not enabled])
fi])
AC_DEFUN([NEON_WITH_LIBS], [
diff --git a/neon/neon-config.in b/neon/neon-config.in
index 27b74d1be..8db113cc5 100644
--- a/neon/neon-config.in
+++ b/neon/neon-config.in
@@ -1,6 +1,6 @@
#! /bin/sh
# Originally from libxml, Copyright (C) Daniel Veillard
-# Modifications for neon Copyright (C) 2000-2002 Joe Orton.
+# Modifications for neon Copyright (C) 2000-2004 Joe Orton.
prefix=@prefix@
exec_prefix=@exec_prefix@
@@ -21,7 +21,7 @@ Known values for OPTION are:
--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@]
+ Known features: dav [@NE_FLAG_DAV@], ssl [@NE_FLAG_SSL@], zlib [@NE_FLAG_ZLIB@], idna [@NE_FLAG_IDNA@], ipv6 [@NE_FLAG_IPV6@], lfs [@NE_FLAG_LFS@]
EOF
@@ -87,16 +87,18 @@ while test $# -gt 0; do
shift
case "$1" in
- ssl|SSL) support @NEON_SUPPORTS_SSL@ ;;
- zlib|ZLIB) support @NEON_SUPPORTS_ZLIB@ ;;
- dav|DAV) support @NEON_SUPPORTS_DAV@ ;;
+ ssl|SSL) support @NE_FLAG_SSL@ ;;
+ zlib|ZLIB) support @NE_FLAG_ZLIB@ ;;
+ ipv6|IPV6) support @NE_FLAG_IPV6@ ;;
+ dav|DAV) support @NE_FLAG_DAV@ ;;
+ idna|IDNA) support @NE_FLAG_IDNA@ ;;
+ lfs|LFS) support @NE_FLAG_LFS@ ;;
*) support no ;;
esac
;;
*)
- usage
- exit 1
+ usage 1 1>&2
;;
esac
shift
diff --git a/neon/neon.mak b/neon/neon.mak
index de7f10a32..bd432f88d 100644
--- a/neon/neon.mak
+++ b/neon/neon.mak
@@ -63,6 +63,11 @@ ZLIB_LIBS = "$(ZLIB_SRC)\zlibdll.lib"
!ENDIF
!ENDIF
+########
+# Support for IPv6
+!IF "$(ENABLE_IPV6)" == "yes"
+IPV6_FLAGS = /D USE_GETADDRINFO
+!ENDIF
!IF "$(DEBUG_BUILD)" == ""
INTDIR = Release
@@ -78,7 +83,7 @@ TARGET = .\libneonD.lib
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)\\"
+CPP_PROJ = /c /nologo $(CFLAGS) $(WIN32_DEFS) $(EXPAT_FLAGS) $(OPENSSL_FLAGS) $(ZLIB_FLAGS) $(IPV6_FLAGS) /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\"
LIB32=link.exe -lib
LIB32_FLAGS=/nologo /out:"$(TARGET)"
diff --git a/neon/src/ChangeLog b/neon/src/ChangeLog
index 4b330c2f7..7859493c7 100644
--- a/neon/src/ChangeLog
+++ b/neon/src/ChangeLog
@@ -1,3 +1,94 @@
+Sun Sep 12 19:21:30 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_locks.c (ne_lock_refresh): Fix to pass correct userdata to
+ callbacks, and do call lk_cdata.
+
+Sun Sep 12 18:53:15 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in (libneon.a): Remove the archive first, avoiding
+ strange problems when build $(OBJECTS) change.
+
+Sun Sep 12 18:40:50 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_utils.h: Add NE_FEATURE_SOCKS.
+
+ * ne_utils.c (ne_has_support): Add NE_FEATURE_SOCKS.
+ (version_string): Use NE_HAVE_SOCKS, add NE_HAVE_IDNA.
+
+ * ne_socket.c (ne_sock_init): Use NE_HAVE_SOCKS.
+
+Sun Sep 12 17:29:54 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_utils.c (version_string): Declare as array rather than
+ pointer; include "IPv6" component as necessary
+
+Sun Sep 12 15:51:38 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_socket.c (ne_iaddr_typeof): New function.
+
+Sun Sep 12 12:00:10 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_defs.h (ne_attribute_malloc): New macro.
+
+ * ne_alloc.h: Use it to avoid warnings with older GCCs.
+
+Wed Aug 25 21:03:40 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_string.h (split_string, split_string_c, pair_string,
+ split_string_free, pair_string_free): Remove obsolete interfaces.
+
+Wed Aug 25 21:01:03 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_cookies.c, ne_cookies.h: Drop cookies support: used old spec
+ revision and wasn't very complete anyway.
+
+Wed Aug 25 20:40:26 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_socket.c: Remove ne_read, ne_write macros and just use recv
+ and send; remove unused SOCK_ERR macro.
+
+Wed Aug 25 20:27:43 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_xml.c (declare_nspaces): Drop rejection of names including a
+ colon to prevent breaking SVN deployments.
+
+Wed Aug 25 19:45:20 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_socket.c (readable_raw): Use poll where available.
+ (ne_sock_connect): Fail if not using poll and fd returned by
+ socket() is greater than FD_SETSIZE.
+
+Wed Aug 25 18:40:28 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_xml.h (ne_xml_parse): Clarify that a len=0 call is required
+ to signify end-of-document.
+
+Wed Aug 25 18:37:13 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.c (resolve_first, resolve_next): New functions.
+ (lookup_host): Use them to allow user-forced addresses.
+
+ * ne_session.c (ne_set_addrlist): New function.
+
+ * ne_private.h (struct ne_session_s): Add addrlist, numaddrs,
+ curaddr fields.
+
+Wed Aug 25 18:25:31 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_xml.c (struct ne_xml_parser_s): Add bom_pos field.
+ (ne_xml_parse): Skip over the UTF-8 Byte Order Mark since
+ the XML parsers do not support it yet.
+
+Wed Jul 7 16:07:44 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.c (do_connect): Fix ne_conn_connected status call
+ (Shameek Basu).
+
+Mon Jul 5 18:40:35 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_basic.c (ne_content_type_handler): Use us-ascii as default
+ charset for text/xml, as per RFC3280.
+
Mon Jul 5 10:56:19 2004 Joe Orton <joe@manyfish.co.uk>
* ne_compress.c (struct ne_decompress_s): Add acceptor field.
@@ -12,20 +103,16 @@ Mon Jul 5 10:52:40 2004 Joe Orton <joe@manyfish.co.uk>
(process_footer): Call the reader callback with size=0 to indicate
end-of-response for a good checksum match.
-Mon Jul 5 10:42:14 2004 Joe Orton <joe@manyfish.co.uk>
-
- * ne_compress.c (gz_destroy, gz_pre_send): New functions.
- (ne_decompress_reader): Register pre-send and destroy hooks,
- to initialize the compression context before each request attempt.
- (Justin Erenkrantz).
-
- * ne_private.h, ne_request.c (ne_kill_pre_send): New function.
-
Sat Jul 3 14:33:56 2004 Joe Orton <joe@manyfish.co.uk>
* ne_auth.c (auth_challenge): Fix to set got_qop in challenge
correctly (Hideaki Takahashi).
+Mon May 17 15:03:54 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_socket.h (ne_addr_resolve): Clarify that 'flags' must
+ be passed as zero for forwards-compat.
+
Sun May 2 21:14:14 2004 Joe Orton <joe@manyfish.co.uk>
Fix buffer overflow in RFC1036 date parser, CVE CAN-2004-0389.
@@ -35,17 +122,19 @@ Sun May 2 21:14:14 2004 Joe Orton <joe@manyfish.co.uk>
(ne_rfc1123_parse, ne_rfc1036_parse, ne_asctime_parse): Make
thread-safe; remove static buffers.
-Thu Mar 11 23:38:01 2004 Joe Orton <joe@manyfish.co.uk>
-
- * ne_openssl.c (provide_client_cert): Avoid malloc(0) when server
- sends no CA names in CertificateRequest.
- (ne_ssl_cert_write): Be paranoid and clear the OpenSSL error stack
- on write failures.
-
Sun May 2 16:59:39 2004 Joe Orton <joe@manyfish.co.uk>
* ne_dates.c [RFC1123_TEST] (main): Remove embedded test cases.
+Sun May 2 13:18:29 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in (LINK): Add -no-undefined.
+
+Fri Apr 16 22:53:59 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_xml.c (declare_nspaces, expand_qname): Don't try to include
+ document context in error strings.
+
Fri Apr 16 11:44:34 2004 Joe Orton <joe@manyfish.co.uk>
* Makefile.in (LIBS): Include NEON_LTLIBS.
@@ -77,11 +166,83 @@ Thu Apr 8 13:40:03 2004 Joe Orton <joe@manyfish.co.uk>
* ne_props.h: Don't use an anonymous enum for the proppatch
operation type, as some C++ compilers don't like it.
+Wed Apr 7 13:50:10 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.c (add_fixed_headers): Don't both sending Keep-Alive
+ header if persistent connections are disabled.
+
+Wed Apr 7 13:47:46 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (auth_challenge): Allow Negotiate challenges from
+ a proxy.
+
+Wed Apr 7 13:36:55 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (clean_session): Remove redundant assignment of
+ GSS_C_NO_CONTEXT; gss_delete_sec_context already does this.
+
+Wed Apr 7 13:33:10 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (get_gss_name): Handle failure case internally.
+ (auth_register): Updated accordingly.
+
+Wed Apr 7 13:15:57 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.c: Use strtoq to print off_t's where necessary.
+
+Wed Apr 7 11:14:24 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (get_gss_name): Take a hostname string.
+ (auth_register): Pass proxy or server hostname to get_gss_name as
+ appropriate.
+
+Wed Apr 7 11:09:50 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (continue_negotiate): If given no input token, and the
+ gssctx is not in the initial state, reset it.
+
+Mon Mar 29 17:06:49 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c: Adjust to cope with GSSAPI continuation:
+ (struct auth_session_s): Store GSSAPI context, name and mechanism.
+ (get_gss_name): Take an ne_session.
+ (continue_negotiate): Renamed from gssapi_challenge; take input
+ token, handle GSS_S_CONTINUE_NEEDED return value.
+ (verify_digest_response): Renamed from verify_response.
+ (verify_negotiate_response): New function.
+ (auth_challenge): Cope with Negotiate responses which gratuitously
+ break the auth-param grammar.
+ (ah_post_send): Handle Negotiate responses.
+ (free_auth, clean_session): Free persisted GSSAPI objects.
+ (auth_register): Initialize GSSAPI objects.
+
+Sun Mar 28 03:03:17 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (get_gss_name): Don't leak token.value.
+
Sun Mar 28 02:59:58 2004 Joe Orton <joe@manyfish.co.uk>
* ne_auth.c (get_cnonce): Only use RAND_pseudo_bytes() if the PRNG
is seeded.
+Sun Mar 28 02:47:20 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (gssapi_challenge, get_gss_name): Simplify.
+
+Sun Mar 28 02:35:48 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (request_gssapi, get_gss_name, auth_challenge):
+ Implement the Negotiate protocol rather than the obsoleted
+ GSS-Negotiate.
+ (make_gss_error): New function.
+ (gssapi_challenge): Use it for better error handling (set session
+ error string); fix memory leaks. Don't delegate credentials.
+
+Sat Mar 27 20:49:24 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (ah_post_send): Clear auth header collector buffers
+ after each request.
+
Fri Mar 26 12:16:15 2004 Joe Orton <joe@manyfish.co.uk>
* ne_socket.c (init_ssl): Just initialize the SSL library; delay
@@ -95,12 +256,72 @@ Fri Mar 26 12:01:38 2004 Joe Orton <joe@manyfish.co.uk>
* ne_utils.c: Include zlib.h before ne_*.h to fix issues
on platforms where zconf.h does "#define const".
-Fri Mar 26 11:55:06 2004 Joe Orton <joe@manyfish.co.uk>
+Thu Mar 11 23:38:01 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_openssl.c (provide_client_cert): Avoid malloc(0) when server
+ sends no CA names in CertificateRequest.
+ (ne_ssl_cert_write): Be paranoid and clear the OpenSSL error stack
+ on write failures.
+
+Sun Mar 7 11:17:04 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in (CFLAGS): Don't use NEON_CFLAGS.
+
+Mon Feb 23 23:03:08 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_string.c (ne_vsnprintf, ne_snprintf): New functions.
+
+Sun Feb 22 23:34:47 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_private.h (struct ne_session_s): Remove expect100_works field.
+
+ * ne_request.c (ne_set_request_expect100): New function.
+ (ne_begin_request): Remove req->use_expect100 manipulation.
+ (send_request): Handle enabling 100continue without a request
+ body.
+
+ * ne_session.c (ne_set_expect100): Removed function.
+
+Sun Feb 22 20:17:04 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_socket.c (error_ossl): Check for ERR_reason_error_string
+ returning NULL.
+
+Sun Feb 22 17:54:43 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_socket.c: Don't disable getaddrinfo support here.
- * ne_auth.c (get_gss_name, request_gssapi, gssapi_challenge,
- auth_challenge): Implement the Negotiate auth scheme rather than
- the obsolete GSS-Negotiate, and fix memory leaks. Only accept
- Negotiate challenges over SSL.
+Sun Feb 22 17:40:07 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_utils.h (min): Remove definition to...
+
+ * ne_uri.c (min): ...here.
+
+Sun Feb 22 17:31:35 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_props.h: Give the 'type' enum a tag name.
+
+Sun Feb 22 17:27:28 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_207.c (end_element): Strip whitespace from cdata.
+
+Sun Feb 22 16:27:58 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_auth.c (struct auth_request): Make auth_hdr, auth_info_hdr
+ fields into ne_buffer *'s.
+ (ah_collect_header): New function.
+ (ah_create): Create ne_buffers for auth_{,info_}hdr; use
+ ah_collect_header rather than ne_duplicate_header to fix handling
+ of multiple auth challenge headers.
+ (ah_post_send): Adjust for char * -> ne_buffer *.
+ (tokenize): Recognize a challenge scheme which is terminated with
+ a comma (i.e. with no challange parameters).
+ (auth_challenge): Fix handling of unrecognized challenges.
+ (ah_destroy): Destroy ne_buffers.
+
+Sun Feb 22 15:04:46 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.c (ne_set_request_body_provider64): New function.
Sun Feb 15 13:37:03 2004 Joe Orton <joe@manyfish.co.uk>
@@ -110,11 +331,112 @@ Sun Feb 15 13:37:03 2004 Joe Orton <joe@manyfish.co.uk>
* ne_openssl.c (ne_ssl_readable_dname): Convert dname strings to
UTF-8, or use "???".
+Sat Feb 14 21:57:25 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_xml.c (invalid_ncname_ch1): New macro.
+ (declare_nspaces): Use it, to reject some more invalid namespace
+ prefixes; also check for a colon anywhere in the NCName.
+ (expand_qname): Likewise for the element name.
+
+Mon Feb 9 21:38:03 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_dates.c [WIN32] (GMTOFF): Use gmt_to_local_win32;
+ (gmt_to_local_win32): New function, from Jiang Lei.
+
+Mon Jan 26 14:38:05 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_socket.c (ne_sock_connect_ssl): Check that OpenSSL version
+ matches between library at run-time and headers at compile-time.
+
+Sat Jan 24 17:49:27 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_dates.c (HAVE_STRUCT_TM___TM_GMTOFF): Alternative GMTOFF()
+ macro.
+
Sat Jan 24 16:49:30 2004 Joe Orton <joe@manyfish.co.uk>
* ne_auth.c (basic_challenge): Cast first parameter to ne_base64
to unsigned char * to fix warnings with some compilers.
+Sat Jan 3 13:17:36 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.h (ne_set_request_body_fd64): Define conditional on
+ NE_LFS.
+
+ * ne_request.c (ne_set_request_body_fd64): Likewise.
+
+Thu Jan 1 18:01:45 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.c: Use NE_HAVE_LFS not _LARGEFILE64_SOURCE in
+ conditional support for off64_t.
+
+Thu Jan 1 17:38:55 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.h [_LARGEFILE64_SOURCE] (ne_set_request_body_fd64):
+ New function.
+
+ * ne_request.c: Define ne_lseek, ne_off_t, ne_strtoff,
+ NE_OFFT_MAX, FMT_NE_OFF_T appropriately for _LARGEFILE64_SOURCE or
+ otherwise.
+ (struct ne_request_s): Use ne_off_t in place of off_t throughout.
+ (body_fd_send): Use ne_lseek; reset 'remain' after seeking.
+ (clength_hdr_handler): Use ne_off_t, ne_strtoff and NE_OFFT_MAX.
+ (set_body_length): Take an ne_off_t length parameter; use
+ FMT_NE_OFF_T to print it.
+ (ne_set_request_body_fd64): New function.
+
+ * ne_utils.h (NE_FEATURE_LFS): New feature.
+
+ * ne_utils.c (ne_has_support): Support NE_FEATURE_LFS.
+
+Mon Nov 24 20:13:14 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.c (struct ne_response): Split handling for chunked vs
+ clength-delimited responses into a union. Use off_t for storing
+ whole-length-of-response values.
+ (read_response_block, ne_read_response_block): Update accordingly.
+ (ne_begin_request): Remove unnecessary variable assignments.
+
+Sun Nov 23 16:03:22 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_request.h (ne_set_request_body_fd): Take offset and length
+ arguments, return void.
+ (ne_set_request_body_provider): Take off_t length argument.
+
+ * ne_request.c (struct ne_request_s): Store current position
+ within buffer/file used as request body source. Store request
+ body lengths using off_t type.
+ (body_string_send): Adjust for renamed fields.
+ (body_fd_send): Seek to requested offset; don't read past
+ requested body length.
+ (set_body_length): Renamed from set_body_size.
+
+ * ne_basic.c (ne_put): Determine file size here; adjust for new
+ ne_set_request_body_fd API.
+
+Sun Nov 23 15:05:12 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_basic.c, ne_basic.h: Remove two-functions-in-one,
+ ne_put_if_unmodified.
+
+Fri Nov 14 14:05:32 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_utils.c (ne_has_support): Add NE_FEATURE_IDNA.
+
+Fri Nov 14 13:11:49 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_session.c (set_hostinfo): [NE_HAVE_LIBIDN]: Use string from
+ IDNA ToAscii operation on provided hostname if successful.
+
+Fri Nov 14 11:23:16 2003 Joe Orton <joe@manyfish.co.uk>
+
+ All files: replace use of NEON_NODAV with NE_HAVE_DAV, NEON_SSL
+ with NE_HAVE_SSL, NEON_ZLIB with NE_HAVE_ZLIB. Use NE_HAVE_DAV
+ not USE_DAV_LOCKS.
+
+ * ne_utils.c (ne_has_support): New feature detection interface,
+ replaces ne_supports_ssl.
+
Thu Nov 13 20:38:28 2003 Joe Orton <joe@manyfish.co.uk>
* ne_request.c (ne_begin_request): Presume a 205 response has no
@@ -126,6 +448,42 @@ Thu Nov 13 20:31:07 2003 Joe Orton <joe@manyfish.co.uk>
request as a valid proxy auth challenge, to work around buggy
proxies.
+Tue Nov 11 21:13:18 2003 Joe Orton <joe@manyfish.co.uk>
+
+ Place library-internal symbols in the "ne__" namespace.
+
+ * ne_request.c (ne__pull_request_body): Renamed from
+ ne_pull_request_body; all callers updated.
+
+ * ne_session.c (ne__negotiate_ssl): Renamed from
+ ne_negotiate_ssl; all callers updated.
+
+Tue Nov 11 21:08:54 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_alloc.h: Mark all allocation functions as having 'malloc'
+ attribute for GCC.
+
+Tue Nov 11 20:36:12 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * ne_xml.h (ne_xml_failure): Replaces ne_xml_valid,
+ inverted and more useful return value.
+
+ * ne_xml.c (struct ne_xml_parser_s): Replace 'valid' field with
+ 'failure', with inverted logic.
+ (start_element, end_element, char_data): Check failure flag
+ appropriately. Set failure flag to return value of callback.
+ Set failure flag to positive integer on a parse error.
+ (ne_xml_create): Don't initialize failure flag.
+ (ne_xml_parse): Check/set failure flag appropriately.
+ (sax_error): Only set an error string (and the error flag)
+ if failure is zero.
+
+ * ne_207.c (ne_simple_request): Adjust to use ne_xml_failure.
+
+ * ne_locks.c (ne_lock, ne_lock_refresh): Likewise.
+
+ * ne_props.c (propfind): Likewise.
+
Wed Oct 22 22:19:19 2003 Joe Orton <joe@manyfish.co.uk>
* ne_request.c (read_response_block): Treat an EOF without clean
diff --git a/neon/src/Makefile.in b/neon/src/Makefile.in
index a0fb73ae0..76beea257 100644
--- a/neon/src/Makefile.in
+++ b/neon/src/Makefile.in
@@ -14,7 +14,7 @@ libdir = @libdir@
# Build paths
VPATH = @srcdir@
-top_builddir = ..
+top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
# Toolchain settings.
@@ -25,7 +25,7 @@ LIBTOOL = @LIBTOOL@
# Flags
CPPFLAGS = @DEFS@ @CPPFLAGS@
-CFLAGS = @CFLAGS@ @NEON_CFLAGS@
+CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@
NEON_LINK_FLAGS = @NEON_LINK_FLAGS@
# Note: don't substitute @LIBS@ in here; during a bundled
@@ -33,15 +33,14 @@ NEON_LINK_FLAGS = @NEON_LINK_FLAGS@
LIBS = @NEON_LIBS@ @NEON_LTLIBS@
COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS)
-LINK = $(LIBTOOL) --quiet --mode=link $(CC) $(LDFLAGS)
+LINK = $(LIBTOOL) --quiet --mode=link $(CC) -no-undefined $(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@
+ ne_redirect.@NEON_OBJEXT@ ne_compress.@NEON_OBJEXT@
NEON_DAVOBJS = $(NEON_BASEOBJS) \
ne_207.@NEON_OBJEXT@ ne_xml.@NEON_OBJEXT@ \
@@ -70,6 +69,7 @@ libneon.la: $(OBJECTS)
$(LINK) -rpath $(libdir) $(NEON_LINK_FLAGS) -o $@ $(OBJECTS) $(LIBS)
libneon.a: $(OBJECTS)
+ @rm -f $@
$(AR) cru $@ $(OBJECTS)
$(RANLIB) $@
@@ -102,6 +102,9 @@ ne_session.@NEON_OBJEXT@: ne_session.c ne_session.h ne_alloc.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_gnutls.@NEON_OBJEXT@: ne_gnutls.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
@@ -139,9 +142,8 @@ 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)
+
+ne_stubssl.@NEON_OBJEXT@: ne_stubssl.c $(neonreq)
diff --git a/neon/src/ne_207.c b/neon/src/ne_207.c
index 039bc6b9d..ad3b272a6 100644
--- a/neon/src/ne_207.c
+++ b/neon/src/ne_207.c
@@ -142,8 +142,12 @@ static int start_element(void *userdata, int parent,
state != ELM_href)
return NE_XML_DECLINE;
- if (state == ELM_propstat && p->start_propstat)
+ if (state == ELM_propstat && p->start_propstat) {
p->propstat = p->start_propstat(p->userdata, p->response);
+ if (p->propstat == NULL) {
+ return NE_XML_ABORT;
+ }
+ }
ne_buffer_clear(p->cdata);
@@ -158,7 +162,7 @@ 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;
+ const char *cdata = ne_shave(p->cdata->data, "\r\n\t ");
switch (state) {
case ELM_responsedescription:
@@ -318,7 +322,7 @@ int ne_simple_request(ne_session *sess, ne_request *req)
if (ret == NE_OK) {
if (ne_get_status(req)->code == 207) {
- if (!ne_xml_valid(p)) {
+ if (ne_xml_failed(p)) {
/* The parse was invalid */
ne_set_error(sess, "%s", ne_xml_get_error(p));
ret = NE_ERROR;
diff --git a/neon/src/ne_207.h b/neon/src/ne_207.h
index 65fe471fc..1ce8b0642 100644
--- a/neon/src/ne_207.h
+++ b/neon/src/ne_207.h
@@ -1,6 +1,6 @@
/*
WebDAV 207 multi-status response handling
- Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -54,7 +54,9 @@ typedef void ne_207_end_response(void *userdata, void *response,
* 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. */
+ * 'end_propstat' callback. If the start_propstat callback returns
+ * NULL, parsing is aborted (the XML parser error must be set by the
+ * 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,
diff --git a/neon/src/ne_acl.c b/neon/src/ne_acl.c
index 888cb752e..a6047e0a2 100644
--- a/neon/src/ne_acl.c
+++ b/neon/src/ne_acl.c
@@ -41,6 +41,7 @@
#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)
{
@@ -110,12 +111,12 @@ int ne_acl_set(ne_session *sess, const char *uri,
ne_request *req = ne_request_create(sess, "ACL", uri);
ne_buffer *body = acl_body(entries, numentries);
-#ifdef USE_DAV_LOCKS
+#ifdef NE_HAVE_DAV
ne_lock_using_resource(req, uri, 0);
#endif
ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
- ne_add_request_header(req, "Content-Type", "text/xml");
+ ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
ret = ne_request_dispatch(req);
ne_buffer_destroy(body);
diff --git a/neon/src/ne_alloc.h b/neon/src/ne_alloc.h
index 1da4a3806..3a3e2ecd7 100644
--- a/neon/src/ne_alloc.h
+++ b/neon/src/ne_alloc.h
@@ -1,6 +1,6 @@
/*
Replacement memory allocation handling etc.
- Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -41,11 +41,11 @@ void ne_oom_callback(void (*callback)(void));
* 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);
+void *ne_malloc(size_t size) ne_attribute_malloc;
+void *ne_calloc(size_t size) ne_attribute_malloc;
+void *ne_realloc(void *ptr, size_t s) ne_attribute_malloc;
+char *ne_strdup(const char *s) ne_attribute_malloc;
+char *ne_strndup(const char *s, size_t n) ne_attribute_malloc;
#define ne_free free
#endif
diff --git a/neon/src/ne_auth.c b/neon/src/ne_auth.c
index fd2cfb787..17c365ce7 100644
--- a/neon/src/ne_auth.c
+++ b/neon/src/ne_auth.c
@@ -50,7 +50,7 @@
#include <windows.h> /* for GetCurrentThreadId() etc */
#endif
-#ifdef NEON_SSL
+#ifdef HAVE_OPENSSL
#include <openssl/rand.h>
#endif
@@ -161,8 +161,11 @@ typedef struct {
/* This used for Basic auth */
char *basic;
#ifdef HAVE_GSSAPI
- /* This used for GSSAPI auth */
+ /* for the GSSAPI/Negotiate scheme: */
char *gssapi_token;
+ gss_ctx_id_t gssctx;
+ gss_name_t gssname;
+ gss_OID gssmech;
#endif
/* These all used for Digest auth */
char *realm;
@@ -202,7 +205,7 @@ struct auth_request {
struct ne_md5_ctx response_body;
/* Results of response-header callbacks */
- char *auth_hdr, *auth_info_hdr;
+ ne_buffer *auth_hdr, *auth_info_hdr;
};
static void clean_session(auth_session *sess)
@@ -214,6 +217,17 @@ static void clean_session(auth_session *sess)
NE_FREE(sess->opaque);
NE_FREE(sess->realm);
#ifdef HAVE_GSSAPI
+ {
+ int major;
+
+ if (sess->gssctx != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&major, sess->gssctx, GSS_C_NO_BUFFER);
+
+ if (sess->gssmech != GSS_C_NO_OID) {
+ gss_release_oid(&major, &sess->gssmech);
+ sess->gssmech = GSS_C_NO_OID;
+ }
+ }
NE_FREE(sess->gssapi_token);
#endif
}
@@ -227,7 +241,7 @@ static char *get_cnonce(void)
ne_md5_init_ctx(&hash);
-#ifdef NEON_SSL
+#ifdef HAVE_OPENSSL
if (RAND_status() == 1 && RAND_pseudo_bytes(data, sizeof data) >= 0)
ne_md5_process_bytes(data, sizeof data, &hash);
else {
@@ -260,7 +274,7 @@ static char *get_cnonce(void)
ne_md5_process_bytes(&pid, sizeof pid, &hash);
}
-#ifdef NEON_SSL
+#ifdef HAVE_OPENSSL
}
#endif
@@ -321,69 +335,162 @@ static char *request_basic(auth_session *sess)
/* Add GSSAPI authentication credentials to a request */
static char *request_gssapi(auth_session *sess)
{
- return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL);
+ if (sess->gssapi_token)
+ return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL);
+ else
+ return NULL;
}
-static int get_gss_name(gss_name_t *server, auth_session *sess)
+/* Create an GSSAPI name for server HOSTNAME; returns non-zero on
+ * error. */
+static void get_gss_name(gss_name_t *server, const char *hostname)
{
- unsigned int major_status, minor_status;
+ unsigned int major, minor;
gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
- token.value = ne_concat("HTTP@", sess->sess->server.hostname, NULL);
+ token.value = ne_concat("HTTP@", hostname, NULL);
token.length = strlen(token.value);
- major_status = gss_import_name(&minor_status, &token,
- GSS_C_NT_HOSTBASED_SERVICE,
- server);
- return GSS_ERROR(major_status) ? -1 : 0;
+ major = gss_import_name(&minor, &token, GSS_C_NT_HOSTBASED_SERVICE,
+ server);
+ ne_free(token.value);
+
+ if (GSS_ERROR(major)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: gss_import_name failed.\n");
+ *server = GSS_C_NO_NAME;
+ }
}
-/* Examine a GSSAPI auth challenge; returns 0 if a valid challenge,
- * else non-zero. */
-static int
-gssapi_challenge(auth_session *sess, struct auth_challenge *parms)
+/* Append GSSAPI error(s) for STATUS of type TYPE to BUF; prepending
+ * ": " to each error if *FLAG is non-zero, setting *FLAG after an
+ * error has been appended. */
+static void make_gss_error(ne_buffer *buf, int *flag,
+ unsigned int status, int type)
{
- gss_ctx_id_t context;
- gss_name_t server_name;
- unsigned int major_status, minor_status;
- gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
- gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int major, minor;
+ int context = 0;
+
+ do {
+ gss_buffer_desc msg;
+ major = gss_display_status(&minor, status, type,
+ GSS_C_NO_OID, &context, &msg);
+ if (major == GSS_S_COMPLETE && msg.length) {
+ if ((*flag)++) ne_buffer_append(buf, ": ", 2);
+ ne_buffer_append(buf, msg.value, msg.length);
+ }
+ if (msg.length) gss_release_buffer(&minor, &msg);
+ } while (context);
+}
- clean_session(sess);
+/* Continue a GSS-API Negotiate exchange, using input TOKEN if
+ * non-NULL. Returns non-zero on error. */
+static int continue_negotiate(auth_session *sess, const char *token)
+{
+ unsigned int major, minor;
+ gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
+ unsigned char *bintoken = NULL;
+ int ret;
+ gss_OID mech = sess->gssmech;
+
+ if (token) {
+ input.length = ne_unbase64(token, &bintoken);
+ if (input.length == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Invalid input [%s].\n",
+ token);
+ return -1;
+ }
+ input.value = bintoken;
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Continuation token [%s]\n", token);
+ }
+ else if (sess->gssctx != GSS_C_NO_CONTEXT) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Reset incomplete context.\n");
+ gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER);
+ }
- if (get_gss_name(&server_name, sess))
+ major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &sess->gssctx,
+ sess->gssname, mech,
+ GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input, &sess->gssmech, &output, NULL, NULL);
+
+ /* done with the input token. */
+ if (bintoken) ne_free(bintoken);
+
+ if (GSS_ERROR(major)) {
+ ne_buffer *err = ne_buffer_create();
+ int flag = 0;
+
+ make_gss_error(err, &flag, major, GSS_C_GSS_CODE);
+ make_gss_error(err, &flag, minor, GSS_C_MECH_CODE);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Error: %s\n", err->data);
+ ne_set_error(sess->sess, _("GSSAPI authentication error (%s)"),
+ err->data);
+ ne_buffer_destroy(err);
return -1;
+ }
- major_status = gss_init_sec_context(&minor_status,
- GSS_C_NO_CREDENTIAL,
- &context,
- server_name,
- GSS_C_NO_OID,
- 0,
- GSS_C_INDEFINITE,
- GSS_C_NO_CHANNEL_BINDINGS,
- &input_token,
- NULL,
- &output_token,
- NULL,
- NULL);
- gss_release_name(&minor_status, &server_name);
-
- if (GSS_ERROR(major_status)) {
- NE_DEBUG(NE_DBG_HTTPAUTH, "gss_init_sec_context failed.\n");
+ if (major == GSS_S_CONTINUE_NEEDED || major == GSS_S_COMPLETE) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: init_sec_context OK. (major=%d)\n",
+ major);
+ ret = 0;
+ }
+ else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Init failure %d.\n", major);
+ ret = -1;
+ }
+
+ if (major != GSS_S_CONTINUE_NEEDED) {
+ /* context no longer needed: destroy it */
+ gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER);
+ }
+
+ if (output.length) {
+ sess->gssapi_token = ne_base64(output.value, output.length);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Output token: [%s]\n",
+ sess->gssapi_token);
+ gss_release_buffer(&minor, &output);
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No output token.\n");
+ }
+
+ return ret;
+}
+
+/* Process a Negotiate challange CHALL in session SESS; returns zero
+ * if challenge is accepted. */
+static int gssapi_challenge(auth_session *sess, struct auth_challenge *chall)
+{
+ int ret = continue_negotiate(sess, chall->opaque);
+ if (ret == 0)
+ sess->scheme = auth_scheme_gssapi;
+ return ret;
+}
+
+/* Verify the header HDR in a Negotiate response. */
+static int verify_negotiate_response(auth_session *sess, char *hdr)
+{
+ char *sep, *ptr = strchr(hdr, ' ');
+
+ if (strncmp(hdr, "Negotiate", ptr - hdr) != 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Not a Negotiate response!\n");
return -1;
}
- if (output_token.length == 0)
- return -1;
+ ptr++;
- sess->gssapi_token = ne_base64(output_token.value, output_token.length);
- gss_release_buffer(&major_status, &output_token);
+ if (strlen(ptr) == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No token in Negotiate response!\n");
+ return 0;
+ }
- NE_DEBUG(NE_DBG_HTTPAUTH,
- "Base64 encoded GSSAPI challenge: %s.\n", sess->gssapi_token);
- sess->scheme = auth_scheme_gssapi;
- return 0;
+ if ((sep = strchr(ptr, ',')) != NULL)
+ *sep = '\0';
+ if ((sep = strchr(ptr, ' ')) != NULL)
+ *sep = '\0';
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Negotiate response token [%s]\n", ptr);
+ return continue_negotiate(sess, ptr);
}
#endif
@@ -532,7 +639,7 @@ static char *request_digest(auth_session *sess, struct auth_request *req)
* 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__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);
@@ -637,7 +744,8 @@ static int tokenize(char **hdr, char **key, char **value, int ischall)
*pnt = '\0';
*value = pnt + 1;
state = AFTER_EQ;
- } else if (*pnt == ' ' && ischall && *key != NULL) {
+ } else if ((*pnt == ' ' || *pnt == ',')
+ && ischall && *key != NULL) {
*value = NULL;
*pnt = '\0';
*hdr = pnt + 1;
@@ -679,8 +787,8 @@ static int tokenize(char **hdr, char **key, char **value, int ischall)
* 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)
+static int verify_digest_response(struct auth_request *req, auth_session *sess,
+ const char *value)
{
char *hdr, *pnt, *key, *val;
auth_qop qop = auth_qop_none;
@@ -842,36 +950,39 @@ static int auth_challenge(auth_session *sess, const char *value)
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) */
+ auth_scheme scheme;
+
if (strcasecmp(key, "basic") == 0) {
- NE_DEBUG(NE_DBG_HTTPAUTH, "Basic scheme.\n");
- chall->scheme = auth_scheme_basic;
+ scheme = auth_scheme_basic;
} else if (strcasecmp(key, "digest") == 0) {
- NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n");
- chall->scheme = auth_scheme_digest;
-#ifdef HAVE_GSSAPI
+ scheme = auth_scheme_digest;
+ }
+#ifdef HAVE_GSSAPI
+ /* cope with a Negotiate parameter which doesn't match the
+ * auth-param due to the broken spec. */
+ else if (chall && chall->scheme == auth_scheme_gssapi
+ && chall->opaque == NULL) {
+ chall->opaque = key;
+ continue;
} else if (strcasecmp(key, "negotiate") == 0) {
- NE_DEBUG(NE_DBG_HTTPAUTH, "GSSAPI scheme.\n");
- chall->scheme = auth_scheme_gssapi;
+ scheme = auth_scheme_gssapi;
+ }
#endif
- } else {
- NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n");
- ne_free(chall);
- challenges = NULL;
- break;
+ else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Ignoring challenge '%s'.\n", key);
+ chall = NULL;
+ continue;
}
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "New '%s' challenge.\n", key);
+ chall = ne_calloc(sizeof *chall);
+ chall->scheme = scheme;
+ chall->next = challenges;
+ challenges = chall;
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.
- */
+ /* Ignore pairs for an unknown challenge. */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Ignored pair: %s = %s\n", key, val);
continue;
}
@@ -924,15 +1035,17 @@ static int auth_challenge(auth_session *sess, const char *value)
success = 0;
#ifdef HAVE_GSSAPI
- if (strcmp(ne_get_scheme(sess->sess), "https") == 0) {
+ /* Ignore Negotiate challenges from origin servers which don't
+ * come over SSL. */
+ if (sess->spec == &ah_proxy_class || sess->context != AUTH_ANY) {
NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for GSSAPI.\n");
/* Try a GSSAPI challenge */
for (chall = challenges; chall != NULL; chall = chall->next) {
if (chall->scheme == auth_scheme_gssapi) {
- if (!gssapi_challenge(sess, chall)) {
- success = 1;
- break;
- }
+ if (!gssapi_challenge(sess, chall)) {
+ success = 1;
+ break;
+ }
}
}
}
@@ -985,12 +1098,21 @@ static int auth_challenge(auth_session *sess, const char *value)
}
/* The body reader callback. */
-static void auth_body_reader(void *cookie, const char *block, size_t length)
+static int 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);
+ return 0;
+}
+
+/* Collect auth challenges into an ne_buffer */
+static void ah_collect_header(void *userdata, const char *value)
+{
+ ne_buffer *ar = userdata;
+ if (ne_buffer_size(ar)) ne_buffer_append(ar, ", ", 2);
+ ne_buffer_zappend(ar, value);
}
static void ah_create(ne_request *req, void *session, const char *method,
@@ -1009,14 +1131,14 @@ static void ah_create(ne_request *req, void *session, const char *method,
areq->method = method;
areq->uri = uri;
areq->request = req;
+ areq->auth_hdr = ne_buffer_create();
+ areq->auth_info_hdr = ne_buffer_create();
ne_add_response_header_handler(req, sess->spec->resp_hdr,
- ne_duplicate_header, &areq->auth_hdr);
-
+ ah_collect_header, areq->auth_hdr);
ne_add_response_header_handler(req, sess->spec->resp_info_hdr,
- ne_duplicate_header,
- &areq->auth_info_hdr);
+ ah_collect_header, areq->auth_info_hdr);
sess->attempt = 0;
@@ -1035,7 +1157,7 @@ static void ah_pre_send(ne_request *r, void *cookie, ne_buffer *request)
} else {
char *value;
- NE_DEBUG(NE_DBG_HTTPAUTH, "Handling.");
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Handling auth session.\n");
req->will_handle = 1;
if (sess->qop == auth_qop_auth_int) {
@@ -1082,22 +1204,44 @@ static int ah_post_send(ne_request *req, void *cookie, const ne_status *status)
if (!areq) return NE_OK;
+#ifdef HAVE_GSSAPI
+ /* whatever happens: forget the GSSAPI token cached thus far */
+ if (sess->gssapi_token) {
+ ne_free(sess->gssapi_token);
+ sess->gssapi_token = NULL;
+ }
+#endif
+
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, "%s", _(sess->spec->fail_msg));
- ret = NE_ERROR;
- } else if ((status->code == sess->spec->status_code ||
- (status->code == 401 && sess->context == AUTH_CONNECT)) &&
- areq->auth_hdr != NULL) {
+ sess->spec->resp_hdr, areq->auth_hdr->data);
+ if (ne_buffer_size(areq->auth_info_hdr)
+ && sess->scheme == auth_scheme_digest) {
+ if (verify_digest_response(areq, sess, areq->auth_info_hdr->data)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n");
+ ne_set_error(sess->sess, "%s", _(sess->spec->fail_msg));
+ ret = NE_ERROR;
+ }
+ }
+#ifdef HAVE_GSSAPI
+ /* one must wonder... has Mr Brezak actually read RFC2617? */
+ else if (sess->scheme == auth_scheme_gssapi
+ && (status->klass == 2 || status->klass == 3)
+ && ne_buffer_size(areq->auth_hdr)) {
+ if (verify_negotiate_response(sess, areq->auth_hdr->data)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Mutual auth failed.\n");
+ ret = NE_ERROR;
+ }
+ }
+#endif /* HAVE_GSSAPI */
+ else if ((status->code == sess->spec->status_code ||
+ (status->code == 401 && sess->context == AUTH_CONNECT)) &&
+ ne_buffer_size(areq->auth_hdr)) {
/* note above: allow a 401 in response to a CONNECT request
* from a proxy since some buggy proxies send that. */
- NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge with code %d.\n", status->code);
- if (!auth_challenge(sess, areq->auth_hdr)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge (code %d).\n", status->code);
+ if (!auth_challenge(sess, areq->auth_hdr->data)) {
ret = NE_RETRY;
} else {
clean_session(sess);
@@ -1105,9 +1249,9 @@ static int ah_post_send(ne_request *req, void *cookie, const ne_status *status)
}
}
- NE_FREE(areq->auth_info_hdr);
- NE_FREE(areq->auth_hdr);
-
+ ne_buffer_clear(areq->auth_hdr);
+ ne_buffer_clear(areq->auth_info_hdr);
+
return ret;
}
@@ -1115,13 +1259,25 @@ 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);
- if (areq) ne_free(areq);
+
+ if (areq) {
+ ne_buffer_destroy(areq->auth_info_hdr);
+ ne_buffer_destroy(areq->auth_hdr);
+ ne_free(areq);
+ }
}
static void free_auth(void *cookie)
{
auth_session *sess = cookie;
+#ifdef HAVE_GSSAPI
+ if (sess->gssname != GSS_C_NO_NAME) {
+ int major;
+ gss_release_name(&major, sess->gssname);
+ }
+#endif
+
clean_session(sess);
ne_free(sess);
}
@@ -1137,10 +1293,19 @@ static void auth_register(ne_session *sess, int isproxy,
ahs->sess = sess;
ahs->spec = ahc;
- if (strcmp(ne_get_scheme(sess), "https") == 0)
+ if (strcmp(ne_get_scheme(sess), "https") == 0) {
ahs->context = isproxy ? AUTH_CONNECT : AUTH_NOTCONNECT;
- else
+#ifdef HAVE_GSSAPI
+ {
+ get_gss_name(&ahs->gssname, (isproxy ? sess->proxy.hostname
+ : sess->server.hostname));
+ ahs->gssctx = GSS_C_NO_CONTEXT;
+ ahs->gssmech = GSS_C_NO_OID;
+ }
+#endif
+ } else {
ahs->context = AUTH_ANY;
+ }
/* Register hooks */
ne_hook_create_request(sess, ah_create, ahs);
diff --git a/neon/src/ne_basic.c b/neon/src/ne_basic.c
index d3b4eb3fa..003877895 100644
--- a/neon/src/ne_basic.c
+++ b/neon/src/ne_basic.c
@@ -1,6 +1,6 @@
/*
Basic HTTP and WebDAV methods
- Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -22,6 +22,7 @@
#include "config.h"
#include <sys/types.h>
+#include <sys/stat.h> /* for struct stat */
#ifdef HAVE_STRING_H
#include <string.h>
@@ -41,13 +42,11 @@
#include "ne_basic.h"
#include "ne_207.h"
-#ifndef NEON_NODAV
+#ifdef NE_HAVE_DAV
#include "ne_uri.h"
-#endif
-
-#ifdef USE_DAV_LOCKS
#include "ne_locks.h"
#endif
+
#include "ne_dates.h"
#include "ne_i18n.h"
@@ -82,73 +81,32 @@ int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime)
/* 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);
+ ne_request *req;
+ struct stat st;
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);
+ if (fstat(fd, &st)) {
+ int errnum = errno;
+ char buf[200];
- 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;
+ ne_set_error(sess, _("Could not determine file size: %s"),
+ ne_strerror(errnum, buf, sizeof buf));
+ return NE_ERROR;
}
-
+
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
+#ifdef NE_HAVE_DAV
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? */
+ ne_lock_using_parent(req, uri);
#endif
- ne_set_request_body_fd(req, fd);
-
+ ne_set_request_body_fd(req, fd, 0, st.st_size);
+
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;
- }
- }
+ if (ret == NE_OK && ne_get_status(req)->klass != 2)
+ ret = NE_ERROR;
ne_request_destroy(req);
@@ -163,27 +121,32 @@ struct get_context {
ne_content_range *range;
};
-static void get_to_fd(void *userdata, const char *block, size_t length)
+static int get_to_fd(void *userdata, const char *block, size_t length)
{
struct get_context *ctx = userdata;
ssize_t ret;
+ if (ctx->error) {
+ return -1;
+ }
+
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;
+ return -1;
} else {
length -= ret;
block += ret;
}
}
}
+
+ return 0;
}
static int accept_206(void *ud, ne_request *req, const ne_status *st)
@@ -396,9 +359,14 @@ void ne_content_type_handler(void *userdata, const char *value)
/* 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";
+ if (ct->charset == NULL && strcasecmp(ct->type, "text") == 0) {
+ /* 3280§3.1: text/xml without charset implies us-ascii. */
+ if (strcasecmp(ct->subtype, "xml") == 0)
+ ct->charset = "us-ascii";
+ /* 2616§3.7.1: subtypes of text/ default to charset ISO-8859-1. */
+ else
+ ct->charset = "ISO-8859-1";
+ }
}
static void dav_hdr_handler(void *userdata, const char *value)
@@ -445,7 +413,7 @@ int ne_options(ne_session *sess, const char *uri,
return ret;
}
-#ifndef NEON_NODAV
+#ifdef NE_HAVE_DAV
void ne_add_depth_header(ne_request *req, int depth)
{
@@ -474,7 +442,7 @@ static int copy_or_move(ne_session *sess, int is_move, int overwrite,
ne_add_depth_header(req, depth);
}
-#ifdef USE_DAV_LOCKS
+#ifdef NE_HAVE_DAV
if (is_move) {
ne_lock_using_resource(req, src, NE_DEPTH_INFINITE);
}
@@ -509,7 +477,7 @@ int ne_delete(ne_session *sess, const char *uri)
{
ne_request *req = ne_request_create(sess, "DELETE", uri);
-#ifdef USE_DAV_LOCKS
+#ifdef NE_HAVE_DAV
ne_lock_using_resource(req, uri, NE_DEPTH_INFINITE);
ne_lock_using_parent(req, uri);
#endif
@@ -539,7 +507,7 @@ int ne_mkcol(ne_session *sess, const char *uri)
req = ne_request_create(sess, "MKCOL", real_uri);
-#ifdef USE_DAV_LOCKS
+#ifdef NE_HAVE_DAV
ne_lock_using_resource(req, real_uri, 0);
ne_lock_using_parent(req, real_uri);
#endif
@@ -551,4 +519,4 @@ int ne_mkcol(ne_session *sess, const char *uri)
return ret;
}
-#endif /* NEON_NODAV */
+#endif /* NE_HAVE_DAV */
diff --git a/neon/src/ne_basic.h b/neon/src/ne_basic.h
index 2daf91960..f9b515754 100644
--- a/neon/src/ne_basic.h
+++ b/neon/src/ne_basic.h
@@ -1,6 +1,6 @@
/*
HTTP/1.1 methods
- Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+ Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -28,58 +28,54 @@
BEGIN_NEON_DECLS
-/* PUT resource at uri, reading request body from f */
-int ne_put(ne_session *sess, const char *uri, int fd);
+/* 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);
-/* GET resource at uri, writing response body into f */
-int ne_get(ne_session *sess, const char *uri, int fd);
-
-#ifndef NEON_NODAV
-
-/* Basic WebDAV methods:
- * ne_copy: copy resoure from src to dest
- * ne_move: move resource from src to dest
- * -> if overwrite is non-zero, the destination resource
- * will be overwritten if it exists.
- * ne_delete: delete resource at uri
- * ne_mkcol: create a collection at uri (uri MUST have a trailing slash).
- */
-int ne_copy(ne_session *sess, int overwrite, const char *src, const char *dest);
-int ne_move(ne_session *sess, int overwrite, const char *src, const char *dest);
-int ne_delete(ne_session *sess, const char *uri);
-int ne_mkcol(ne_session *sess, const char *uri);
+/* 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);
#define NE_DEPTH_ZERO (0)
#define NE_DEPTH_ONE (1)
#define NE_DEPTH_INFINITE (2)
-/* Adds a Depth: header to a request */
-void ne_add_depth_header(ne_request *req, int depth);
+/* 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.
+ */
-#endif /* NEON_NODAV */
+/* 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);
-/* PUT resource at uri as above, only if it has not been modified
- * since given modtime. If server is HTTP/1.1, uses If-Unmodified-Since
- * header; guaranteed failure if resource is modified after 'modtime'.
- * If server is HTTP/1.0, HEAD's the resource first to fetch current
- * modtime; race condition if resource is modified between HEAD and PUT.
- */
-int ne_put_if_unmodified(ne_session *sess,
- const char *uri, int fd, time_t modtime);
+/* Move resource from 'src' to dest 'path'. */
+int ne_move(ne_session *sess, int overwrite,
+ const char *src, const char *dest);
-/* GET resource at uri, passing response body blocks to 'reader' */
-int ne_read_file(ne_session *sess, const char *uri,
- ne_block_reader reader, void *userdata);
+/* 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);
-/* Retrieve modification time of resource at uri, place in *modtime.
- * (uses HEAD) */
-int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime);
+/* Adds a Depth: header to a request */
+void ne_add_depth_header(ne_request *req, int depth);
+
+/* 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;
+} ne_content_type;
/* Sets (*ne_content_type)userdata appropriately.
* Caller must free ->value after use */
@@ -87,21 +83,15 @@ void ne_content_type_handler(void *userdata, const char *value);
/* Server capabilities: */
typedef struct {
- unsigned int broken_expect100:1; /* True if the server is known to
- * have broken Expect:
- * 100-continue support; Apache
- * 1.3.6 and earlier. */
-
unsigned int dav_class1; /* True if Class 1 WebDAV server */
unsigned int dav_class2; /* True if Class 2 WebDAV server */
unsigned int dav_executable; /* True if supports the 'executable'
* property a. la. mod_dav */
} ne_server_capabilities;
-/* Determines server capabilities (using OPTIONS).
- * Pass uri="*" to determine proxy server capabilities if using
- * a proxy server. */
-int ne_options(ne_session *sess, const char *uri,
+/* 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
@@ -124,12 +114,12 @@ typedef struct {
* range.start = resume_from;
* range.end = range.start + 999; (= 1000 bytes)
* fseek(myfile, resume_from, SEEK_SET);
- * ne_get_range(sess, uri, &range, myfile); */
-int ne_get_range(ne_session *sess, const char *uri,
+ * ne_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 *uri, int fd, const char *buffer);
+int ne_post(ne_session *sess, const char *path, int fd, const char *buffer);
END_NEON_DECLS
diff --git a/neon/src/ne_compress.c b/neon/src/ne_compress.c
index f35dc9e96..9b09c1968 100644
--- a/neon/src/ne_compress.c
+++ b/neon/src/ne_compress.c
@@ -33,9 +33,7 @@
#include "ne_utils.h"
#include "ne_i18n.h"
-#include "ne_private.h"
-
-#ifdef NEON_ZLIB
+#ifdef NE_HAVE_ZLIB
#include <zlib.h>
@@ -89,8 +87,7 @@ struct ne_decompress_s {
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. */
+ NE_Z_FINISHED /* stream is finished. */
} state;
};
@@ -105,7 +102,7 @@ struct ne_decompress_s {
* 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.
+ * HDR_ERROR: invalid header, give up (session error is set).
*/
static int parse_header(ne_decompress *ctx)
{
@@ -115,7 +112,6 @@ static int parse_header(ne_decompress *ctx)
h->id1, h->id2, h->cmeth, h->flags);
if (h->id1 != ID1 || h->id2 != ID2 || h->cmeth != 8) {
- ctx->state = NE_Z_ERROR;
ne_set_error(ctx->session, "Compressed stream invalid");
return HDR_ERROR;
}
@@ -131,7 +127,6 @@ static int parse_header(ne_decompress *ctx)
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;
}
@@ -147,14 +142,14 @@ static int parse_header(ne_decompress *ctx)
/* Process extra 'len' bytes of 'buf' which were received after the
* DEFLATE data. */
-static void process_footer(ne_decompress *ctx,
+static int 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;
+ return -1;
} else {
memcpy(ctx->footer + ctx->footcount, buf, len);
ctx->footcount += len;
@@ -170,10 +165,11 @@ static void process_footer(ne_decompress *ctx,
"given %lu vs computed %lu\n", crc, ctx->checksum);
ne_set_error(ctx->session,
"Checksum invalid for compressed stream");
- ctx->state = NE_Z_ERROR;
+ return -1;
}
}
}
+ return 0;
}
/* A zlib function failed with 'code'; set the session error string
@@ -197,7 +193,7 @@ static void set_zlib_error(ne_decompress *ctx, const char *msg, int code)
}
/* Inflate response buffer 'buf' of length 'len'. */
-static void do_inflate(ne_decompress *ctx, const char *buf, size_t len)
+static int do_inflate(ne_decompress *ctx, const char *buf, size_t len)
{
int ret;
@@ -236,39 +232,56 @@ static void do_inflate(ne_decompress *ctx, const char *buf, size_t len)
ctx->zstr.avail_in);
/* process the footer. */
ctx->state = NE_Z_AFTER_DATA;
- process_footer(ctx, ctx->zstr.next_in, ctx->zstr.avail_in);
+ return process_footer(ctx, ctx->zstr.next_in, ctx->zstr.avail_in);
} else if (ret != Z_OK) {
- ctx->state = NE_Z_ERROR;
set_zlib_error(ctx, _("Could not inflate data"), ret);
+ return NE_ERROR;
}
+ return 0;
}
/* Callback which is passed blocks of the response body. */
-static void gz_reader(void *ud, const char *buf, size_t len)
+static int gz_reader(void *ud, const char *buf, size_t len)
{
ne_decompress *ctx = ud;
const char *zbuf;
size_t count;
+ if (len == 0) {
+ /* End of response: */
+ switch (ctx->state) {
+ case NE_Z_BEFORE_DATA:
+ if (ctx->enchdr && strcasecmp(ctx->enchdr, "gzip") == 0) {
+ /* response was truncated: break out. */
+ break;
+ }
+ /* else, fall through */
+ case NE_Z_FINISHED: /* complete gzip response */
+ case NE_Z_PASSTHROUGH: /* complete uncompressed response */
+ return ctx->reader(ctx->userdata, buf, 0);
+ default:
+ /* invalid state: truncated response. */
+ break;
+ }
+ /* else: truncated response, fail. */
+ ne_set_error(ctx->session, "Compressed response was truncated");
+ return NE_ERROR;
+ }
+
switch (ctx->state) {
case NE_Z_PASSTHROUGH:
/* move along there. */
- ctx->reader(ctx->userdata, buf, len);
- return;
-
- case NE_Z_ERROR:
- /* beyond hope. */
- break;
+ return ctx->reader(ctx->userdata, buf, len);
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");
+ return NE_ERROR;
}
- break;
+ break;
case NE_Z_BEFORE_DATA:
/* work out whether this is a compressed response or not. */
@@ -280,7 +293,7 @@ static void gz_reader(void *ud, const char *buf, size_t len)
ret = inflateInit2(&ctx->zstr, -MAX_WBITS);
if (ret != Z_OK) {
set_zlib_error(ctx, _("Could not initialize zlib"), ret);
- return;
+ return -1;
}
ctx->zstrinit = 1;
@@ -290,8 +303,7 @@ static void gz_reader(void *ud, const char *buf, size_t len)
* 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;
+ return ctx->reader(ctx->userdata, buf, len);
}
ctx->state = NE_Z_IN_HEADER;
@@ -308,7 +320,7 @@ static void gz_reader(void *ud, const char *buf, size_t len)
ctx->incount += count;
/* have we got the full header yet? */
if (ctx->incount != 10) {
- return;
+ return 0;
}
buf += count;
@@ -317,14 +329,15 @@ static void gz_reader(void *ud, const char *buf, size_t len)
switch (parse_header(ctx)) {
case HDR_EXTENDED:
if (len == 0)
- return;
+ return 0;
break;
+ case HDR_ERROR:
+ return NE_ERROR;
case HDR_DONE:
if (len > 0) {
- do_inflate(ctx, buf, len);
+ return do_inflate(ctx, buf, len);
}
- default:
- return;
+ break;
}
/* FALLTHROUGH */
@@ -334,7 +347,7 @@ static void gz_reader(void *ud, const char *buf, size_t len)
zbuf = memchr(buf, '\0', len);
if (zbuf == NULL) {
/* not found it yet. */
- return;
+ return 0;
}
NE_DEBUG(NE_DBG_HTTP,
@@ -346,26 +359,23 @@ static void gz_reader(void *ud, const char *buf, size_t len)
ctx->state = NE_Z_INFLATING;
if (len == 0) {
/* end of string was at end of buffer. */
- return;
+ return 0;
}
/* FALLTHROUGH */
case NE_Z_INFLATING:
- do_inflate(ctx, buf, len);
- break;
+ return do_inflate(ctx, buf, len);
case NE_Z_AFTER_DATA:
- process_footer(ctx, (unsigned char *)buf, len);
- break;
+ return process_footer(ctx, (unsigned char *)buf, len);
}
+ return 0;
}
-int ne_decompress_destroy(ne_decompress *ctx)
+void ne_decompress_destroy(ne_decompress *ctx)
{
- int ret;
-
if (ctx->zstrinit)
/* inflateEnd only fails if it's passed NULL etc; ignore
* return value. */
@@ -374,50 +384,7 @@ int ne_decompress_destroy(ne_decompress *ctx)
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;
-}
-
-/* Prepare for a compressed response */
-static void gz_pre_send(ne_request *r, void *ud, ne_buffer *req)
-{
- ne_decompress *ctx = ud;
-
- NE_DEBUG(NE_DBG_HTTP, "compress: Initialization.\n");
-
- /* (Re-)Initialize the context */
- ctx->state = NE_Z_BEFORE_DATA;
- if (ctx->zstrinit) inflateEnd(&ctx->zstr);
- ctx->zstrinit = 0;
- ctx->incount = ctx->footcount = 0;
- ctx->checksum = crc32(0L, Z_NULL, 0);
- if (ctx->enchdr) {
- ne_free(ctx->enchdr);
- ctx->enchdr = NULL;
- }
-}
-
-/* Kill the pre-send hook */
-static void gz_destroy(ne_request *req, void *userdata)
-{
- ne_kill_pre_send(ne_get_session(req), gz_pre_send, userdata);
}
/* Wrapper for user-passed acceptor function. */
@@ -439,18 +406,18 @@ ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt,
ne_add_response_body_reader(req, gz_acceptor, 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);
ctx->acceptor = acpt;
- ne_hook_pre_send(ctx->session, gz_pre_send, ctx);
- ne_hook_destroy_request(ctx->session, gz_destroy, ctx);
-
return ctx;
}
-#else /* !NEON_ZLIB */
+#else /* !NE_HAVE_ZLIB */
/* Pass-through interface present to provide ABI compatibility. */
@@ -467,4 +434,4 @@ int ne_decompress_destroy(ne_decompress *dc)
return 0;
}
-#endif /* NEON_ZLIB */
+#endif /* NE_HAVE_ZLIB */
diff --git a/neon/src/ne_compress.h b/neon/src/ne_compress.h
index 9fa6b73bb..7b19687d3 100644
--- a/neon/src/ne_compress.h
+++ b/neon/src/ne_compress.h
@@ -1,6 +1,6 @@
/*
- Compressed HTPT request/response Handling
- Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+ Compressed HTTP response handling
+ Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -33,12 +33,12 @@ typedef struct ne_decompress_s ne_decompress;
*
* Returns pointer to context object which must be passed to
* ne_decompress_destroy after the request has been dispatched, to
- * free any internal state. */
+ * free any internal state. If an error occurs during decompression,
+ * the request will be aborted and session error string set. */
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);
+/* Destroys decompression state. */
+void ne_decompress_destroy(ne_decompress *ctx);
#endif /* NE_COMPRESS_H */
diff --git a/neon/src/ne_dates.c b/neon/src/ne_dates.c
index 27c35175d..1e4921acf 100644
--- a/neon/src/ne_dates.c
+++ b/neon/src/ne_dates.c
@@ -1,6 +1,7 @@
/*
Date manipulation routines
Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2004 Jiang Lei <tristone@deluxe.ocn.ne.jp>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -35,7 +36,7 @@
#include "ne_alloc.h"
#include "ne_dates.h"
-#include "ne_utils.h"
+#include "ne_string.h"
/* Generic date manipulation routines. */
@@ -61,11 +62,35 @@ static const char *short_months[12] = {
#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
#define GMTOFF(t) ((t).tm_gmtoff)
+#elif defined(HAVE_STRUCT_TM___TM_GMTOFF)
+#define GMTOFF(t) ((t).__tm_gmtoff)
+#elif defined(WIN32)
+#define GMTOFF(t) (gmt_to_local_win32())
#else
/* FIXME: work out the offset anyway. */
#define GMTOFF(t) (0)
#endif
+#ifdef WIN32
+time_t gmt_to_local_win32(void)
+{
+ TIME_ZONE_INFORMATION tzinfo;
+ DWORD dwStandardDaylight;
+ long bias;
+
+ dwStandardDaylight = GetTimeZoneInformation(&tzinfo);
+ bias = tzinfo.Bias;
+
+ if (dwStandardDaylight == TIME_ZONE_ID_STANDARD)
+ bias += tzinfo.StandardBias;
+
+ if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT)
+ bias += tzinfo.DaylightBias;
+
+ return (- bias * 60);
+}
+#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) {
diff --git a/neon/src/ne_defs.h b/neon/src/ne_defs.h
index 4dacbda1a..c2bbb220e 100644
--- a/neon/src/ne_defs.h
+++ b/neon/src/ne_defs.h
@@ -1,6 +1,6 @@
/*
Standard definitions for neon headers
- Copyright (C) 2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2003-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -35,7 +35,13 @@
#endif
#ifdef __GNUC__
+#if __GNUC__ >= 3
+#define ne_attribute_malloc __attribute__((malloc))
+#else
+#define ne_attribute_malloc
+#endif
#define ne_attribute(x) __attribute__(x)
#else
#define ne_attribute(x)
+#define ne_attribute_malloc
#endif
diff --git a/neon/src/ne_gnutls.c b/neon/src/ne_gnutls.c
new file mode 100644
index 000000000..b6f3b4366
--- /dev/null
+++ b/neon/src/ne_gnutls.c
@@ -0,0 +1,853 @@
+/*
+ neon SSL/TLS support using GNU TLS
+ Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2004, Aleix Conchillo Flaque <aleix@member.fsf.org>
+
+ 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/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/pkcs12.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 {
+ int subject; /* non-zero if this is the subject DN object */
+ gnutls_x509_crt cert;
+};
+
+struct ne_ssl_certificate_s {
+ ne_ssl_dname subj_dn, issuer_dn;
+ gnutls_x509_crt subject;
+ ne_ssl_certificate *issuer;
+ char *identity;
+};
+
+struct ne_ssl_client_cert_s {
+ gnutls_pkcs12 p12;
+ int decrypted; /* non-zero if successfully decrypted. */
+ ne_ssl_certificate cert;
+ gnutls_x509_privkey pkey;
+ char *friendly_name;
+};
+
+/* Returns the highest used index in subject (or issuer) DN of
+ * certificate CERT for OID, or -1 if no RDNs are present in the DN
+ * using that OID. */
+static int oid_find_highest_index(gnutls_x509_crt cert, int subject, const char *oid)
+{
+ int ret, idx = -1;
+
+ do {
+ size_t len = 0;
+
+ if (subject)
+ ret = gnutls_x509_crt_get_dn_by_oid(cert, oid, ++idx, 0,
+ NULL, &len);
+ else
+ ret = gnutls_x509_crt_get_issuer_dn_by_oid(cert, oid, ++idx, 0,
+ NULL, &len);
+ } while (ret == GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+ return idx - 1;
+}
+
+/* Appends the value of RDN with given oid from certitifcate x5
+ * subject (if subject is non-zero), or issuer DN to buffer 'buf': */
+static void append_rdn(ne_buffer *buf, gnutls_x509_crt x5, int subject, const char *oid)
+{
+ int idx, top, ret;
+ char rdn[50];
+
+ top = oid_find_highest_index(x5, subject, oid);
+
+ for (idx = top; idx >= 0; idx--) {
+ size_t rdnlen = sizeof rdn;
+
+ if (subject)
+ ret = gnutls_x509_crt_get_dn_by_oid(x5, oid, idx, 0, rdn, &rdnlen);
+ else
+ ret = gnutls_x509_crt_get_issuer_dn_by_oid(x5, oid, idx, 0, rdn, &rdnlen);
+
+ if (ret < 0)
+ return;
+
+ if (buf->used > 1) {
+ ne_buffer_append(buf, ", ", 2);
+ }
+
+ ne_buffer_append(buf, rdn, rdnlen);
+ }
+}
+
+
+char *ne_ssl_readable_dname(const ne_ssl_dname *name)
+{
+ ne_buffer *buf = ne_buffer_create();
+#if 0
+ /* this code can be used once there is a released version of GnuTLS
+ * with fixed _get_dn_oid functions */
+ int ret, idx = 0;
+
+ do {
+ char oid[32] = {0};
+ size_t oidlen = sizeof oid;
+
+ ret = name->subject
+ ? gnutls_x509_crt_get_dn_oid(name->cert, idx, oid, &oidlen)
+ : gnutls_x509_crt_get_issuer_dn_oid(name->cert, idx, oid, &oidlen);
+
+ if (ret == 0) {
+ append_rdn(buf, name->cert, name->subject, oid);
+ idx++;
+ }
+ } while (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+#else
+
+#define APPEND_RDN(x) append_rdn(buf, name->cert, name->subject, GNUTLS_OID_##x)
+
+ APPEND_RDN(X520_ORGANIZATIONAL_UNIT_NAME);
+ APPEND_RDN(X520_ORGANIZATION_NAME);
+ APPEND_RDN(X520_LOCALITY_NAME);
+ APPEND_RDN(X520_STATE_OR_PROVINCE_NAME);
+ APPEND_RDN(X520_COUNTRY_NAME);
+
+ if (buf->used == 1) APPEND_RDN(X520_COMMON_NAME);
+ if (buf->used == 1) APPEND_RDN(PKCS9_EMAIL);
+
+#undef APPEND_RDN
+#endif
+
+ return ne_buffer_finish(buf);
+}
+
+int ne_ssl_dname_cmp(const ne_ssl_dname *dn1, const ne_ssl_dname *dn2)
+{
+#warning incomplete
+ return 1;
+}
+
+void ne_ssl_clicert_free(ne_ssl_client_cert *cc)
+{
+ if (cc->p12)
+ gnutls_pkcs12_deinit(cc->p12);
+ if (cc->decrypted) {
+ if (cc->cert.identity) ne_free(cc->cert.identity);
+ if (cc->pkey) gnutls_x509_privkey_deinit(cc->pkey);
+ if (cc->cert.subject) gnutls_x509_crt_deinit(cc->cert.subject);
+ }
+ if (cc->friendly_name) ne_free(cc->friendly_name);
+ ne_free(cc);
+}
+
+void ne_ssl_cert_validity(const ne_ssl_certificate *cert,
+ char *from, char *until)
+{
+#warning FIXME strftime not portable
+ if (from) {
+ time_t t = gnutls_x509_crt_get_activation_time(cert->subject);
+ strftime(from, NE_SSL_VDATELEN, "%b %d %H:%M:%S %Y %Z", localtime(&t));
+ }
+ if (until) {
+ time_t t = gnutls_x509_crt_get_expiration_time(cert->subject);
+ strftime(until, NE_SSL_VDATELEN, "%b %d %H:%M:%S %Y %Z", localtime(&t));
+ }
+}
+
+/* Return non-zero if hostname from certificate (cn) matches hostname
+ * used for session (hostname). (Wildcard matching is no longer
+ * mandated by RFC3280, but certs are deployed which use wildcards) */
+static int match_hostname(char *cn, const char *hostname)
+{
+ const char *dot;
+ NE_DEBUG(NE_DBG_SSL, "Match %s on %s...\n", cn, hostname);
+ dot = strchr(hostname, '.');
+ if (dot == NULL) {
+ char *pnt = strchr(cn, '.');
+ /* hostname is not fully-qualified; unqualify the cn. */
+ if (pnt != NULL) {
+ *pnt = '\0';
+ }
+ }
+ else if (strncmp(cn, "*.", 2) == 0) {
+ hostname = dot + 1;
+ cn += 2;
+ }
+ return !strcasecmp(cn, hostname);
+}
+
+/* Check certificate identity. Returns zero if identity matches; 1 if
+ * identity does not match, or <0 if the certificate had no identity.
+ * If 'identity' is non-NULL, store the malloc-allocated identity in
+ * *identity. If 'server' is non-NULL, it must be the network address
+ * of the server in use, and identity must be NULL. */
+static int check_identity(const char *hostname, gnutls_x509_crt cert,
+ char **identity)
+{
+ char name[255];
+ unsigned int critical;
+ int ret, seq = 0;
+ int match = 0, found = 0;
+ size_t len;
+
+ do {
+ len = sizeof name;
+ ret = gnutls_x509_crt_get_subject_alt_name(cert, seq, name, &len,
+ &critical);
+ switch (ret) {
+ case GNUTLS_SAN_DNSNAME:
+ if (identity && !found) *identity = ne_strdup(name);
+ match = match_hostname(name, hostname);
+ found = 1;
+ break;
+ default:
+ break;
+ }
+ seq++;
+ } while (!match && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ /* Check against the commonName if no DNS alt. names were found,
+ * as per RFC3280. */
+ if (!found) {
+ seq = oid_find_highest_index(cert, 1, GNUTLS_OID_X520_COMMON_NAME);
+
+ if (seq >= 0) {
+ len = sizeof name;
+ name[0] = '\0';
+ ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME,
+ seq, 0, name, &len);
+ if (ret == 0) {
+ if (identity) *identity = ne_strdup(name);
+ match = match_hostname(name, hostname);
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ 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,
+ gnutls_x509_crt x5)
+{
+ cert->subj_dn.cert = x5;
+ cert->subj_dn.subject = 1;
+ cert->issuer_dn.cert = x5;
+ cert->issuer_dn.subject = 0;
+ cert->issuer = NULL;
+ cert->subject = x5;
+ cert->identity = NULL;
+ check_identity("", x5, &cert->identity);
+ return cert;
+}
+
+/* Returns a copy certificate of certificate SRC. */
+static gnutls_x509_crt x509_crt_copy(gnutls_x509_crt src)
+{
+ int ret;
+ size_t size;
+ gnutls_datum tmp;
+ gnutls_x509_crt dest;
+
+ if (gnutls_x509_crt_init(&dest) == 0) {
+ return NULL;
+ }
+
+ if (gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, NULL, &size)
+ != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ gnutls_x509_crt_deinit(dest);
+ return NULL;
+ }
+
+ tmp.data = ne_malloc(size);
+ ret = gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, tmp.data, &size);
+ if (ret == 0) {
+ tmp.size = size;
+ ret = gnutls_x509_crt_import(dest, &tmp, GNUTLS_X509_FMT_DER);
+ }
+
+ if (ret) {
+ gnutls_x509_crt_deinit(dest);
+ dest = NULL;
+ }
+
+ ne_free(tmp.data);
+ return dest;
+}
+
+/* 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)
+{
+ int ret;
+ ne_ssl_client_cert *newcc = ne_calloc(sizeof *newcc);
+
+ newcc->decrypted = 1;
+
+ ret = gnutls_x509_privkey_init(&newcc->pkey);
+ if (ret != 0) goto dup_error;
+
+ ret = gnutls_x509_privkey_cpy(newcc->pkey, cc->pkey);
+ if (ret != 0) goto dup_error;
+
+ newcc->cert.subject = x509_crt_copy(cc->cert.subject);
+ if (!newcc->cert.subject) goto dup_error;
+
+ if (cc->friendly_name) newcc->friendly_name = ne_strdup(cc->friendly_name);
+
+ populate_cert(&newcc->cert, newcc->cert.subject);
+ return newcc;
+
+dup_error:
+ if (newcc->pkey) gnutls_x509_privkey_deinit(newcc->pkey);
+ if (newcc->cert.subject) gnutls_x509_crt_deinit(newcc->cert.subject);
+ ne_free(newcc);
+ return NULL;
+}
+
+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(int flags)
+{
+ ne_ssl_context *ctx = ne_malloc(sizeof *ctx);
+ gnutls_certificate_allocate_credentials(&ctx->cred);
+ return ctx;
+}
+
+int ne_ssl_context_keypair(ne_ssl_context *ctx,
+ const char *cert, const char *key)
+{
+ gnutls_certificate_set_x509_key_file(ctx->cred, cert, key,
+ GNUTLS_X509_FMT_PEM);
+ return 0;
+}
+
+int ne_ssl_context_set_verify(ne_ssl_context *ctx, int required,
+ const char *ca_names, const char *verify_cas)
+{
+ if (verify_cas) {
+ gnutls_certificate_set_x509_trust_file(ctx->cred, verify_cas,
+ GNUTLS_X509_FMT_PEM);
+ }
+#warning argh
+ return 0;
+}
+
+
+void ne_ssl_context_destroy(ne_ssl_context *ctx)
+{
+ gnutls_certificate_free_credentials(ctx->cred);
+ ne_free(ctx);
+}
+
+/* Return the certificate chain sent by the peer, or NULL on error. */
+static ne_ssl_certificate *make_peers_chain(gnutls_session sock)
+{
+ ne_ssl_certificate *current = NULL, *top = NULL;
+ const gnutls_datum *certs;
+ unsigned int n, count;
+
+ certs = gnutls_certificate_get_peers(sock, &count);
+ if (!certs) {
+ return NULL;
+ }
+
+ for (n = 0; n < count; n++) {
+ ne_ssl_certificate *cert = ne_malloc(sizeof *cert);
+ gnutls_x509_crt x5;
+
+ if (gnutls_x509_crt_init(&x5) ||
+ gnutls_x509_crt_import(x5, &certs[n], GNUTLS_X509_FMT_DER)) {
+ /* leak! */
+ return NULL;
+ }
+
+ populate_cert(cert, x5);
+
+ 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, gnutls_session sock,
+ ne_ssl_certificate *chain)
+{
+ time_t before, after, now = time(NULL);
+ int ret, failures = 0;
+
+ before = gnutls_x509_crt_get_activation_time(chain->subject);
+ after = gnutls_x509_crt_get_expiration_time(chain->subject);
+
+ if (now < before)
+ failures |= NE_SSL_NOTYETVALID;
+ else if (now > after)
+ failures |= NE_SSL_EXPIRED;
+
+ ret = check_identity(sess->server.hostname, chain->subject, 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;
+ }
+
+ if (gnutls_certificate_verify_peers(sock)) {
+ failures |= NE_SSL_UNTRUSTED;
+ }
+
+ NE_DEBUG(NE_DBG_SSL, "Failures = %d\n", failures);
+
+ if (failures == 0) {
+ ret = NE_OK;
+ } else {
+#warning TODO: set up error string
+ ret = NE_ERROR;
+ if (sess->ssl_verify_fn
+ && sess->ssl_verify_fn(sess->ssl_verify_ud, failures, chain) == 0)
+ ret = NE_OK;
+ }
+
+ return ret;
+}
+
+/* Negotiate an SSL connection. */
+int ne__negotiate_ssl(ne_request *req)
+{
+ ne_session *const sess = ne_get_session(req);
+ ne_ssl_context *const ctx = sess->ssl_context;
+ ne_ssl_certificate *chain;
+ gnutls_session sock;
+
+ NE_DEBUG(NE_DBG_SSL, "Negotiating SSL connection.\n");
+
+ if (ne_sock_connect_ssl(sess->socket, ctx)) {
+ ne_set_error(sess, _("SSL negotiation failed: %s"),
+ ne_sock_error(sess->socket));
+ return NE_ERROR;
+ }
+
+ sock = ne__sock_sslsock(sess->socket);
+
+ chain = make_peers_chain(sock);
+ if (chain == NULL) {
+ ne_set_error(sess, _("Server did not send certificate chain"));
+ return NE_ERROR;
+ }
+
+ if (check_certificate(sess, sock, chain)) {
+ ne_ssl_cert_free(chain);
+ return NE_ERROR;
+ }
+
+ 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_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert)
+{
+ gnutls_x509_crt certs = cert->subject;
+ gnutls_certificate_set_x509_trust(ctx->cred, &certs, 1);
+}
+
+void ne_ssl_trust_default_ca(ne_session *sess)
+{
+#warning incomplete
+}
+
+/* Read the contents of file FILENAME into *DATUM. */
+static int read_to_datum(const char *filename, gnutls_datum *datum)
+{
+ FILE *f = fopen(filename, "r");
+ ne_buffer *buf;
+ char tmp[4192];
+ size_t len;
+
+ if (!f) {
+ return -1;
+ }
+
+ buf = ne_buffer_ncreate(8192);
+ while ((len = fread(tmp, 1, sizeof tmp, f)) > 0) {
+ ne_buffer_append(buf, tmp, len);
+ }
+
+ if (!feof(f)) {
+ ne_buffer_destroy(buf);
+ return -1;
+ }
+
+ datum->size = ne_buffer_size(buf);
+ datum->data = ne_buffer_finish(buf);
+ return 0;
+}
+
+/* Parses a PKCS#12 structure and loads the certificate, private key
+ * and friendly name if possible. Returns zero on success, non-zero
+ * on error. */
+static int pkcs12_parse(gnutls_pkcs12 p12, gnutls_x509_privkey *pkey,
+ gnutls_x509_crt *x5, char **friendly_name,
+ const char *password)
+{
+ gnutls_pkcs12_bag bag = NULL;
+ int i, j, ret = 0;
+
+ for (i = 0; ret == 0; ++i) {
+ if (bag) gnutls_pkcs12_bag_deinit(bag);
+
+ ret = gnutls_pkcs12_bag_init(&bag);
+ if (ret < 0) continue;
+
+ ret = gnutls_pkcs12_get_bag(p12, i, bag);
+ if (ret < 0) continue;
+
+ gnutls_pkcs12_bag_decrypt(bag, password == NULL ? "" : password);
+
+ for (j = 0; ret == 0 && j < gnutls_pkcs12_bag_get_count(bag); ++j) {
+ gnutls_pkcs12_bag_type type;
+ gnutls_datum data;
+
+ if (friendly_name && *friendly_name == NULL) {
+ char *name;
+ gnutls_pkcs12_bag_get_friendly_name(bag, j, &name);
+ if (name) {
+ if (name[0] == '.') name++; /* weird GnuTLS bug? */
+ *friendly_name = ne_strdup(name);
+ }
+ }
+
+ type = gnutls_pkcs12_bag_get_type(bag, j);
+ switch (type) {
+ case GNUTLS_BAG_PKCS8_KEY:
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ gnutls_x509_privkey_init(pkey);
+
+ ret = gnutls_pkcs12_bag_get_data(bag, j, &data);
+ if (ret < 0) continue;
+
+ ret = gnutls_x509_privkey_import_pkcs8(*pkey, &data,
+ GNUTLS_X509_FMT_DER,
+ password,
+ 0);
+ if (ret < 0) continue;
+ break;
+ case GNUTLS_BAG_CERTIFICATE:
+ gnutls_x509_crt_init(x5);
+
+ ret = gnutls_pkcs12_bag_get_data(bag, j, &data);
+ if (ret < 0) continue;
+
+ ret = gnutls_x509_crt_import(*x5, &data, GNUTLS_X509_FMT_DER);
+ if (ret < 0) continue;
+
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Make sure last bag is freed */
+ if (bag) gnutls_pkcs12_bag_deinit(bag);
+
+ /* Free in case of error */
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ if (*x5) gnutls_x509_crt_deinit(*x5);
+ if (*pkey) gnutls_x509_privkey_deinit(*pkey);
+ if (friendly_name && *friendly_name) ne_free(*friendly_name);
+ }
+
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ret = 0;
+ return ret;
+}
+
+ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename)
+{
+ int ret;
+ gnutls_datum data;
+ gnutls_pkcs12 p12;
+ ne_ssl_client_cert *cc;
+ char *friendly_name = NULL;
+ gnutls_x509_crt cert = NULL;
+ gnutls_x509_privkey pkey = NULL;
+
+ if (read_to_datum(filename, &data))
+ return NULL;
+
+ if (gnutls_pkcs12_init(&p12) != 0) {
+ return NULL;
+ }
+
+ ret = gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0);
+ ne_free(data.data);
+ if (ret < 0) {
+ gnutls_pkcs12_deinit(p12);
+ return NULL;
+ }
+
+ ret = pkcs12_parse(p12, &pkey, &cert, &friendly_name, NULL);
+
+ if (gnutls_pkcs12_verify_mac(p12, "") == 0) {
+ cc = ne_calloc(sizeof *cc);
+ cc->pkey = pkey;
+ cc->decrypted = 1;
+ cc->friendly_name = friendly_name;
+ populate_cert(&cc->cert, cert);
+ gnutls_pkcs12_deinit(p12);
+ cc->p12 = NULL;
+ return cc;
+ } else {
+ if (ret == 0) {
+ cc = ne_calloc(sizeof *cc);
+ cc->friendly_name = friendly_name;
+ cc->p12 = p12;
+ return cc;
+ } else {
+ gnutls_pkcs12_deinit(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)
+{
+ int ret;
+ gnutls_x509_crt cert = NULL;
+ gnutls_x509_privkey pkey = NULL;
+
+ if (gnutls_pkcs12_verify_mac(cc->p12, password) != 0) {
+ return -1;
+ }
+
+ ret = pkcs12_parse(cc->p12, &pkey, &cert, NULL, password);
+ if (ret < 0)
+ return ret;
+
+ gnutls_pkcs12_deinit(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)
+{
+ int ret;
+ gnutls_datum data;
+ gnutls_x509_crt x5;
+
+ if (read_to_datum(filename, &data))
+ return NULL;
+
+ if (gnutls_x509_crt_init(&x5) != 0)
+ return NULL;
+
+ ret = gnutls_x509_crt_import(x5, &data, GNUTLS_X509_FMT_PEM);
+ ne_free(data.data);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(x5);
+ return NULL;
+ }
+
+ return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), x5);
+}
+
+int ne_ssl_cert_write(const ne_ssl_certificate *cert, const char *filename)
+{
+ unsigned char buffer[10*1024];
+ int len = sizeof buffer;
+
+ FILE *fp = fopen(filename, "w");
+
+ if (fp == NULL) return -1;
+
+ if (gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_PEM, buffer,
+ &len) < 0) {
+ fclose(fp);
+ return -1;
+ }
+
+ if (fwrite(buffer, len, 1, fp) != 1) {
+ fclose(fp);
+ return -1;
+ }
+
+ if (fclose(fp) != 0)
+ return -1;
+
+ return 0;
+}
+
+void ne_ssl_cert_free(ne_ssl_certificate *cert)
+{
+ gnutls_x509_crt_deinit(cert->subject);
+ if (cert->identity) ne_free(cert->identity);
+ if (cert->issuer) ne_ssl_cert_free(cert->issuer);
+ ne_free(cert);
+}
+
+int ne_ssl_cert_cmp(const ne_ssl_certificate *c1, const ne_ssl_certificate *c2)
+{
+ char digest1[NE_SSL_DIGESTLEN], digest2[NE_SSL_DIGESTLEN];
+
+ if (ne_ssl_cert_digest(c1, digest1) || ne_ssl_cert_digest(c2, digest2)) {
+ return -1;
+ }
+
+ return strcmp(digest1, digest2);
+}
+
+/* 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)
+{
+ int ret;
+ size_t len;
+ unsigned char *der;
+ gnutls_datum buffer = { NULL, 0 };
+ gnutls_x509_crt x5;
+
+ if (gnutls_x509_crt_init(&x5) != 0)
+ return NULL;
+
+ /* decode the base64 to get the raw DER representation */
+ len = ne_unbase64(data, &der);
+ if (len == 0) return NULL;
+
+ buffer.data = der;
+ buffer.size = len;
+
+ ret = gnutls_x509_crt_import(x5, &buffer, GNUTLS_X509_FMT_DER);
+ ne_free(der);
+
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(x5);
+ return NULL;
+ }
+
+ return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), x5);
+}
+
+char *ne_ssl_cert_export(const ne_ssl_certificate *cert)
+{
+ unsigned char *der;
+ size_t len = 0;
+ char *ret;
+
+ /* find the length of the DER encoding. */
+ if (gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_DER, NULL, &len) !=
+ GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ return NULL;
+ }
+
+ der = ne_malloc(len);
+ if (gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_DER, der, &len)) {
+ ne_free(der);
+ return NULL;
+ }
+
+ ret = ne_base64(der, len);
+ ne_free(der);
+ return ret;
+}
+
+int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest)
+{
+ int j, len = 20;
+ char sha1[20], *p;
+
+ if (gnutls_x509_crt_get_fingerprint(cert->subject, GNUTLS_DIG_SHA,
+ sha1, &len) < 0)
+ 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/neon/src/ne_locks.c b/neon/src/ne_locks.c
index 8863bd4f4..7fc25a4d0 100644
--- a/neon/src/ne_locks.c
+++ b/neon/src/ne_locks.c
@@ -725,7 +725,7 @@ int ne_lock(ne_session *sess, struct ne_lock *lock)
ne_buffer_destroy(body);
ne_buffer_destroy(ctx.cdata);
- parse_failed = !ne_xml_valid(parser);
+ parse_failed = ne_xml_failed(parser);
if (ret == NE_OK && ne_get_status(req)->klass == 2) {
if (ctx.token == NULL) {
@@ -780,9 +780,13 @@ 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;
+ struct lock_ctx ctx;
+
+ memset(&ctx, 0, sizeof ctx);
+ ctx.cdata = ne_buffer_create();
/* Handle the response and update *lock appropriately. */
- ne_xml_push_handler(parser, lk_startelm, NULL, lk_endelm, lock);
+ ne_xml_push_handler(parser, lk_startelm, lk_cdata, lk_endelm, &ctx);
ne_add_response_body_reader(req, ne_accept_2xx,
ne_xml_parse_v, parser);
@@ -797,7 +801,7 @@ int ne_lock_refresh(ne_session *sess, struct ne_lock *lock)
ret = ne_request_dispatch(req);
- parse_failed = !ne_xml_valid(parser);
+ parse_failed = ne_xml_failed(parser);
if (ret == NE_OK && ne_get_status(req)->klass == 2) {
if (parse_failed) {
@@ -812,6 +816,7 @@ int ne_lock_refresh(ne_session *sess, struct ne_lock *lock)
ret = NE_ERROR;
}
+ ne_buffer_destroy(ctx.cdata);
ne_request_destroy(req);
ne_xml_destroy(parser);
diff --git a/neon/src/ne_openssl.c b/neon/src/ne_openssl.c
index a19d3d4aa..99ee32811 100644
--- a/neon/src/ne_openssl.c
+++ b/neon/src/ne_openssl.c
@@ -218,10 +218,8 @@ static int match_hostname(char *cn, const char *hostname)
/* Check certificate identity. Returns zero if identity matches; 1 if
* identity does not match, or <0 if the certificate had no identity.
* If 'identity' is non-NULL, store the malloc-allocated identity in
- * *identity. If 'server' is non-NULL, it must be the network address
- * of the server in use, and identity must be NULL. */
-static int check_identity(const char *hostname, X509 *cert, char **identity,
- const ne_inet_addr *server)
+ * *identity. */
+static int check_identity(const char *hostname, X509 *cert, char **identity)
{
STACK_OF(GENERAL_NAME) *names;
int match = 0, found = 0;
@@ -241,7 +239,7 @@ static int check_identity(const char *hostname, X509 *cert, char **identity,
match = match_hostname(name, hostname);
ne_free(name);
found = 1;
- } else if (nm->type == GEN_IPADD && server) {
+ } else if (nm->type == GEN_IPADD) {
/* compare IP address with server IP address. */
ne_inet_addr *ia;
if (nm->d.ip->length == 4)
@@ -252,7 +250,10 @@ static int check_identity(const char *hostname, X509 *cert, char **identity,
ia = NULL;
/* ne_iaddr_make returns NULL if address type is unsupported */
if (ia != NULL) { /* address type was supported. */
- match = ne_iaddr_cmp(server, ia) == 0;
+ char buf[128];
+
+ match = strcmp(hostname,
+ ne_iaddr_print(ia, buf, sizeof buf)) == 0;
found = 1;
ne_iaddr_free(ia);
} else {
@@ -295,7 +296,8 @@ static int check_identity(const char *hostname, X509 *cert, char **identity,
ne_free(name);
}
- NE_DEBUG(NE_DBG_SSL, "Identity match: %s\n", match ? "good" : "bad");
+ NE_DEBUG(NE_DBG_SSL, "Identity match for '%s': %s\n", hostname,
+ match ? "good" : "bad");
return match ? 0 : 1;
}
@@ -308,7 +310,7 @@ static ne_ssl_certificate *populate_cert(ne_ssl_certificate *cert, X509 *x5)
cert->subject = x5;
/* Retrieve the cert identity; pass a dummy hostname to match. */
cert->identity = NULL;
- check_identity("", x5, &cert->identity, NULL);
+ check_identity("", x5, &cert->identity);
return cert;
}
@@ -323,7 +325,7 @@ static ne_ssl_certificate *make_chain(STACK_OF(X509) *chain)
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
+#ifdef 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);
@@ -357,8 +359,7 @@ static int check_certificate(ne_session *sess, SSL *ssl, ne_ssl_certificate *cha
/* Check certificate was issued to this server; pass network
* address of server if a proxy is not in use. */
- ret = check_identity(sess->server.hostname, cert, NULL,
- sess->use_proxy ? NULL : sess->server.current);
+ ret = check_identity(sess->server.hostname, cert, NULL);
if (ret < 0) {
ne_set_error(sess, _("Server certificate was missing commonName "
"attribute in subject name"));
@@ -481,18 +482,56 @@ 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 *ne_ssl_context_create(int mode)
{
- 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);
+ ne_ssl_context *ctx = ne_calloc(sizeof *ctx);
+ if (mode == NE_SSL_CTX_CLIENT) {
+ 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);
+ } else if (mode == NE_SSL_CTX_SERVER) {
+ ctx->ctx = SSL_CTX_new(SSLv23_server_method());
+ } else {
+ ctx->ctx = SSL_CTX_new(SSLv2_server_method());
+ }
return ctx;
}
+int ne_ssl_context_keypair(ne_ssl_context *ctx, const char *cert,
+ const char *key)
+{
+ int ret;
+
+ ret = SSL_CTX_use_PrivateKey_file(ctx->ctx, key, SSL_FILETYPE_PEM);
+ if (ret == 1) {
+ ret = SSL_CTX_use_certificate_file(ctx->ctx, cert, SSL_FILETYPE_PEM);
+ }
+
+ return ret == 1 ? 0 : -1;
+}
+
+int ne_ssl_context_set_verify(ne_ssl_context *ctx,
+ int required,
+ const char *ca_names,
+ const char *verify_cas)
+{
+ if (required) {
+ SSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+ }
+ if (ca_names) {
+ SSL_CTX_set_client_CA_list(ctx->ctx,
+ SSL_load_client_CA_file(ca_names));
+ }
+ if (verify_cas) {
+ SSL_CTX_load_verify_locations(ctx->ctx, verify_cas, NULL);
+ }
+ return 0;
+}
+
void ne_ssl_context_destroy(ne_ssl_context *ctx)
{
SSL_CTX_free(ctx->ctx);
@@ -502,11 +541,11 @@ void ne_ssl_context_destroy(ne_ssl_context *ctx)
}
/* For internal use only. */
-int ne_negotiate_ssl(ne_request *req)
+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;
+ SSL *ssl;
STACK_OF(X509) *chain;
int freechain = 0; /* non-zero if chain should be free'd. */
@@ -527,12 +566,12 @@ int ne_negotiate_ssl(ne_request *req)
return NE_ERROR;
}
- sock = ne_sock_sslsock(sess->socket);
+ ssl = ne__sock_sslsock(sess->socket);
- chain = SSL_get_peer_cert_chain(sock->ssl);
+ chain = SSL_get_peer_cert_chain(ssl);
/* For an SSLv2 connection, the cert chain will always be NULL. */
if (chain == NULL) {
- X509 *cert = SSL_get_peer_certificate(sock->ssl);
+ X509 *cert = SSL_get_peer_certificate(ssl);
if (cert) {
chain = sk_X509_new_null();
sk_X509_push(chain, cert);
@@ -561,7 +600,7 @@ int ne_negotiate_ssl(ne_request *req)
if (freechain) sk_X509_free(chain); /* no longer need the chain */
- if (check_certificate(sess, sock->ssl, cert)) {
+ if (check_certificate(sess, ssl, cert)) {
NE_DEBUG(NE_DBG_SSL, "SSL certificate checks failed: %s\n",
sess->error);
ne_ssl_cert_free(cert);
@@ -573,12 +612,12 @@ int ne_negotiate_ssl(ne_request *req)
if (!ctx->sess) {
/* store the session. */
- ctx->sess = SSL_get1_session(sock->ssl);
+ ctx->sess = SSL_get1_session(ssl);
}
if (sess->notify_cb) {
sess->notify_cb(sess->notify_ud, ne_conn_secure,
- SSL_get_version(sock->ssl));
+ SSL_get_version(ssl));
}
return NE_OK;
@@ -604,7 +643,7 @@ 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)
+void ne_ssl_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert)
{
X509_STORE *store = SSL_CTX_get_cert_store(ctx->ctx);
diff --git a/neon/src/ne_private.h b/neon/src/ne_private.h
index 49f9d6445..b5d952990 100644
--- a/neon/src/ne_private.h
+++ b/neon/src/ne_private.h
@@ -67,14 +67,16 @@ struct ne_session_s {
char *scheme;
struct host_info server, proxy;
+ /* application-provided address list */
+ const ne_inet_addr **addrlist;
+ size_t numaddrs, curaddr;
+
/* 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;
@@ -88,7 +90,7 @@ struct ne_session_s {
char *user_agent; /* full User-Agent: header field */
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
ne_ssl_client_cert *client_cert;
ne_ssl_certificate *server_cert;
ne_ssl_context *ssl_context;
@@ -105,17 +107,15 @@ struct ne_session_s {
char error[BUFSIZ];
};
+/* Pushes block of 'count' bytes at 'buf'. Returns non-zero on
+ * error. */
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);
+ * 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);
-
-/* 0.24.x hack to fix ne_compress layer problems */
-void ne_kill_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);
+int ne__negotiate_ssl(ne_request *req);
#endif /* HTTP_PRIVATE_H */
diff --git a/neon/src/ne_privssl.h b/neon/src/ne_privssl.h
index 1f637590e..197d6f1a3 100644
--- a/neon/src/ne_privssl.h
+++ b/neon/src/ne_privssl.h
@@ -1,6 +1,7 @@
/*
SSL interface definitions internal to neon.
- Copyright (C) 2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2003, 2004, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2004, Aleix Conchillo Flaque <aleix@member.fsf.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -25,24 +26,38 @@
#ifndef NE_PRIVSSL_H
#define NE_PRIVSSL_H
-/* This is the private interface between ne_socket and ne_openssl. */
+/* This is the private interface between ne_socket, ne_gnutls and
+ * ne_openssl. */
-#ifdef NEON_SSL
+#include "ne_ssl.h"
+#include "ne_socket.h"
-#include <openssl/ssl.h>
+#ifdef HAVE_OPENSSL
-#include "ne_ssl.h"
+#include <openssl/ssl.h>
-/* SSL context */
struct ne_ssl_context_s {
SSL_CTX *ctx;
SSL_SESSION *sess;
};
-struct ne_ssl_socket_s {
- SSL *ssl;
+typedef SSL *ne_ssl_socket;
+
+#endif /* HAVE_OPENSSL */
+
+#ifdef HAVE_GNUTLS
+
+#include <gnutls/gnutls.h>
+
+struct ne_ssl_context_s {
+ gnutls_certificate_credentials cred;
+ /* TODO: store session here too */
};
-#endif /* NEON_SSL */
+typedef gnutls_session ne_ssl_socket;
+
+#endif /* HAVE_GNUTLS */
+
+ne_ssl_socket ne__sock_sslsock(ne_socket *sock);
-#endif
+#endif /* NE_PRIVSSL_H */
diff --git a/neon/src/ne_props.c b/neon/src/ne_props.c
index 057085e55..fc18a67b3 100644
--- a/neon/src/ne_props.c
+++ b/neon/src/ne_props.c
@@ -33,6 +33,7 @@
#include "ne_props.h"
#include "ne_basic.h"
#include "ne_locks.h"
+#include "ne_i18n.h"
/* don't store flat props with a value > 10K */
#define MAX_FLATPROP_LEN (102400)
@@ -83,11 +84,12 @@ struct propstat {
/* Results set. */
struct ne_prop_result_set_s {
struct propstat *pstats;
- int numpstats;
+ int numpstats, counter;
void *private;
char *href;
};
+#define MAX_PROP_COUNTER (1024)
static int
startelm(void *userdata, int state, const char *name, const char *nspace,
@@ -141,7 +143,7 @@ static int propfind(ne_propfind_handler *handler,
if (ret == NE_OK && ne_get_status(req)->klass != 2) {
ret = NE_ERROR;
- } else if (!ne_xml_valid(handler->parser)) {
+ } else if (ne_xml_failed(handler->parser)) {
ne_set_error(handler->sess, "%s", ne_xml_get_error(handler->parser));
ret = NE_ERROR;
}
@@ -220,7 +222,7 @@ int ne_proppatch(ne_session *sess, const char *uri,
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
+#ifdef NE_HAVE_DAV
ne_lock_using_resource(req, uri, NE_DEPTH_ZERO);
#endif
@@ -360,9 +362,15 @@ static void *start_response(void *userdata, const char *href)
static void *start_propstat(void *userdata, void *response)
{
ne_prop_result_set *set = response;
- int n;
+ ne_propfind_handler *hdl = userdata;
struct propstat *pstat;
+ int n;
+ if (++hdl->current->counter == MAX_PROP_COUNTER) {
+ ne_xml_set_error(hdl->parser, _("Response exceeds maximum property count"));
+ return NULL;
+ }
+
n = set->numpstats;
set->pstats = ne_realloc(set->pstats, sizeof(struct propstat) * (n+1));
set->numpstats = n+1;
@@ -396,6 +404,13 @@ static int startelm(void *userdata, int parent,
return ELM_flatprop;
}
+ /* Enforce maximum number of properties per resource to prevent a
+ * memory exhaustion attack by a hostile server. */
+ if (++hdl->current->counter == MAX_PROP_COUNTER) {
+ ne_xml_set_error(hdl->parser, _("Response exceeds maximum property count"));
+ return NE_XML_ABORT;
+ }
+
/* Add a property to this propstat */
n = pstat->numprops;
diff --git a/neon/src/ne_request.c b/neon/src/ne_request.c
index 7233cf378..6049a7e64 100644
--- a/neon/src/ne_request.c
+++ b/neon/src/ne_request.c
@@ -26,10 +26,6 @@
#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 */
@@ -90,6 +86,33 @@ struct body_reader {
struct body_reader *next;
};
+#ifdef NE_LFS
+#define ne_lseek lseek64
+typedef off64_t ne_off_t;
+#define FMT_NE_OFF_T NE_FMT_OFF64_T
+#if defined(LONG_LONG_MAX) && !defined(LLONG_MAX)
+#define LLONG_MAX LONG_LONG_MAX
+#endif
+#define NE_OFFT_MAX LLONG_MAX
+#ifdef HAVE_STRTOLL
+#define ne_strtoff strtoll
+#else
+#define ne_strtoff strtoq
+#endif
+#else /* !NE_LFS */
+typedef off_t ne_off_t;
+#define ne_lseek lseek
+#define FMT_NE_OFF_T NE_FMT_OFF_T
+#define NE_OFFT_MAX LONG_MAX
+#if SIZEOF_OFF_T > SIZEOF_LONG && defined(HAVE_STRTOLL)
+#define ne_strtoff strtoll
+#elif SIZEOF_OFF_T > SIZEOF_LONG && defined(HAVE_STRTOQ)
+#define ne_strtoff strtoq
+#else
+#define ne_strtoff strtol
+#endif
+#endif /* NE_LFS */
+
struct ne_request_s {
char *method, *uri; /* method and Request-URI */
@@ -99,16 +122,23 @@ struct ne_request_s {
ne_provide_body body_cb;
void *body_ud;
- /* Comes from either an fd or a buffer. */
+ /* Request body source: file or buffer (if not callback). */
union {
- int fd;
+ struct {
+ int fd;
+ ne_off_t offset, length;
+ ne_off_t remain; /* remaining bytes to send. */
+ } file;
struct {
+ /* length bytes @ buffer = whole body.
+ * remain bytes @ pnt = remaining bytes to send */
const char *buffer, *pnt;
- size_t left;
+ size_t length, remain;
} buf;
} body;
- size_t body_size, body_progress;
+ ne_off_t body_length; /* length of request body */
+ ne_off_t body_progress; /* number of bytes of body sent so far */
/* temporary store for response lines. */
char respbuf[BUFSIZ];
@@ -117,17 +147,25 @@ struct ne_request_s {
/* The transfer encoding types */
struct ne_response {
- int length; /* Response entity-body content-length */
- size_t left; /* Bytes left to read */
- size_t chunk_left; /* Bytes of chunk left to read */
- size_t total; /* total bytes read so far. */
- /* how the message length is detemined: */
enum {
R_TILLEOF = 0, /* read till eof */
- R_NO_BODY, /* implicitly no body (HEAD, 204, 205, 304) */
+ R_NO_BODY, /* implicitly no body (HEAD, 204, 304) */
R_CHUNKED, /* using chunked transfer-encoding */
R_CLENGTH /* using given content-length */
} mode;
+ union {
+ /* clen: used if mode == R_CLENGTH; total and bytes
+ * remaining to be read of response body. */
+ struct {
+ ne_off_t total, remain;
+ } clen;
+ /* chunk: used if mode == R_CHUNKED; total and bytes
+ * remaining to be read of current chunk */
+ struct {
+ size_t total, remain;
+ } chunk;
+ } body;
+ ne_off_t progress; /* number of bytes read of response */
} resp;
/* List of callbacks which are passed response headers */
@@ -316,25 +354,6 @@ void ne_hook_destroy_session(ne_session *sess,
ADD_HOOK(sess->destroy_sess_hooks, fn, userdata);
}
-/* 0.24.x hack to fix ne_compress layer problems */
-void ne_kill_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata)
-{
- struct hook **last, *hk;
-
- last = &sess->pre_send_hooks;
- hk = *last;
-
- while (hk) {
- if (hk->fn == (void_fn)fn && hk->userdata == userdata) {
- *last = hk->next;
- ne_free(hk);
- return;
- }
- last = &hk->next;
- hk = *last;
- }
-}
-
void ne_set_session_private(ne_session *sess, const char *id, void *userdata)
{
add_hook(&sess->private, id, NULL, userdata);
@@ -350,16 +369,16 @@ 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.remain = req->body.buf.length;
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;
+ if (req->body.buf.remain < count)
+ count = req->body.buf.remain;
memcpy(buffer, req->body.buf.pnt, count);
req->body.buf.pnt += count;
- req->body.buf.left -= count;
+ req->body.buf.remain -= count;
}
return count;
@@ -370,16 +389,26 @@ 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);
+ if (req->body.file.remain == 0)
+ return 0;
+ if ((off_t)count > req->body.file.remain)
+ count = req->body.file.remain;
+ return read(req->body.file.fd, buffer, count);
} else {
- /* rewind since we may have to send it again */
- return lseek(req->body.fd, SEEK_SET, 0);
+ /* rewind for next send. */
+ if (ne_lseek(req->body.file.fd, req->body.file.offset, SEEK_SET)
+ == req->body.file.offset) {
+ req->body.file.remain = req->body.file.length;
+ return 0;
+ } else {
+ return -1;
+ }
}
}
/* 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 ne__pull_request_body(ne_request *req, ne_push_fn fn, void *ud)
{
int ret = 0;
char buffer[BUFSIZ];
@@ -419,7 +448,7 @@ static int send_with_progress(void *userdata, const char *data, size_t n)
if (ret == 0) {
req->body_progress += n;
req->session->progress_cb(req->session->progress_ud,
- req->body_progress, req->body_size);
+ req->body_progress, req->body_length);
}
return ret;
@@ -434,10 +463,10 @@ static int send_request_body(ne_request *req)
if (req->session->progress_cb) {
/* with progress callbacks. */
req->body_progress = 0;
- ret = ne_pull_request_body(req, send_with_progress, req);
+ 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,
+ ret = ne__pull_request_body(req, (ne_push_fn)ne_sock_fullwrite,
req->session->socket);
}
@@ -457,7 +486,8 @@ static void add_fixed_headers(ne_request *req)
* harder to get a persistent connection, except if using a proxy
* as per 2068 sec 19.7.1. Always add TE: trailers since those
* are understood. */
- if (!req->session->is_http11 && !req->session->use_proxy) {
+ if (!req->session->no_persist && !req->session->is_http11
+ && !req->session->use_proxy) {
ne_buffer_zappend(req->headers,
"Keep-Alive: " EOL
"Connection: TE, Keep-Alive" EOL
@@ -487,7 +517,8 @@ static void te_hdr_handler(void *userdata, const char *value)
{
struct ne_response *resp = userdata;
- resp->mode = R_CHUNKED;
+ resp->mode = R_CHUNKED;
+ resp->body.chunk.remain = 0;
}
/* Handler for the "Connection" response header */
@@ -504,10 +535,10 @@ static void connection_hdr_handler(void *userdata, const char *value)
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) {
+ ne_off_t len = ne_strtoff(value, NULL, 10);
+ if (len != NE_OFFT_MAX && len >= 0 && resp->mode == R_TILLEOF) {
resp->mode = R_CLENGTH;
- resp->length = len;
+ resp->body.clen.total = resp->body.clen.remain = len;
}
}
@@ -560,47 +591,66 @@ ne_request *ne_request_create(ne_session *sess,
return req;
}
-static void set_body_size(ne_request *req, size_t size)
+/* Set the request body length to 'length' */
+static void set_body_length(ne_request *req, ne_off_t length)
{
- req->body_size = size;
- ne_print_request_header(req, "Content-Length", "%" NE_FMT_SIZE_T, size);
+ req->body_length = length;
+ ne_print_request_header(req, "Content-Length", "%" FMT_NE_OFF_T, length);
}
void ne_set_request_body_buffer(ne_request *req, const char *buffer,
size_t size)
{
req->body.buf.buffer = buffer;
+ req->body.buf.length = size;
req->body_cb = body_string_send;
req->body_ud = req;
- set_body_size(req, size);
+ set_body_length(req, size);
}
-void ne_set_request_body_provider(ne_request *req, size_t bodysize,
+void ne_set_request_body_provider(ne_request *req, off_t bodysize,
ne_provide_body provider, void *ud)
{
req->body_cb = provider;
req->body_ud = ud;
- set_body_size(req, bodysize);
+ set_body_length(req, bodysize);
}
-int ne_set_request_body_fd(ne_request *req, int fd)
+void ne_set_request_body_fd(ne_request *req, int fd,
+ off_t offset, off_t length)
{
- 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.file.fd = fd;
+ req->body.file.offset = offset;
+ req->body.file.length = length;
req->body_cb = body_fd_send;
req->body_ud = req;
- set_body_size(req, bodyst.st_size);
- return 0;
+ set_body_length(req, length);
+}
+
+#ifdef NE_LFS
+void ne_set_request_body_fd64(ne_request *req, int fd,
+ off64_t offset, off64_t length)
+{
+ req->body.file.fd = fd;
+ req->body.file.offset = offset;
+ req->body.file.length = length;
+ req->body_cb = body_fd_send;
+ req->body_ud = req;
+ set_body_length(req, length);
+}
+
+void ne_set_request_body_provider64(ne_request *req, off64_t bodysize,
+ ne_provide_body provider, void *ud)
+{
+ req->body_cb = provider;
+ req->body_ud = ud;
+ set_body_length(req, bodysize);
+}
+#endif
+
+void ne_set_request_expect100(ne_request *req, int flag)
+{
+ req->use_expect100 = flag;
}
void ne_add_request_header(ne_request *req, const char *name,
@@ -707,34 +757,33 @@ void ne_request_destroy(ne_request *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...
- */
+/* Reads a block of the response into BUFFER, which is of size
+ * *BUFLEN. Returns zero on success or non-zero on error. On
+ * success, *BUFLEN is updated to be the number of bytes read into
+ * BUFFER (which will be 0 to indicate the end of the repsonse). On
+ * error, the connection is closed and the session error string is
+ * set. */
static int read_response_block(ne_request *req, struct ne_response *resp,
char *buffer, size_t *buflen)
{
+ ne_socket *const sock = req->session->socket;
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;
+ /* Chunked transfer-encoding: chunk syntax is "SIZE CRLF CHUNK
+ * CRLF SIZE CRLF CHUNK CRLF ..." followed by zero-length
+ * chunk: "CHUNK CRLF 0 CRLF". resp.chunk.remain contains the
+ * number of bytes left to read in the current chunk. */
+ if (resp->body.chunk.remain == 0) {
+ unsigned long chunk_len;
char *ptr;
- /* We are at the start of a new chunk. */
- NE_DEBUG(NE_DBG_HTTP, "New chunk.\n");
+
+ /* The start of a new chunk. */
SOCK_ERR(req, ne_sock_readline(sock, buffer, *buflen),
_("Could not read chunk size"));
- NE_DEBUG(NE_DBG_HTTP, "[Chunk Size] < %s", buffer);
+ NE_DEBUG(NE_DBG_HTTP, "[chunk] < %s", buffer);
chunk_len = strtoul(buffer, &ptr, 16);
/* limit chunk size to <= UINT_MAX, so it will probably
* fit in a size_t. */
@@ -743,18 +792,14 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
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;
+ resp->body.chunk.remain = chunk_len;
}
- willread = resp->chunk_left;
+ willread = resp->body.chunk.remain > *buflen
+ ? *buflen : resp->body.chunk.remain;
break;
case R_CLENGTH:
- willread = resp->left;
+ willread = resp->body.clen.remain > (off_t)*buflen
+ ? *buflen : (size_t)resp->body.clen.remain;
break;
case R_TILLEOF:
willread = *buflen;
@@ -764,8 +809,7 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
willread = 0;
break;
}
- if (willread > *buflen) willread = *buflen;
- else if (willread == 0) {
+ if (willread == 0) {
*buflen = 0;
return 0;
}
@@ -792,8 +836,8 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
"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) {
+ resp->body.chunk.remain -= readlen;
+ if (resp->body.chunk.remain == 0) {
char crlfbuf[2];
/* If we've read a whole chunk, read a CRLF */
readlen = ne_sock_fullread(sock, crlfbuf, 2);
@@ -804,8 +848,9 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
return aborted(req, _("Chunk delimiter was invalid"), 0);
}
} else if (resp->mode == R_CLENGTH) {
- resp->left -= readlen;
+ resp->body.clen.remain -= readlen;
}
+ resp->progress += readlen;
return NE_OK;
}
@@ -813,20 +858,21 @@ ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen)
{
struct body_reader *rdr;
size_t readlen = buflen;
+ struct ne_response *const resp = &req->resp;
- if (read_response_block(req, &req->resp, buffer, &readlen))
+ if (read_response_block(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);
+ req->session->progress_cb(req->session->progress_ud, resp->progress,
+ resp->mode==R_CLENGTH ? resp->body.clen.total:-1);
}
- /* TODO: call the readers when this fails too. */
for (rdr = req->body_readers; rdr!=NULL; rdr=rdr->next) {
- if (rdr->use) rdr->handler(rdr->userdata, buffer, readlen);
+ if (rdr->use && rdr->handler(rdr->userdata, buffer, readlen) != 0) {
+ ne_close_connection(req->session);
+ return -1;
+ }
}
return readlen;
@@ -864,10 +910,10 @@ static ne_buffer *build_request(ne_request *req)
static void dump_request(const char *request)
{
- if ((NE_DBG_HTTPPLAIN&ne_debug_mask) == NE_DBG_HTTPPLAIN) {
+ if (ne_debug_mask & NE_DBG_HTTPPLAIN) {
/* Display everything mode */
NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", request);
- } else {
+ } else if (ne_debug_mask & NE_DBG_HTTP) {
/* Blank out the Authorization paramaters */
char *reqdebug = ne_strdup(request), *pnt = reqdebug;
while ((pnt = strstr(pnt, "Authorization: ")) != NULL) {
@@ -979,7 +1025,7 @@ static int send_request(ne_request *req, const ne_buffer *request)
return RETRY_RET(retry, ret, aret);
}
- if (!req->use_expect100 && req->body_size > 0) {
+ if (!req->use_expect100 && req->body_length > 0) {
/* Send request body, if not using 100-continue. */
ret = send_request_body(req);
if (ret < 0) {
@@ -999,7 +1045,8 @@ static int send_request(ne_request *req, const ne_buffer *request)
/* Discard headers with the interim response. */
if ((ret = discard_headers(req)) != NE_OK) break;
- if (req->use_expect100 && (status->code == 100) && !sentbody) {
+ if (req->use_expect100 && (status->code == 100)
+ && req->body_length > 0 && !sentbody) {
/* Send the body after receiving the first 100 Continue */
if ((ret = send_request_body(req)) != NE_OK) break;
sentbody = 1;
@@ -1135,8 +1182,12 @@ static int read_response_headers(ne_request *req)
return ret;
}
+/* Perform any necessary DNS lookup for the host given by *info;
+ * return NE_ code. */
static int lookup_host(ne_session *sess, struct host_info *info)
{
+ if (sess->addrlist) return NE_OK;
+
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);
@@ -1169,10 +1220,6 @@ int ne_begin_request(ne_request *req)
req->resp.mode = R_TILLEOF;
- /* FIXME: Determine whether to use the Expect: 100-continue header. */
- req->use_expect100 = (req->session->expect100_works > -1) &&
- (req->body_size > HTTP_EXPECT_MINSIZE) && req->session->is_http11;
-
/* Build the request string, and send it */
data = build_request(req);
DEBUG_DUMP_REQUEST(data->data);
@@ -1197,7 +1244,7 @@ int ne_begin_request(ne_request *req)
/* Read the headers */
HTTP_ERR(read_response_headers(req));
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
/* Special case for CONNECT handling: the response has no body,
* and the connection can persist. */
if (req->session->in_connect && st->klass == 2) {
@@ -1206,9 +1253,9 @@ int ne_begin_request(ne_request *req)
}
#endif
- /* HEAD requests and 204, 205, 304 responses have no response body,
+ /* 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==205 || st->code==304)
+ 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
@@ -1218,9 +1265,6 @@ int ne_begin_request(ne_request *req)
rdr->use = rdr->accept_response(rdr->userdata, req, st);
}
- req->resp.left = req->resp.length;
- req->resp.chunk_left = 0;
-
return NE_OK;
}
@@ -1292,7 +1336,7 @@ ne_session *ne_get_session(const ne_request *req)
return req->session;
}
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
/* Create a CONNECT tunnel through the proxy server.
* Returns HTTP_* */
static int proxy_tunnel(ne_session *sess)
@@ -1324,13 +1368,37 @@ static int proxy_tunnel(ne_session *sess)
}
#endif
+/* Return the first resolved address for the given host. */
+static const ne_inet_addr *resolve_first(ne_session *sess,
+ struct host_info *host)
+{
+ if (sess->addrlist) {
+ sess->curaddr = 0;
+ return sess->addrlist[0];
+ } else {
+ return ne_addr_first(host->address);
+ }
+}
+
+/* Return the next resolved address for the given host or NULL if
+ * there are no more addresses. */
+static const ne_inet_addr *resolve_next(ne_session *sess,
+ struct host_info *host)
+{
+ if (sess->addrlist) {
+ if (sess->curaddr++ < sess->numaddrs)
+ return sess->addrlist[sess->curaddr];
+ else
+ return NULL;
+ } else {
+ return ne_addr_next(host->address);
+ }
+}
+
/* 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;
@@ -1342,7 +1410,7 @@ static int do_connect(ne_request *req, struct host_info *host, const char *err)
}
if (host->current == NULL)
- host->current = ne_addr_first(host->address);
+ host->current = resolve_first(sess, host);
do {
notify_status(sess, ne_conn_connecting, host->hostport);
@@ -1355,7 +1423,7 @@ static int do_connect(ne_request *req, struct host_info *host, const char *err)
#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);
+ (host->current = resolve_next(sess, host)) != NULL);
if (ret) {
ne_set_error(sess, "%s: %s", err, ne_sock_error(sess->socket));
@@ -1363,7 +1431,7 @@ static int do_connect(ne_request *req, struct host_info *host, const char *err)
return NE_CONNECT;
}
- notify_status(sess, ne_conn_connected, sess->proxy.hostport);
+ notify_status(sess, ne_conn_connected, host->hostport);
if (sess->rdtimeout)
ne_sock_read_timeout(sess->socket, sess->rdtimeout);
@@ -1389,7 +1457,7 @@ static int open_connection(ne_request *req)
if (ret != NE_OK) return ret;
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
/* Negotiate SSL layer if required. */
if (sess->use_ssl && !sess->in_connect) {
/* CONNECT tunnel */
@@ -1397,7 +1465,7 @@ static int open_connection(ne_request *req)
ret = proxy_tunnel(sess);
if (ret == NE_OK)
- ret = ne_negotiate_ssl(req);
+ ret = ne__negotiate_ssl(req);
/* This is probably only really needed for ne_negotiate_ssl
* failures as proxy_tunnel will fail via aborted(). */
diff --git a/neon/src/ne_request.h b/neon/src/ne_request.h
index e59b59de7..b3dadbad9 100644
--- a/neon/src/ne_request.h
+++ b/neon/src/ne_request.h
@@ -1,6 +1,6 @@
/*
HTTP Request Handling
- Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -53,17 +53,21 @@ typedef struct ne_request_s ne_request;
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. */
+/* The request body will be taken from 'size' bytes of 'buffer'. */
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);
+/* The request body will be taken from 'length' bytes read from the
+ * file descriptor 'fd', starting from file offset 'offset'. */
+void ne_set_request_body_fd(ne_request *req, int fd,
+ off_t offset, off_t length);
+
+#ifdef NE_LFS
+/* Alternate version of ne_set_request_body_fd taking off64_t
+ * offset type for systems supporting _LARGEFILE64_SOURCE. */
+void ne_set_request_body_fd64(ne_request *req, int fd,
+ off64_t offset, off64_t length);
+#endif
/* "Pull"-based request body provider: a callback which is invoked to
* provide blocks of request body on demand.
@@ -79,13 +83,20 @@ int ne_set_request_body_fd(ne_request *req, int fd);
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,
+/* Install a callback which is invoked as needed to provide the
+ * request body, a block at a time. The total size of the request
+ * body is 'length'; the callback must ensure that it returns no more
+ * than 'length' bytes in total. */
+void ne_set_request_body_provider(ne_request *req, off_t length,
ne_provide_body provider, void *userdata);
+#ifdef NE_LFS
+/* Duplicate version of ne_set_request_body_provider, taking an off64_t
+ * offset. */
+void ne_set_request_body_provider64(ne_request *req, off64_t length,
+ ne_provide_body provider, void *userdata);
+#endif
+
/* Handling response bodies... you provide TWO callbacks:
*
* 1) 'acceptance' callback: determines whether you want to handle the
@@ -108,8 +119,12 @@ int ne_accept_2xx(void *userdata, ne_request *req, const ne_status *st);
* 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);
+/* Callback for reading a block of data. Returns zero on success, or
+ * -1 on error. If returning an error, the response will be aborted
+ * and the callback will not be invoked again. The request dispatch
+ * (or ne_read_response_block call) will fail with NE_ERROR; the
+ * session error string should have been set by the callback. */
+typedef int (*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
@@ -213,6 +228,12 @@ int ne_end_request(ne_request *req);
*/
ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);
+/* Include the HTTP/1.1 header "Expect: 100-continue" in request 'req'
+ * if 'flag' is non-zero. Warning: 100-continue support is not
+ * implemented correctly in some HTTP/1.1 servers, enabling this
+ * feature may cause requests to hang or time out. */
+void ne_set_request_expect100(ne_request *req, int flag);
+
/**** Request hooks handling *****/
typedef void (*ne_free_hooks)(void *cookie);
@@ -261,4 +282,3 @@ void *ne_get_request_private(ne_request *req, const char *id);
END_NEON_DECLS
#endif /* NE_REQUEST_H */
-
diff --git a/neon/src/ne_session.c b/neon/src/ne_session.c
index 04b777e31..027160005 100644
--- a/neon/src/ne_session.c
+++ b/neon/src/ne_session.c
@@ -1,6 +1,6 @@
/*
HTTP session handling
- Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
Portions are:
Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
@@ -33,6 +33,10 @@
#include <errno.h>
#endif
+#ifdef NE_HAVE_IDNA
+#include <idna.h>
+#endif
+
#include "ne_session.h"
#include "ne_alloc.h"
#include "ne_utils.h"
@@ -84,7 +88,7 @@ void ne_session_destroy(ne_session *sess)
ne_close_connection(sess);
}
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
if (sess->ssl_context)
ne_ssl_context_destroy(sess->ssl_context);
@@ -118,6 +122,14 @@ static void set_hostport(struct host_info *host, unsigned int defaultport)
static void
set_hostinfo(struct host_info *info, const char *hostname, unsigned int port)
{
+#ifdef NE_HAVE_IDNA
+ char *ihost;
+
+#define FLAGS IDNA_USE_STD3_ASCII_RULES
+ if (idna_to_ascii_8z(hostname, &ihost, FLAGS) == IDNA_SUCCESS)
+ info->hostname = ihost;
+ else /* fall back to use provided hostname string */
+#endif
info->hostname = ne_strdup(hostname);
info->port = port;
}
@@ -139,16 +151,14 @@ ne_session *ne_session_create(const char *scheme,
set_hostinfo(&sess->server, hostname, port);
set_hostport(&sess->server, sess->use_ssl?443:80);
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
if (sess->use_ssl) {
- sess->ssl_context = ne_ssl_context_create();
+ sess->ssl_context = ne_ssl_context_create(0);
}
#endif
sess->scheme = ne_strdup(scheme);
- /* Default expect-100 to OFF. */
- sess->expect100_works = -1;
return sess;
}
@@ -160,6 +170,12 @@ void ne_session_proxy(ne_session *sess, const char *hostname,
set_hostinfo(&sess->proxy, hostname, port);
}
+void ne_set_addrlist(ne_session *sess, const ne_inet_addr **addrs, size_t n)
+{
+ sess->addrlist = addrs;
+ sess->numaddrs = n;
+}
+
void ne_set_error(ne_session *sess, const char *format, ...)
{
va_list params;
@@ -184,15 +200,6 @@ void ne_set_status(ne_session *sess,
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;
@@ -268,10 +275,7 @@ void ne_ssl_provide_clicert(ne_session *sess,
void ne_ssl_trust_cert(ne_session *sess, const ne_ssl_certificate *cert)
{
-#ifdef NEON_SSL
- ne_ssl_ctx_trustcert(sess->ssl_context, cert);
+#ifdef NE_HAVE_SSL
+ ne_ssl_context_trustcert(sess->ssl_context, cert);
#endif
}
-
-
-
diff --git a/neon/src/ne_session.h b/neon/src/ne_session.h
index 69e8647eb..5e846357b 100644
--- a/neon/src/ne_session.h
+++ b/neon/src/ne_session.h
@@ -1,6 +1,6 @@
/*
HTTP session handling
- Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -27,6 +27,7 @@
#include "ne_ssl.h"
#include "ne_uri.h" /* for ne_uri */
#include "ne_defs.h"
+#include "ne_socket.h"
BEGIN_NEON_DECLS
@@ -34,7 +35,9 @@ 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. */
+ * server. If neon is built with support for IDNA, 'hostname' can be
+ * a UTF-8-encoded Unicode string; otherwise, it must be an ASCII
+ * string. */
ne_session *ne_session_create(const char *scheme,
const char *hostname, unsigned int port);
@@ -49,18 +52,13 @@ void ne_close_connection(ne_session *sess);
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);
+/* Disable use of persistent connection if 'flag' is non-zero, else
+ * enable (the default). */
+void ne_set_persist(ne_session *sess, int flag);
+
+/* Bypass the normal name resolution; force the use of specific set of
+ * addresses for this session, addrs[0]...addrs[n-1]. */
+void ne_set_addrlist(ne_session *sess, const ne_inet_addr **addrs, size_t n);
/* Progress callback. */
typedef void (*ne_progress)(void *userdata, off_t progress, off_t total);
diff --git a/neon/src/ne_socket.c b/neon/src/ne_socket.c
index 1bcf257d1..224014109 100644
--- a/neon/src/ne_socket.c
+++ b/neon/src/ne_socket.c
@@ -1,7 +1,8 @@
/*
- socket handling routines
+ Socket handling routines
Copyright (C) 1998-2004, Joe Orton <joe@manyfish.co.uk>,
Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+ Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -29,10 +30,6 @@
#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>
@@ -40,13 +37,16 @@
#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 NE_USE_POLL
+#include <sys/poll.h>
+#elif defined(HAVE_SYS_SELECT_H)
+#include <sys/select.h>
+#endif
+
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
@@ -63,9 +63,12 @@
#ifdef WIN32
#include <winsock2.h>
#include <stddef.h>
+#ifdef USE_GETADDRINFO
+#include <ws2tcpip.h>
+#endif
#endif
-#if defined(NEON_SSL) && defined(HAVE_LIMITS_H)
+#if defined(HAVE_OPENSSL) && defined(HAVE_LIMITS_H)
#include <limits.h> /* for INT_MAX */
#endif
#ifdef HAVE_STRING_H
@@ -91,18 +94,17 @@
#include <socks.h>
#endif
-#ifdef NEON_SSL
+#ifdef HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h> /* for PKCS12_PBE_add */
#include <openssl/rand.h>
-
-#include "ne_privssl.h"
+#include <openssl/opensslv.h> /* for OPENSSL_VERSION_NUMBER */
#endif
-#include "ne_i18n.h"
-#include "ne_utils.h"
-#include "ne_string.h"
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
#define NE_INET_ADDR_DEFINED
/* A slightly ugly hack: change the ne_inet_addr definition to be the
@@ -112,35 +114,35 @@
* structure, or losing type-safety by using void *. */
#ifdef USE_GETADDRINFO
typedef struct addrinfo ne_inet_addr;
+#else
+typedef struct in_addr ne_inet_addr;
+#endif
+
+#ifdef NE_HAVE_SSL
+#include "ne_privssl.h" /* MUST come after ne_inet_addr is defined */
+#endif
/* To avoid doing AAAA queries unless absolutely necessary, either use
* AI_ADDRCONFIG where available, or a run-time check for working IPv6
* support; the latter is only known to work on Linux. */
-#if !defined(USE_GAI_ADDRCONFIG) && defined(__linux__)
+#if defined(USE_GETADDRINFO) && !defined(USE_GAI_ADDRCONFIG) && defined(__linux__)
#define USE_CHECK_IPV6
#endif
-#else
-typedef struct in_addr ne_inet_addr;
-#endif
-
+#include "ne_i18n.h"
+#include "ne_utils.h"
+#include "ne_string.h"
#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
@@ -151,7 +153,13 @@ typedef struct in_addr ne_inet_addr;
#define NE_ISCLOSED(e) ((e) == WSAESHUTDOWN || (e) == WSAENOTCONN)
#define NE_ISINTR(e) (0)
#else /* Unix */
+/* ECONNABORTED shouldn't really be returned by anything but accept()
+ * but apparently nobody told CygWin that... */
+#ifdef ECONNABORTED
+#define NE_ISRESET(e) ((e) == ECONNRESET || (e) == ECONNABORTED)
+#else
#define NE_ISRESET(e) ((e) == ECONNRESET)
+#endif
#define NE_ISCLOSED(e) ((e) == EPIPE)
#define NE_ISINTR(e) ((e) == EINTR)
#endif
@@ -165,8 +173,8 @@ 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. */
+ /* Write up to 'len' bytes from 'buf' to socket. Return number of
+ * bytes written on success, or <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. */
@@ -179,7 +187,7 @@ struct ne_socket_s {
void *progress_ud;
int rdtimeout; /* read timeout. */
const struct iofns *ops;
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
ne_ssl_socket ssl;
#endif
/* The read buffer: ->buffer stores byte which have been read; as
@@ -223,16 +231,21 @@ static void print_error(int errnum, char *buffer, size_t buflen)
#define set_strerror(s, e) ne_strerror((e), (s)->error, sizeof (s)->error)
#endif
-#ifdef NEON_SSL
-
-/* Initialize SSL library. */
+#if defined(HAVE_OPENSSL)
static void init_ssl(void)
{
SSL_load_error_strings();
SSL_library_init();
PKCS12_PBE_add(); /* ### not sure why this is needed. */
}
+#elif defined(HAVE_GNUTLS)
+static void init_ssl(void)
+{
+ gnutls_global_init();
+}
+#endif /* HAVE_OPENSSL */
+#ifdef HAVE_OPENSSL
/* Seed the SSL PRNG, if necessary; returns non-zero on failure. */
static int seed_ssl_prng(void)
{
@@ -240,7 +253,7 @@ static int seed_ssl_prng(void)
if (RAND_status() == 1)
return 0;
-#ifdef EGD_PATH
+#if defined(EGD_PATH)
NE_DEBUG(NE_DBG_SOCKET, "Seeding PRNG from " EGD_PATH "...\n");
if (RAND_egd(EGD_PATH) != -1)
return 0;
@@ -260,7 +273,7 @@ static int seed_ssl_prng(void)
NE_DEBUG(NE_DBG_SOCKET, "No entropy source found; could not seed PRNG.\n");
return -1;
}
-#endif /* NEON_SSL */
+#endif /* HAVE_OPENSSL */
#ifdef USE_CHECK_IPV6
static int ipv6_disabled = 0;
@@ -307,7 +320,7 @@ int ne_sock_init(void)
#endif
-#ifdef NEON_SOCKS
+#ifdef NE_HAVE_SOCKS
SOCKSinit("neon");
#endif
@@ -319,7 +332,7 @@ int ne_sock_init(void)
init_ipv6();
#endif
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
init_ssl();
#endif
@@ -332,6 +345,9 @@ void ne_sock_exit(void)
#ifdef WIN32
WSACleanup();
#endif
+#ifdef HAVE_GNUTLS
+ gnutls_global_deinit();
+#endif
init_result = 0;
}
@@ -345,9 +361,6 @@ int ne_sock_block(ne_socket *sock, int 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;
@@ -411,7 +424,20 @@ ssize_t ne_sock_peek(ne_socket *sock, char *buffer, size_t buflen)
/* Await data on raw fd in socket. */
static int readable_raw(ne_socket *sock, int secs)
{
- int fdno = sock->fd, ret;
+ int ret;
+#ifdef NE_USE_POLL
+ struct pollfd fds;
+ int timeout = secs > 0 ? secs * 1000 : -1;
+
+ fds.fd = sock->fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+
+ do {
+ ret = poll(&fds, 1, timeout);
+ } while (ret < 0 && NE_ISINTR(ne_errno));
+#else
+ int fdno = sock->fd;
fd_set rdfds;
struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
@@ -425,6 +451,8 @@ static int readable_raw(ne_socket *sock, int secs)
}
ret = select(fdno + 1, &rdfds, NULL, NULL, tvp);
} while (ret < 0 && NE_ISINTR(ne_errno));
+#endif
+
if (ret < 0) {
set_strerror(sock, ne_errno);
return NE_SOCK_ERROR;
@@ -440,7 +468,7 @@ static ssize_t read_raw(ne_socket *sock, char *buffer, size_t len)
if (ret) return ret;
do {
- ret = ne_read(sock->fd, buffer, len);
+ ret = recv(sock->fd, buffer, len, 0);
} while (ret == -1 && NE_ISINTR(ne_errno));
if (ret == 0) {
@@ -460,46 +488,36 @@ static ssize_t read_raw(ne_socket *sock, char *buffer, size_t len)
static ssize_t write_raw(ne_socket *sock, const char *data, size_t length)
{
- ssize_t wrote;
+ ssize_t ret;
do {
- wrote = ne_write(sock->fd, data, length);
- if (wrote > 0) {
- data += wrote;
- length -= wrote;
- }
- } while ((wrote > 0 || NE_ISINTR(ne_errno)) && length > 0);
+ ret = send(sock->fd, data, length, 0);
+ } while (ret == -1 && NE_ISINTR(ne_errno));
- if (wrote < 0) {
+ if (ret < 0) {
int errnum = ne_errno;
set_strerror(sock, errnum);
return MAP_ERR(errnum);
}
-
- return 0;
+ return ret;
}
static const struct iofns iofns_raw = { read_raw, write_raw, readable_raw };
-#ifdef NEON_SSL
+#ifdef HAVE_OPENSSL
/* 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))
+ if (SSL_pending(sock->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;
+ int err = SSL_get_error(sock->ssl, sret), ret = NE_SOCK_ERROR;
+ const char *str;
switch (err) {
case SSL_ERROR_ZERO_RETURN:
@@ -520,13 +538,15 @@ static int error_ossl(ne_socket *sock, int sret)
ret = MAP_ERR(err);
}
} else {
- ne_snprintf(sock->error, sizeof sock->error,
- _("SSL error: %s"), ERR_reason_error_string(err));
+ str = ERR_reason_error_string(err);
+ ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"),
+ str ? str : _("unknown error code"));
}
break;
default:
+ str = ERR_reason_error_string(ERR_get_error());
ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"),
- ERR_reason_error_string(ERR_get_error()));
+ str ? str : _("unknown error code"));
break;
}
return ret;
@@ -543,7 +563,7 @@ static ssize_t read_ossl(ne_socket *sock, char *buffer, size_t len)
ret = readable_ossl(sock, sock->rdtimeout);
if (ret) return ret;
- ret = SSL_read(sock->ssl.ssl, buffer, CAST2INT(len));
+ ret = SSL_read(sock->ssl, buffer, CAST2INT(len));
if (ret <= 0)
ret = error_ossl(sock, ret);
@@ -553,26 +573,126 @@ static ssize_t read_ossl(ne_socket *sock, char *buffer, size_t len)
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);
+ ret = SSL_write(sock->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;
+ return ret;
}
-static const struct iofns iofns_ossl = {
+static const struct iofns iofns_ssl = {
read_ossl,
write_ossl,
readable_ossl
};
-#endif /* NEON_SSL */
+#elif defined(HAVE_GNUTLS)
+
+/* Return zero if an alert value can be ignored. */
+static int check_alert(ne_socket *sock, ssize_t ret)
+{
+ const char *alert;
+
+ if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+ alert = gnutls_alert_get_name(gnutls_alert_get(sock->ssl));
+ NE_DEBUG(NE_DBG_SOCKET, "TLS warning alert: %s\n", alert);
+ return 0;
+ } else if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+ alert = gnutls_alert_get_name(gnutls_alert_get(sock->ssl));
+ NE_DEBUG(NE_DBG_SOCKET, "TLS fatal alert: %s\n", alert);
+ return -1;
+ }
+ return ret;
+}
+
+static int readable_gnutls(ne_socket *sock, int secs)
+{
+ if (gnutls_record_check_pending(sock->ssl)) {
+ return 0;
+ }
+ return readable_raw(sock, secs);
+}
+
+static ssize_t error_gnutls(ne_socket *sock, ssize_t sret)
+{
+ ssize_t ret;
+
+ switch (sret) {
+ case 0:
+ ret = NE_SOCK_CLOSED;
+ set_error(sock, _("Connection closed"));
+ break;
+ case GNUTLS_E_FATAL_ALERT_RECEIVED:
+ ret = NE_SOCK_RESET;
+ ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"),
+ gnutls_alert_get_name(gnutls_alert_get(sock->ssl)));
+ break;
+ default:
+ ret = NE_SOCK_ERROR;
+ ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"),
+ gnutls_strerror(sret));
+ }
+ return ret;
+}
+
+#define RETRY_GNUTLS(sock, ret) ((ret < 0) \
+ && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN \
+ || check_alert(sock, ret) == 0))
+
+static ssize_t read_gnutls(ne_socket *sock, char *buffer, size_t len)
+{
+ ssize_t ret;
+
+ ret = readable_gnutls(sock, sock->rdtimeout);
+ if (ret) return ret;
+
+ do {
+ ret = gnutls_record_recv(sock->ssl, buffer, len);
+ } while (RETRY_GNUTLS(sock, ret));
+
+ if (ret <= 0)
+ ret = error_gnutls(sock, ret);
+
+ return ret;
+}
+
+static ssize_t write_gnutls(ne_socket *sock, const char *data, size_t len)
+{
+ ssize_t ret;
+
+ do {
+ ret = gnutls_record_send(sock->ssl, data, len);
+ } while (RETRY_GNUTLS(sock, ret));
+
+ if (ret < 0)
+ return error_gnutls(sock, ret);
+
+ return ret;
+}
+
+static const struct iofns iofns_ssl = {
+ read_gnutls,
+ write_gnutls,
+ readable_gnutls
+};
+
+#endif
int ne_sock_fullwrite(ne_socket *sock, const char *data, size_t len)
{
- return sock->ops->write(sock, data, len);
+ ssize_t ret;
+
+ do {
+ ret = sock->ops->write(sock, data, len);
+ if (ret > 0) {
+ data += ret;
+ len -= ret;
+ }
+ } while (ret > 0 && len > 0);
+
+ return ret < 0 ? ret : 0;
}
ssize_t ne_sock_readline(ne_socket *sock, char *buf, size_t buflen)
@@ -761,7 +881,7 @@ char *ne_addr_error(const ne_sock_addr *addr, char *buf, size_t bufsiz)
char *ne_iaddr_print(const ne_inet_addr *ia, char *buf, size_t bufsiz)
{
-#ifdef USE_GETADDRINFO /* implies inet_ntop */
+#if defined(USE_GETADDRINFO) && defined(HAVE_INET_NTOP)
const char *ret;
#ifdef AF_INET6
if (ia->ai_family == AF_INET6) {
@@ -776,7 +896,12 @@ char *ne_iaddr_print(const ne_inet_addr *ia, char *buf, size_t bufsiz)
ret = NULL;
if (ret == NULL)
ne_strnzcpy(buf, "[IP address]", bufsiz);
-#else
+#elif defined(USE_GETADDRINFO) && defined(NI_NUMERICHOST)
+ /* use getnameinfo instead for Win32, which lacks inet_ntop: */
+ if (getnameinfo(ia->ai_addr, ia->ai_addrlen, buf, bufsiz, NULL, 0,
+ NI_NUMERICHOST))
+ ne_strnzcpy(buf, "[IP address]", bufsiz);
+#else /* USE_GETADDRINFO */
ne_strnzcpy(buf, inet_ntoa(*ia), bufsiz);
#endif
return buf;
@@ -854,6 +979,14 @@ int ne_sock_connect(ne_socket *sock,
return -1;
}
+#if !defined(NE_USE_POLL) && !defined(WIN32)
+ if (fd > FD_SETSIZE) {
+ ne_close(fd);
+ set_error(sock, _("Socket descriptor number exceeds FD_SETSIZE"));
+ return NE_SOCK_ERROR;
+ }
+#endif
+
#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP)
{ /* Disable the Nagle algorithm; better to add write buffering
* instead of doing this. */
@@ -862,7 +995,7 @@ int ne_sock_connect(ne_socket *sock,
}
#endif
- if (raw_connect(fd, addr, ntohs(port))) {
+ if (raw_connect(fd, addr, htons(port))) {
set_strerror(sock, ne_errno);
ne_close(fd);
return -1;
@@ -908,6 +1041,15 @@ ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw)
return ia;
}
+ne_iaddr_type ne_iaddr_typeof(const ne_inet_addr *ia)
+{
+#ifdef USE_GETADDRINFO
+ return ia->ai_family == AF_INET6 ? ne_iaddr_ipv6 : ne_iaddr_ipv4;
+#else
+ return ne_iaddr_ipv4;
+#endif
+}
+
int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2)
{
#ifdef USE_GETADDRINFO
@@ -959,25 +1101,59 @@ void ne_sock_read_timeout(ne_socket *sock, int timeout)
sock->rdtimeout = timeout;
}
-#ifdef NEON_SSL
+#ifdef NE_HAVE_SSL
-void ne_sock_switch_ssl(ne_socket *sock, void *ssl)
+int ne_sock_accept_ssl(ne_socket *sock, ne_ssl_context *ctx)
{
- sock->ssl.ssl = ssl;
- sock->ops = &iofns_ossl;
+ int ret;
+ ne_ssl_socket ssl;
+
+#if defined(HAVE_OPENSSL)
+ ssl = SSL_new(ctx->ctx);
+
+ SSL_set_fd(ssl, sock->fd);
+
+ sock->ssl = ssl;
+ ret = SSL_accept(ssl);
+ if (ret != 1) {
+ return error_ossl(sock, ret);
+ }
+#elif defined(HAVE_GNUTLS)
+ gnutls_init(&ssl, GNUTLS_SERVER);
+ gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, ctx->cred);
+ gnutls_set_default_priority(ssl);
+
+ sock->ssl = ssl;
+ gnutls_transport_set_ptr(sock->ssl, (gnutls_transport_ptr) sock->fd);
+ ret = gnutls_handshake(ssl);
+ if (ret < 0) {
+ return error_gnutls(sock, ret);
+ }
+#endif
+ sock->ops = &iofns_ssl;
+ return 0;
}
int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx)
{
- SSL *ssl;
int ret;
+#if defined(HAVE_OPENSSL)
+ SSL *ssl;
+
if (seed_ssl_prng()) {
set_error(sock, _("SSL disabled due to lack of entropy"));
return NE_SOCK_ERROR;
}
- sock->ssl.ssl = ssl = SSL_new(ctx->ctx);
+ /* If runtime library version differs from compile-time version
+ * number in major/minor/fix level, abort soon. */
+ if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & 0xFFFFF000) {
+ set_error(sock, _("SSL disabled due to library version mismatch"));
+ return NE_SOCK_ERROR;
+ }
+
+ sock->ssl = ssl = SSL_new(ctx->ctx);
if (!ssl) {
set_error(sock, _("Could not create SSL structure"));
return NE_SOCK_ERROR;
@@ -986,7 +1162,7 @@ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx)
SSL_set_app_data(ssl, ctx);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
SSL_set_fd(ssl, sock->fd);
- sock->ops = &iofns_ossl;
+ sock->ops = &iofns_ssl;
if (ctx->sess)
SSL_set_session(ssl, ctx->sess);
@@ -995,16 +1171,30 @@ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx)
if (ret != 1) {
error_ossl(sock, ret);
SSL_free(ssl);
- sock->ssl.ssl = NULL;
+ sock->ssl = NULL;
return NE_SOCK_ERROR;
}
+#elif defined(HAVE_GNUTLS)
+ /* DH and RSA params are set in ne_ssl_context_create */
+ gnutls_init(&sock->ssl, GNUTLS_CLIENT);
+ gnutls_set_default_priority(sock->ssl);
+ gnutls_credentials_set(sock->ssl, GNUTLS_CRD_CERTIFICATE, ctx->cred);
+
+ gnutls_transport_set_ptr(sock->ssl, (gnutls_transport_ptr) sock->fd);
+ sock->ops = &iofns_ssl;
+ ret = gnutls_handshake(sock->ssl);
+ if (ret < 0) {
+ error_gnutls(sock, ret);
+ return NE_SOCK_ERROR;
+ }
+#endif
return 0;
}
-ne_ssl_socket *ne_sock_sslsock(ne_socket *sock)
+ne_ssl_socket ne__sock_sslsock(ne_socket *sock)
{
- return &sock->ssl;
+ return sock->ssl;
}
#endif
@@ -1018,12 +1208,21 @@ const char *ne_sock_error(const ne_socket *sock)
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);
+
+#if defined(HAVE_OPENSSL)
+ if (sock->ssl) {
+ SSL_shutdown(sock->ssl);
+ SSL_free(sock->ssl);
+ }
+#elif defined(HAVE_GNUTLS)
+ if (sock->ssl) {
+ do {
+ ret = gnutls_bye(sock->ssl, GNUTLS_SHUT_RDWR);
+ } while (ret < 0
+ && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN));
}
#endif
+
if (sock->fd < 0)
ret = 0;
else
diff --git a/neon/src/ne_socket.h b/neon/src/ne_socket.h
index 4da0c0c37..2061b4dd3 100644
--- a/neon/src/ne_socket.h
+++ b/neon/src/ne_socket.h
@@ -1,6 +1,6 @@
/*
socket handling interface
- Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -36,7 +36,7 @@ BEGIN_NEON_DECLS
#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. */
+/* Secure connection was closed without proper SSL shutdown. */
#define NE_SOCK_TRUNC (-5)
/* ne_socket represents a TCP socket. */
@@ -57,7 +57,7 @@ int ne_sock_init(void);
/* Shutdown any underlying libraries. */
void ne_sock_exit(void);
-/* Resolve the given hostname. 'flags' are currently ignored. Hex
+/* Resolve the given hostname. 'flags' must be zero. 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);
@@ -102,6 +102,9 @@ ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw);
* are not equal. */
int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2);
+/* Returns the type of the given network address. */
+ne_iaddr_type ne_iaddr_typeof(const ne_inet_addr *ia);
+
/* 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);
@@ -177,16 +180,14 @@ void ne_sock_read_timeout(ne_socket *sock, int timeout);
* 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);
+/* Negotiate an SSL connection on socket as an SSL server, using given
+ * SSL context. */
+int ne_sock_accept_ssl(ne_socket *sock, ne_ssl_context *ctx);
-/* Perform an SSL negotiation on 'sock', using given context. */
+/* Negotiate an SSL connection on socket as an SSL client, using given
+ * SSL 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/neon/src/ne_ssl.h b/neon/src/ne_ssl.h
index bda65a33d..99df86ac3 100644
--- a/neon/src/ne_ssl.h
+++ b/neon/src/ne_ssl.h
@@ -133,14 +133,31 @@ 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. */
+
+/* SSL context object. The interfaces to manipulate an SSL context
+ * are only needed when interfacing directly with ne_socket.h. */
typedef struct ne_ssl_context_s ne_ssl_context;
+/* Context creation modes: */
+#define NE_SSL_CTX_CLIENT (0) /* client context */
+#define NE_SSL_CTX_SERVER (1) /* default server context */
+#define NE_SSL_CTX_SERVERv2 (2) /* SSLv2-specific server context */
+
/* Create an SSL context. */
-ne_ssl_context *ne_ssl_context_create(void);
+ne_ssl_context *ne_ssl_context_create(int mode);
+
+/* Client mode: trust the given certificate 'cert' in context 'ctx'. */
+void ne_ssl_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert);
+
+/* Server mode: use given cert and key (filenames to PEM certificates). */
+int ne_ssl_context_keypair(ne_ssl_context *ctx,
+ const char *cert, const char *key);
-/* Trust the given certificate 'cert' in context 'ctx'. */
-void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert);
+/* Server mode: set client cert verification options: required is non-zero if
+ * a client cert is required, if ca_names is non-NULL it is a filename containing
+ * a set of PEM certs from which CA names are sent in the ccert request. */
+int ne_ssl_context_set_verify(ne_ssl_context *ctx, int required,
+ const char *ca_names, const char *verify_cas);
/* Destroy an SSL context. */
void ne_ssl_context_destroy(ne_ssl_context *ctx);
diff --git a/neon/src/ne_string.c b/neon/src/ne_string.c
index 3cddad901..31e3612b2 100644
--- a/neon/src/ne_string.c
+++ b/neon/src/ne_string.c
@@ -1,6 +1,6 @@
/*
String utility functions
- Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -31,6 +31,8 @@
#include <unistd.h>
#endif
+#include <stdio.h>
+
#include <ctype.h> /* for isprint() etc in ne_strclean() */
#include "ne_alloc.h"
@@ -101,172 +103,6 @@ char *ne_shave(char *str, const char *whitespace)
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);
@@ -517,3 +353,31 @@ char *ne_strerror(int errnum, char *buf, size_t buflen)
#endif
return buf;
}
+
+
+/* Wrapper for ne_snprintf. */
+size_t ne_snprintf(char *str, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+#ifdef HAVE_TRIO
+ trio_vsnprintf(str, size, fmt, ap);
+#else
+ vsnprintf(str, size, fmt, ap);
+#endif
+ va_end(ap);
+ str[size-1] = '\0';
+ return strlen(str);
+}
+
+/* Wrapper for ne_vsnprintf. */
+size_t ne_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
+{
+#ifdef HAVE_TRIO
+ trio_vsnprintf(str, size, fmt, ap);
+#else
+ vsnprintf(str, size, fmt, ap);
+#endif
+ str[size-1] = '\0';
+ return strlen(str);
+}
diff --git a/neon/src/ne_string.h b/neon/src/ne_string.h
index 81ede092e..9412c905b 100644
--- a/neon/src/ne_string.h
+++ b/neon/src/ne_string.h
@@ -1,6 +1,6 @@
/*
String utility functions
- Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2002, 2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -57,17 +57,6 @@ char *ne_base64(const unsigned char *text, size_t len);
* 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. */
@@ -132,6 +121,15 @@ 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')))
+/* Wrapper for snprintf: always NUL-terminates returned buffer, and
+ * returns strlen(str). */
+size_t ne_snprintf(char *str, size_t size, const char *fmt, ...)
+ ne_attribute((format(printf, 3, 4)));
+
+/* Wrapper for vsnprintf. */
+size_t ne_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
+ ne_attribute((format(printf, 3, 0)));
+
END_NEON_DECLS
#endif /* NE_STRING_H */
diff --git a/neon/src/ne_stubssl.c b/neon/src/ne_stubssl.c
index ff71e1e0f..8580b9713 100644
--- a/neon/src/ne_stubssl.c
+++ b/neon/src/ne_stubssl.c
@@ -1,6 +1,6 @@
/*
Stubs for SSL support when no SSL library has been configured
- Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -82,14 +82,22 @@ 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)
+ne_ssl_context *ne_ssl_context_create(int mode)
{
return NULL;
}
-void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert)
+void ne_ssl_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert)
{}
+int ne_ssl_context_set_verify(ne_ssl_context *ctx,
+ int required,
+ const char *ca_names,
+ const char *verify_cas)
+{
+ return -1;
+}
+
void ne_ssl_context_destroy(ne_ssl_context *ctx) {}
int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char digest[60])
diff --git a/neon/src/ne_uri.c b/neon/src/ne_uri.c
index 51107d1bc..0aab20d39 100644
--- a/neon/src/ne_uri.c
+++ b/neon/src/ne_uri.c
@@ -34,9 +34,10 @@
#include <stdlib.h>
#endif
+#include <stdio.h>
+
#include <ctype.h>
-#include "ne_utils.h" /* for 'min' */
#include "ne_string.h" /* for ne_buffer */
#include "ne_alloc.h"
#include "ne_uri.h"
@@ -277,6 +278,11 @@ int ne_uri_cmp(const ne_uri *u1, const ne_uri *u2)
#undef CMP
#undef CASECMP
+#ifndef WIN32
+#undef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
/* TODO: implement properly */
int ne_path_compare(const char *a, const char *b)
{
diff --git a/neon/src/ne_utils.c b/neon/src/ne_utils.c
index 76929b0d0..9b22f0950 100644
--- a/neon/src/ne_utils.c
+++ b/neon/src/ne_utils.c
@@ -30,10 +30,18 @@
#include <stdio.h>
#include <ctype.h> /* isdigit() for ne_parse_statusline */
-#ifdef NEON_SSL
+#ifdef NE_HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_OPENSSL
#include <openssl/opensslv.h>
#endif
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
/* libxml2: pick up the version string. */
#if defined(HAVE_LIBXML)
#include <libxml/xmlversion.h>
@@ -41,10 +49,6 @@
#include <expat.h>
#endif
-#ifdef NEON_ZLIB
-#include <zlib.h>
-#endif
-
#include "ne_utils.h"
#include "ne_string.h" /* for ne_strdup */
#include "ne_dates.h"
@@ -78,12 +82,15 @@ void ne_debug(int ch, const char *template, ...)
#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 ": "
+static const char version_string[] = "neon " NEON_VERSION ": "
#ifdef NEON_IS_LIBRARY
"Library build"
#else
"Bundled build"
#endif
+#ifdef NE_HAVE_IPV6
+ ", IPv6"
+#endif
#ifdef HAVE_EXPAT
", Expat"
/* expat >=1.95.2 exported the version */
@@ -95,19 +102,25 @@ static const char *version_string = "neon " NEON_VERSION ": "
", libxml " LIBXML_DOTTED_VERSION
#endif /* HAVE_LIBXML */
#endif /* !HAVE_EXPAT */
-#if defined(NEON_ZLIB) && defined(ZLIB_VERSION)
+#if defined(NE_HAVE_ZLIB) && defined(ZLIB_VERSION)
", zlib " ZLIB_VERSION
-#endif /* NEON_ZLIB && ... */
-#ifdef NEON_SOCKS
+#endif /* NE_HAVE_ZLIB && ... */
+#ifdef NE_HAVE_SOCKS
", SOCKSv5"
#endif
-#ifdef NEON_SSL
+#ifdef NE_HAVE_IDNA
+ ", IDNA"
+#endif
+#ifdef HAVE_OPENSSL
#ifdef OPENSSL_VERSION_TEXT
", " OPENSSL_VERSION_TEXT
#else
"OpenSSL (unknown version)"
#endif /* OPENSSL_VERSION_TEXT */
-#endif
+#endif /* HAVE_OPENSSL */
+#ifdef HAVE_GNUTLS
+ ", GNU TLS " LIBGNUTLS_VERSION
+#endif /* HAVE_GNUTLS */
"."
;
@@ -121,13 +134,31 @@ int ne_version_match(int major, int minor)
return (NEON_VERSION_MAJOR != major) || (NEON_VERSION_MINOR < minor);
}
-int ne_supports_ssl(void)
+int ne_has_support(int feature)
{
-#ifdef NEON_SSL
- return 1;
-#else
- return 0;
+ switch (feature) {
+#ifdef NE_HAVE_SSL
+ case NE_FEATURE_SSL:
+#endif
+#ifdef NE_HAVE_ZLIB
+ case NE_FEATURE_ZLIB:
+#endif
+#ifdef NE_HAVE_IPV6
+ case NE_FEATURE_IPV6:
+#endif
+#ifdef NE_HAVE_IDNA
+ case NE_FEATURE_IDNA:
+#endif
+#ifdef NE_HAVE_SOCKS
+ case NE_FEATURE_SOCKS:
+#endif
+#ifdef NE_HAVE_LFS
+ case NE_FEATURE_LFS:
#endif
+ return 1;
+ default:
+ return 0;
+ }
}
int ne_parse_statusline(const char *status_line, ne_status *st)
diff --git a/neon/src/ne_utils.h b/neon/src/ne_utils.h
index 87e659300..df07c2fa9 100644
--- a/neon/src/ne_utils.h
+++ b/neon/src/ne_utils.h
@@ -1,6 +1,6 @@
/*
HTTP utility functions
- Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -46,22 +46,16 @@ const char *ne_version_string(void);
* 'minor'. */
int ne_version_match(int major, int minor);
-/* Returns non-zero if neon has support for SSL. */
-int ne_supports_ssl(void);
+#define NE_FEATURE_SSL (1) /* SSL/TLS support */
+#define NE_FEATURE_ZLIB (2) /* zlib compression in compress interface */
+#define NE_FEATURE_IPV6 (3) /* IPv6 is supported in resolver */
+#define NE_FEATURE_IDNA (4) /* internationalized domain name support */
+#define NE_FEATURE_LFS (5) /* large file support */
+#define NE_FEATURE_SOCKS (6) /* SOCKSv5 support */
-/* 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
+/* Returns non-zero if neon has support for given feature code
+ * NE_FEATURE_*. */
+int ne_has_support(int feature);
/* CONSIDER: mutt has a nicer way of way of doing debugging output... maybe
* switch to like that. */
diff --git a/neon/src/ne_xml.c b/neon/src/ne_xml.c
index d405fa2ed..a0c67a91e 100644
--- a/neon/src/ne_xml.c
+++ b/neon/src/ne_xml.c
@@ -1,5 +1,5 @@
/*
- Higher Level Interface to XML Parsers.
+ Wrapper interface to XML parser
Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
@@ -96,9 +96,11 @@ struct ne_xml_parser_s {
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 failure; /* zero whilst parse should continue */
int prune; /* if non-zero, depth within a dead branch */
+ int bom_pos;
+
#ifdef HAVE_EXPAT
XML_Parser parser;
char *encoding;
@@ -193,6 +195,21 @@ const char *ne_xml_doc_encoding(const ne_xml_parser *p)
#endif
}
+/* The first character of the REC-xml-names "NCName" rule excludes
+ * "Digit | '.' | '-' | '_' | CombiningChar | Extender"; the XML
+ * parser will not enforce this rule in a namespace declaration since
+ * it treats the entire attribute name as a REC-xml "Name" rule. It's
+ * too hard to check for all of CombiningChar | Digit | Extender here,
+ * but the valid_ncname_ch1 macro catches some of the rest. */
+
+/* Return non-zero if 'ch' is an invalid start character for an NCName: */
+#define invalid_ncname_ch1(ch) ((ch) == '\0' || strchr("-.0123456789", (ch)) != NULL)
+
+/* Subversion repositories have been deployed which use property names
+ * marshalled as NCNames including a colon character; these should
+ * also be rejected but will be allowed for the time being. */
+#define invalid_ncname(xn) (invalid_ncname_ch1((xn)[0]))
+
/* Extract the namespace prefix declarations from 'atts'. */
static int declare_nspaces(ne_xml_parser *p, struct element *elm,
const ne_xml_char **atts)
@@ -200,13 +217,15 @@ static int declare_nspaces(ne_xml_parser *p, struct element *elm,
int n;
for (n = 0; atts && atts[n]; n += 2) {
- if (strcasecmp(atts[n], "xmlns") == 0) {
+ if (strcmp(atts[n], "xmlns") == 0) {
/* New default namespace */
elm->default_ns = ne_strdup(atts[n+1]);
- } else if (strncasecmp(atts[n], "xmlns:", 6) == 0) {
+ } else if (strncmp(atts[n], "xmlns:", 6) == 0) {
struct namespace *ns;
- if (atts[n][6] == '\0' || atts[n+1][0] == '\0') {
+ /* Reject some invalid NCNames as namespace prefix, and an
+ * empty URI as the namespace URI */
+ if (invalid_ncname(atts[n] + 6) || atts[n+1][0] == '\0') {
ne_snprintf(p->error, ERR_SIZE,
("XML parse error at line %d: invalid namespace "
"declaration"), ne_xml_currentline(p));
@@ -243,22 +262,20 @@ static int expand_qname(ne_xml_parser *p, struct element *elm,
elm->name = ne_strdup(qname);
elm->nspace = e->default_ns;
+ } else if (invalid_ncname(pfx + 1) || qname == pfx) {
+ ne_snprintf(p->error, ERR_SIZE,
+ _("XML parse error at line %d: invalid element name"),
+ ne_xml_currentline(p));
+ return -1;
} 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"),
+ ("XML parse error at line %d: undeclared namespace prefix"),
ne_xml_currentline(p));
return -1;
}
@@ -275,7 +292,7 @@ static void start_element(void *userdata, const ne_xml_char *name,
struct handler *hand;
int state = NE_XML_DECLINE;
- if (!p->valid) return;
+ if (p->failure) return;
if (p->prune) {
p->prune++;
@@ -288,7 +305,7 @@ static void start_element(void *userdata, const ne_xml_char *name,
p->current = elm;
if (declare_nspaces(p, elm, atts) || expand_qname(p, elm, name)) {
- p->valid = 0;
+ p->failure = 1;
return;
}
@@ -308,8 +325,8 @@ static void start_element(void *userdata, const ne_xml_char *name,
else if (state == NE_XML_DECLINE)
/* prune this branch. */
p->prune++;
- else /* state == NE_XML_ABORT */
- p->valid = 0;
+ else /* state < 0 => abort parse */
+ p->failure = state;
}
/* Destroys an element structure. */
@@ -337,12 +354,11 @@ 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 (p->failure || 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;
+ if (elm->handler->cdata_cb) {
+ p->failure = elm->handler->cdata_cb(elm->handler->userdata, elm->state, data, len);
+ NE_DEBUG(NE_DBG_XML, "Cdata callback returned %d.\n", p->failure);
}
}
@@ -352,15 +368,17 @@ 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->failure) 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;
+ } else if (elm->handler->endelm_cb) {
+ p->failure = elm->handler->endelm_cb(elm->handler->userdata, elm->state,
+ elm->nspace, elm->name);
+ if (p->failure) {
+ NE_DEBUG(NE_DBG_XML, "XML: end-element for %d failed with %d.\n",
+ elm->state, p->failure);
+ }
}
NE_DEBUG(NE_DBG_XMLPARSE, "XML: end-element (%d, {%s, %s})\n",
@@ -397,8 +415,6 @@ static const char *resolve_nspace(const struct element *elm,
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 = "";
@@ -448,24 +464,22 @@ void ne_xml_push_handler(ne_xml_parser *p,
}
}
-void ne_xml_parse_v(void *userdata, const char *block, size_t len)
+int 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);
+ return ne_xml_parse(p, (const ne_xml_char *)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)
+#define BOM_UTF8 "\xEF\xBB\xBF" /* UTF-8 BOM */
+
+int 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) {
+ if (p->failure) {
NE_DEBUG(NE_DBG_XML, "Not parsing %" NE_FMT_SIZE_T " bytes.\n",
len);
- return;
+ return p->failure;
}
if (len == 0) {
flag = -1;
@@ -476,34 +490,54 @@ void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len)
len);
flag = 0;
}
- /* Note, don't write a parser error if !p->valid, since an error
+
+ if (p->bom_pos < 3) {
+ while (len > 0 && p->bom_pos < 3 &&
+ block[0] == BOM_UTF8[p->bom_pos]) {
+ block++;
+ len--;
+ p->bom_pos++;
+ }
+ if (len == 0)
+ return 0;
+ if (p->bom_pos == 0) {
+ p->bom_pos = 3; /* no BOM */
+ } else if (p->bom_pos > 0 && p->bom_pos < 3) {
+ strcpy(p->error, _("Invalid Byte Order Mark"));
+ return p->failure = 1;
+ }
+ }
+
+ /* Note, don't write a parser error if p->failure, 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) {
+ if (ret == 0 && p->failure == 0) {
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;
+ p->failure = 1;
}
#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) {
+ if (p->parser->errNo && p->failure == 0) {
ne_snprintf(p->error, ERR_SIZE, "XML parse error at line %d.",
ne_xml_currentline(p));
- p->valid = 0;
+ p->failure = 1;
+ NE_DEBUG(NE_DBG_XMLPARSE, "XML parse error: %s\n", p->error);
}
#endif
+ return p->failure;
}
-int ne_xml_valid(ne_xml_parser *p)
+int ne_xml_failed(ne_xml_parser *p)
{
- return p->valid;
+ return p->failure;
}
void ne_xml_destroy(ne_xml_parser *p)
@@ -553,11 +587,12 @@ static void sax_error(void *ctx, const char *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;
+ if (p->failure == 0) {
+ ne_snprintf(p->error, ERR_SIZE,
+ _("XML parse error at line %d: %s."),
+ p->parser->input->line, buf);
+ p->failure = 1;
+ }
}
#endif
diff --git a/neon/src/ne_xml.h b/neon/src/ne_xml.h
index 58cde9acb..50b350a8f 100644
--- a/neon/src/ne_xml.h
+++ b/neon/src/ne_xml.h
@@ -1,6 +1,6 @@
/*
neon XML parser interface
- Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -86,31 +86,38 @@ void ne_xml_push_handler(ne_xml_parser *p,
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);
+/* ne_xml_failed returns non-zero if there was an error during
+ * parsing, or zero if the parse completed successfully. The return
+ * value is equal to that of the last ne_xml_parse call for this
+ * parser. */
+int ne_xml_failed(ne_xml_parser *p);
+
+/* Set error string for parser: the string may be truncated. */
+void ne_xml_set_error(ne_xml_parser *p, const char *msg);
+
+/* Return the error string (never NULL). After ne_xml_failed returns
+ * >0, this will describe the parse error. Otherwise it will be a
+ * default error string. */
+const char *ne_xml_get_error(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);
+/* Parse the given block of input of length len. Parser must be
+ * called with len=0 to signify the end of the document (for that
+ * case, the block argument is ignored). Returns zero on success, or
+ * non-zero on error: for an XML syntax error, a positive number is
+ * returned; if parsing is aborted by a caller-supplied callback, that
+ * callback's return value is returned. */
+int 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);
+int 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 string for parser. */
-void ne_xml_set_error(ne_xml_parser *p, const char *msg);
-
-/* Return the error string for the parser and never NULL. */
-const char *ne_xml_get_error(ne_xml_parser *p);
-
/* 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. */
diff --git a/neon/test/ChangeLog b/neon/test/ChangeLog
index 080a87fcc..d484dedfa 100644
--- a/neon/test/ChangeLog
+++ b/neon/test/ChangeLog
@@ -1,6 +1,25 @@
+Wed Aug 25 21:05:28 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * cookies.c: Removed.
+
+ * Makefile.in: Updated.
+
+Wed Aug 25 19:47:28 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * socket.c (do_connect, begin): Simplify do_connect use.
+
+Wed Aug 25 18:28:19 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * xml.c (matches, fail_parse): Test for UTF-8 BOM handling.
+
+Mon Jul 5 18:41:07 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * basic.c (content_type): Test for correct default charset for
+ text/xml content-type by RFC3280.
+
Mon Jul 5 10:59:17 2004 Joe Orton <joe@manyfish.co.uk>
- Add regression tests for trio of ne_compress.c bugs:
+ Add XFAIL regression tests for trio of ne_compress.c bugs:
* compress.c (reader): Validate that a size=0 call comes only
after the expected response data, and use struct string.
@@ -16,12 +35,6 @@ Sun Jul 4 21:55:00 2004 Joe Orton <joe@manyfish.co.uk>
* sockets.c: All callers updated.
-Tue May 18 21:00:07 2004 Joe Orton <joe@manyfish.co.uk>
-
- * ssl.c (fail_ssl_request):
- * basic.c (do_range): Fix false negatives from gcc
- -Wformat-security.
-
Sun May 2 21:16:45 2004 Joe Orton <joe@manyfish.co.uk>
* util-tests.c (regress_dates): Add regression tests.
@@ -31,6 +44,39 @@ Wed Apr 14 10:45:43 2004 Joe Orton <joe@manyfish.co.uk>
* props.c (regress, patch_regress): Add regression tests for
CAN-2004-0179 issues.
+Thu Apr 8 13:57:04 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * largefile.c (read_large_response): Go faster: turn off debugging
+ during request dispatch.
+
+Wed Apr 7 13:39:37 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * auth.c (basic): Add some multi-scheme challenges.
+
+Wed Apr 7 13:14:16 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * request.c (s_progress): Use NE_FMT_OFF_T for printing off_t's
+ throughout.
+
+Sun Feb 22 23:38:05 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * request.c (expect_100_once, serve_100_once): Adjust for new
+ 100-continue interface.
+ (expect_100_nobody): New test.
+
+Sun Feb 22 20:39:15 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * cookies.c (parsing): Use ne_cookie_empty_cache.
+
+Sun Feb 22 17:28:41 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * props.c (pfind_simple): Test for whitespace handling.
+
+Sun Feb 22 16:31:52 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * auth.c (basic): Test handling of Basic challenge in presence of
+ multiple challenges.
+
Sun Feb 15 12:34:13 2004 Joe Orton <joe@manyfish.co.uk>
* makekeys.sh, openssl.conf: Create new utf8subj.cert,
@@ -39,10 +85,67 @@ Sun Feb 15 12:34:13 2004 Joe Orton <joe@manyfish.co.uk>
* ssl.c (dname_readable): Test that ne_ssl_readable_dname always
gives back UTF-8.
+Sat Feb 14 21:59:17 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * xml.c (fail_parse): Add tests for invalid NCNames in namespace
+ prefix declaration and as element names.
+
+Sun Jan 25 15:21:56 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * largefile.c (serve_large_response, read_large_response): New
+ test.
+
+Sat Jan 24 18:10:14 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in: Fix test suite for 'make' implementatinos which
+ don't handle single-suffix inference rules.
+
+Sat Jan 3 14:10:14 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * largefile.c (send_high_offset): Renamed from send_large_file.
+
+Sat Jan 3 13:57:16 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in (BASIC_TESTS): Add cookies.
+
+Thu Jan 1 17:42:30 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (support): Check for NE_FEATURE_LFS.
+
+ * largefile.c: New file.
+
+ * Makefile.in: Add lfs-check, largefile, largefile.lo targets.
+
+Sat Nov 15 08:04:22 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * request.c (idna_hostname, dup_header, serve_check_host): New
+ test.
+
+Fri Nov 14 14:06:57 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (support): Test for NE_FEATURE_IDNA.
+
+Fri Nov 14 11:26:29 2003 Joe Orton <joe@manyfish.co.uk>
+
+ * acl.c: Remove NEON_NODAV condition.
+
+ * stubs.c: Use new NE_HAVE_* conditions.
+
+ * util-tests.c (supports): Test new ne_has_support interface.
+
Thu Nov 13 20:33:44 2003 Joe Orton <joe@manyfish.co.uk>
* request.c (no_body_205): New test.
+Tue Nov 11 20:36:43 2003 Joe Orton <joe@manyfish.co.uk>
+
+ Adjust for ne_xml_valid->ne_xml_failure API change.
+
+ * xml.c (chardata, startelm_abort, endelm_abort, parse_match):
+ Check for propagation of negative failure codes.
+ (fail_parse): Check for positive failure code.
+ (attributes): Use ne_xml_failure.
+
Sat Oct 25 00:11:29 2003 Joe Orton <joe@manyfish.co.uk>
* ssl.c (fail_truncated_eof): Remove test.
diff --git a/neon/test/Makefile.in b/neon/test/Makefile.in
index 9d89e425e..b49fa550e 100644
--- a/neon/test/Makefile.in
+++ b/neon/test/Makefile.in
@@ -19,10 +19,11 @@ CC = @CC@
OPENSSL = @OPENSSL@
HELPERS = @HELPERS@
-BASIC_TESTS = uri-tests util-tests string-tests session socket \
- request auth basic stubs redirect
+BASIC_TESTS = uri-tests util-tests string-tests socket \
+ session request auth basic stubs redirect
ZLIB_TESTS = compress
-ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz corrupt1.gz corrupt2.gz
+ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz \
+ corrupt1.gz corrupt2.gz empty.gz
DAV_TESTS = xml acl props lock
SSL_TESTS = socket-ssl ssl
SSL_HELPERS = ca-stamp
@@ -52,7 +53,7 @@ clean:
rm -f $(TESTS) $(HELPERS) *.*o common/*.*o libtest.a *.log
rm -rf ca .libs
rm -f ca-stamp client.key *.csr ssigned.pem wrongcn.pem \
- server.cert client.cert client.p12 *.cert
+ server.cert client.cert client.p12 *.cert sparse.bin
check: $(TESTS) $(HELPERS)
@SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh $(TESTS)
@@ -60,6 +61,9 @@ check: $(TESTS) $(HELPERS)
grind: $(TESTS) $(HELPERS)
@SRCDIR=$(srcdir) HARNESS="$(VALGRIND)" $(SHELL) $(srcdir)/run.sh $(TESTS)
+lfs-check: largefile $(LFS_HELPERS)
+ @SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh largefile
+
NEWS = $(top_srcdir)/NEWS
file1.gz: $(NEWS)
@@ -87,6 +91,9 @@ corrupt1.gz: file1.gz
corrupt2.gz:
cat $(NEWS) > $@
+empty.gz:
+ touch $@
+
# Dummy target to create the CA keys etc. makekeys stderr is redirected
# since it changes for every invocation; not helpful for regression
# testing.
@@ -107,9 +114,6 @@ $(LIBTEST): $(LIBOBJS)
.c.lo:
$(COMPILE) -c $< -o $@
-.lo:
- $(LINK) -o $@ $< $(LIBS)
-
# Recompile socket.c with SOCKET_SSL defined
socket-ssl.lo: $(srcdir)/socket.c $(HDRS)
$(COMPILE) -DSOCKET_SSL -c $(srcdir)/socket.c -o $@
@@ -120,6 +124,9 @@ socket-ssl: socket-ssl.lo $(LIBTEST)
resolve: resolve.lo $(LIBNEON)
$(LINK) -o $@ resolve.lo $(LIBNEON)
+common/tests.lo: $(srcdir)/common/tests.c $(OBJDEPS)
+common/child.lo: $(srcdir)/common/child.c $(OBJDEPS)
+utils.lo: $(srcdir)/utils.c $(OBJDEPS)
auth.lo: $(srcdir)/auth.c $(OBJDEPS)
uri-tests.lo: $(srcdir)/uri-tests.c $(OBJDEPS)
util-tests.lo: $(srcdir)/util-tests.c $(OBJDEPS)
@@ -137,26 +144,46 @@ session.lo: $(srcdir)/session.c $(OBJDEPS)
redirect.lo: $(srcdir)/redirect.c $(OBJDEPS)
basic.lo: $(srcdir)/basic.c $(OBJDEPS)
ssl.lo: $(srcdir)/ssl.c $(OBJDEPS)
-cookies.lo: $(srcdir)/cookies.c $(OBJDEPS)
+lock.lo: $(srcdir)/lock.c $(OBJDEPS)
+largefile.lo: $(srcdir)/largefile.c $(OBJDEPS)
auth: auth.lo $(DEPS)
+ $(LINK) -o $@ auth.lo $(DEPS)
basic: basic.lo $(DEPS)
+ $(LINK) -o $@ basic.lo $(DEPS)
uri-tests: uri-tests.lo $(DEPS)
+ $(LINK) -o $@ uri-tests.lo $(DEPS)
util-tests: util-tests.lo $(DEPS)
+ $(LINK) -o $@ util-tests.lo $(DEPS)
string-tests: string-tests.lo $(DEPS)
+ $(LINK) -o $@ string-tests.lo $(DEPS)
socket: socket.lo $(DEPS)
+ $(LINK) -o $@ socket.lo $(DEPS)
server: server.lo $(DEPS)
+ $(LINK) -o $@ server.lo $(DEPS)
request: request.lo $(DEPS)
+ $(LINK) -o $@ request.lo $(DEPS)
regress: regress.lo $(DEPS)
+ $(LINK) -o $@ regress.lo $(DEPS)
compress: compress.lo $(DEPS)
+ $(LINK) -o $@ compress.lo $(DEPS)
acl: acl.lo $(DEPS)
+ $(LINK) -o $@ acl.lo $(DEPS)
utils: utils.lo $(DEPS)
+ $(LINK) -o $@ utils.lo $(DEPS)
stubs: stubs.lo $(DEPS)
+ $(LINK) -o $@ stubs.lo $(DEPS)
props: props.lo $(DEPS)
+ $(LINK) -o $@ props.lo $(DEPS)
session: session.lo $(DEPS)
+ $(LINK) -o $@ session.lo $(DEPS)
redirect: redirect.lo $(DEPS)
-basic: basic.lo $(DEPS)
+ $(LINK) -o $@ redirect.lo $(DEPS)
ssl: ssl.lo $(DEPS)
+ $(LINK) -o $@ ssl.lo $(DEPS)
xml: xml.lo $(DEPS)
+ $(LINK) -o $@ xml.lo $(DEPS)
lock: lock.lo $(DEPS)
-cookies: cookies.lo $(DEPS)
+ $(LINK) -o $@ lock.lo $(DEPS)
+largefile: largefile.lo $(DEPS)
+ $(LINK) -o $@ largefile.lo $(DEPS)
diff --git a/neon/test/acl.c b/neon/test/acl.c
index 14008b16a..2e72caa80 100644
--- a/neon/test/acl.c
+++ b/neon/test/acl.c
@@ -24,18 +24,6 @@
#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)
@@ -111,5 +99,3 @@ ne_test tests[] = {
T(deny_byprop),
T(NULL)
};
-
-#endif /* NEON_NODAV */
diff --git a/neon/test/auth.c b/neon/test/auth.c
index 30fe88eb7..7f7e99379 100644
--- a/neon/test/auth.c
+++ b/neon/test/auth.c
@@ -1,6 +1,6 @@
/*
Authentication tests
- Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,11 +40,16 @@
static const char username[] = "Aladdin", password[] = "open sesame";
static int auth_failed;
-static const char www_wally[] = "WWW-Authenticate: Basic realm=WallyWorld";
+#define BASIC_WALLY "Basic realm=WallyWorld"
+#define CHAL_WALLY "WWW-Authenticate: " BASIC_WALLY
static int auth_cb(void *userdata, const char *realm, int tries,
char *un, char *pw)
{
+ if (strcmp(realm, "WallyWorld")) {
+ NE_DEBUG(NE_DBG_HTTP, "Got wrong realm '%s'!\n", realm);
+ return -1;
+ }
strcpy(un, username);
strcpy(pw, password);
return tries;
@@ -87,6 +92,8 @@ static int send_response(ne_socket *sock, const char *hdr, int code, int eoc)
* second doesn't. */
static int auth_serve(ne_socket *sock, void *userdata)
{
+ char *hdr = userdata;
+
auth_failed = 1;
/* Register globals for discard_request. */
@@ -94,7 +101,7 @@ static int auth_serve(ne_socket *sock, void *userdata)
want_header = "Authorization";
discard_request(sock);
- send_response(sock, www_wally, 401, 0);
+ send_response(sock, hdr, 401, 0);
discard_request(sock);
send_response(sock, NULL, auth_failed?500:200, 1);
@@ -102,33 +109,71 @@ static int auth_serve(ne_socket *sock, void *userdata)
return 0;
}
+/* Test that various Basic auth challenges are correctly handled. */
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"));
+ const char *hdrs[] = {
+ /* simplest case */
+ CHAL_WALLY,
+
+ /* several challenges, one header */
+ "WWW-Authenticate: BarFooScheme, " BASIC_WALLY,
+
+ /* several challenges, one header */
+ CHAL_WALLY ", BarFooScheme realm=\"PenguinWorld\"",
+
+ /* whitespace tests. */
+ "WWW-Authenticate: Basic realm=WallyWorld ",
+
+ /* nego test. */
+ "WWW-Authenticate: Negotiate fish, Basic realm=WallyWorld",
+
+ /* nego test. */
+ "WWW-Authenticate: Negotiate fish, bar=boo, Basic realm=WallyWorld",
+
+ /* multi-header case 1 */
+ "WWW-Authenticate: BarFooScheme\r\n"
+ CHAL_WALLY,
+
+ /* multi-header cases 1 */
+ CHAL_WALLY "\r\n"
+ "WWW-Authenticate: BarFooScheme bar=\"foo\"",
+
+ /* multi-header case 3 */
+ "WWW-Authenticate: FooBarChall foo=\"bar\"\r\n"
+ CHAL_WALLY "\r\n"
+ "WWW-Authenticate: BarFooScheme bar=\"foo\""
+ };
+ size_t n;
+
+ for (n = 0; n < sizeof(hdrs)/sizeof(hdrs[0]); n++) {
+ ne_session *sess;
+
+ CALL(make_session(&sess, auth_serve, (void *)hdrs[n]));
+ ne_set_server_auth(sess, auth_cb, NULL);
+
+ CALL(any_2xx_request(sess, "/norman"));
+
+ ne_session_destroy(sess);
+ CALL(await_server());
+ }
- 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);
+ send_response(sock, CHAL_WALLY, 401, 0);
discard_request(sock);
- send_response(sock, www_wally, 401, 0);
+ send_response(sock, CHAL_WALLY, 401, 0);
discard_request(sock);
send_response(sock, NULL, 200, 0);
discard_request(sock);
- send_response(sock, www_wally, 401, 0);
+ send_response(sock, CHAL_WALLY, 401, 0);
discard_request(sock);
send_response(sock, NULL, 200, 0);
@@ -140,19 +185,19 @@ static int retry_serve(ne_socket *sock, void *ud)
send_response(sock, NULL, 200, 0);
discard_request(sock);
- send_response(sock, www_wally, 401, 0);
+ send_response(sock, CHAL_WALLY, 401, 0);
discard_request(sock);
send_response(sock, NULL, 200, 0);
discard_request(sock);
- send_response(sock, www_wally, 401, 0);
+ send_response(sock, CHAL_WALLY, 401, 0);
discard_request(sock);
- send_response(sock, www_wally, 401, 0);
+ send_response(sock, CHAL_WALLY, 401, 0);
discard_request(sock);
- send_response(sock, www_wally, 401, 0);
+ send_response(sock, CHAL_WALLY, 401, 0);
discard_request(sock);
send_response(sock, NULL, 200, 0);
@@ -296,8 +341,8 @@ static int tunnel_regress(void)
/* test digest auth 2617-style. */
-/* negotiation: within a single header, multiple headers.
- * check digest has precedence */
+/* test that digest has precedence over Basic for multi-scheme
+ * challenges */
/* test auth-int, auth-int FAILURE. chunk trailers/non-trailer */
diff --git a/neon/test/basic.c b/neon/test/basic.c
index 9ffe0e188..cabe2d248 100644
--- a/neon/test/basic.c
+++ b/neon/test/basic.c
@@ -46,8 +46,10 @@ static int content_type(void)
{ "foo/bar", "foo", "bar", NULL },
{ "foo/bar ", "foo", "bar", NULL },
{ "application/xml", "application", "xml", NULL },
- /* text/ subtypes default to charset ISO-8859-1. */
+ /* text/ subtypes default to charset ISO-8859-1, per 2616. */
{ "text/lemon", "text", "lemon", "ISO-8859-1" },
+ /* text/xml defaults to charset us-ascii, per 3280 */
+ { "text/xml", "text", "xml", "us-ascii" },
#undef TXU
#define TXU "text", "xml", "utf-8"
/* 2616 doesn't *say* that charset can be quoted, but bets are
diff --git a/neon/test/ca1.pem b/neon/test/ca1.pem
new file mode 100644
index 000000000..a52ebe44b
--- /dev/null
+++ b/neon/test/ca1.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICQTCCAeugAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnzELMAkGA1UEBhMCR0Ix
+FTATBgNVBAgTDExpbmNvbG5zaGlyZTEQMA4GA1UEBxMHTGluY29sbjERMA8GA1UE
+ChMIQ0FzIEx0ZC4xGDAWBgNVBAsTD0ZpcnN0IFJhbmRvbSBDQTEaMBgGA1UEAxMR
+Zmlyc3QuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y
+ZzAeFw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGfMQswCQYDVQQGEwJH
+QjEVMBMGA1UECBMMTGluY29sbnNoaXJlMRAwDgYDVQQHEwdMaW5jb2xuMREwDwYD
+VQQKEwhDQXMgTHRkLjEYMBYGA1UECxMPRmlyc3QgUmFuZG9tIENBMRowGAYDVQQD
+ExFmaXJzdC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPY
+WrNlH1FP+psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEA
+AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAOvtDu4Cs6nj5DG1
+NG5gqCkrVWqvjSd1jzsxOJBoN9gk/d87rAzNCvK4dTkFf9btdRZwv5OZFJzKLcU8
+f7qzMnM=
+-----END CERTIFICATE-----
diff --git a/neon/test/ca2.pem b/neon/test/ca2.pem
new file mode 100644
index 000000000..d0708b993
--- /dev/null
+++ b/neon/test/ca2.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICPzCCAemgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnjELMAkGA1UEBhMCR0Ix
+ETAPBgNVBAgTCENvcm53YWxsMREwDwYDVQQHEwhGYWxtb3V0aDERMA8GA1UEChMI
+Q0FzIEx0ZC4xGTAXBgNVBAsTEFNlY29uZCBSYW5kb20gQ0ExGzAZBgNVBAMTEnNl
+Y29uZC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3Jn
+MB4XDTA0MTAyOTEzMTM0N1oXDTA3MDQxNzEzMTM0N1owgZ4xCzAJBgNVBAYTAkdC
+MREwDwYDVQQIEwhDb3Jud2FsbDERMA8GA1UEBxMIRmFsbW91dGgxETAPBgNVBAoT
+CENBcyBMdGQuMRkwFwYDVQQLExBTZWNvbmQgUmFuZG9tIENBMRswGQYDVQQDExJz
+ZWNvbmQuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y
+ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9KloEgT2Fqz
+ZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRhtAgMBAAGj
+EDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQANIBQyploOs1JrmzZu
+TATbjH6wsoEWdAUKs+UMuigRHskqK4j6PutoPWYFB+Ebxfh1miiaWS/KwFzWmPte
+iNeK
+-----END CERTIFICATE-----
diff --git a/neon/test/ca3.pem b/neon/test/ca3.pem
new file mode 100644
index 000000000..c445e497a
--- /dev/null
+++ b/neon/test/ca3.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICNzCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCR0Ix
+EDAOBgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENB
+cyBMdGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJk
+LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN
+MDQxMDI5MTMxMzQ3WhcNMDcwNDE3MTMxMzQ3WjCBmjELMAkGA1UEBhMCR0IxEDAO
+BgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENBcyBM
+dGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJkLmV4
+YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwXDANBgkq
+hkiG9w0BAQEFAANLADBIAkEA80VObGfPglkD7z5KicPSpaBIE9has2UfUU/6mybk
+QZkaYYka9IOyToeTK+y12ikoXnRS4RonbrciisCGSZ0YbQIDAQABoxAwDjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAwL7CNh2/1USUCHz55X/5BmySC1UJ
+mImoh0DD0LocIQJUjGsScm3M8HXarGxl6Vfkklt/k2TMAy6YCTv+1/dazw==
+-----END CERTIFICATE-----
diff --git a/neon/test/ca4.pem b/neon/test/ca4.pem
new file mode 100644
index 000000000..d08509a2e
--- /dev/null
+++ b/neon/test/ca4.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICOzCCAeWgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnDELMAkGA1UEBhMCR0Ix
+EDAOBgNVBAgTB05vcmZvbGsxEDAOBgNVBAcTB05vcndpY2gxETAPBgNVBAoTCENB
+cyBMdGQuMRkwFwYDVQQLExBGb3VydGggUmFuZG9tIENBMRswGQYDVQQDExJmb3Vy
+dGguZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZzAe
+Fw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGcMQswCQYDVQQGEwJHQjEQ
+MA4GA1UECBMHTm9yZm9sazEQMA4GA1UEBxMHTm9yd2ljaDERMA8GA1UEChMIQ0Fz
+IEx0ZC4xGTAXBgNVBAsTEEZvdXJ0aCBSYW5kb20gQ0ExGzAZBgNVBAMTEmZvdXJ0
+aC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3JnMFww
+DQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP
++psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAaMQMA4w
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAMxApCvWUMkO47bO/Cpy6ow2
+BTb8BQW+NeSXV3EIlu1IPVJ9e1pcfTtVP5z5bseeUQSMcRWwEbV3Aie+a5xsT9Y=
+-----END CERTIFICATE-----
diff --git a/neon/test/calist.pem b/neon/test/calist.pem
new file mode 100644
index 000000000..2ea2e13dd
--- /dev/null
+++ b/neon/test/calist.pem
@@ -0,0 +1,58 @@
+-----BEGIN CERTIFICATE-----
+MIICQTCCAeugAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnzELMAkGA1UEBhMCR0Ix
+FTATBgNVBAgTDExpbmNvbG5zaGlyZTEQMA4GA1UEBxMHTGluY29sbjERMA8GA1UE
+ChMIQ0FzIEx0ZC4xGDAWBgNVBAsTD0ZpcnN0IFJhbmRvbSBDQTEaMBgGA1UEAxMR
+Zmlyc3QuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y
+ZzAeFw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGfMQswCQYDVQQGEwJH
+QjEVMBMGA1UECBMMTGluY29sbnNoaXJlMRAwDgYDVQQHEwdMaW5jb2xuMREwDwYD
+VQQKEwhDQXMgTHRkLjEYMBYGA1UECxMPRmlyc3QgUmFuZG9tIENBMRowGAYDVQQD
+ExFmaXJzdC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPY
+WrNlH1FP+psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEA
+AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAOvtDu4Cs6nj5DG1
+NG5gqCkrVWqvjSd1jzsxOJBoN9gk/d87rAzNCvK4dTkFf9btdRZwv5OZFJzKLcU8
+f7qzMnM=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPzCCAemgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnjELMAkGA1UEBhMCR0Ix
+ETAPBgNVBAgTCENvcm53YWxsMREwDwYDVQQHEwhGYWxtb3V0aDERMA8GA1UEChMI
+Q0FzIEx0ZC4xGTAXBgNVBAsTEFNlY29uZCBSYW5kb20gQ0ExGzAZBgNVBAMTEnNl
+Y29uZC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3Jn
+MB4XDTA0MTAyOTEzMTM0N1oXDTA3MDQxNzEzMTM0N1owgZ4xCzAJBgNVBAYTAkdC
+MREwDwYDVQQIEwhDb3Jud2FsbDERMA8GA1UEBxMIRmFsbW91dGgxETAPBgNVBAoT
+CENBcyBMdGQuMRkwFwYDVQQLExBTZWNvbmQgUmFuZG9tIENBMRswGQYDVQQDExJz
+ZWNvbmQuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y
+ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9KloEgT2Fqz
+ZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRhtAgMBAAGj
+EDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQANIBQyploOs1JrmzZu
+TATbjH6wsoEWdAUKs+UMuigRHskqK4j6PutoPWYFB+Ebxfh1miiaWS/KwFzWmPte
+iNeK
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICNzCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCR0Ix
+EDAOBgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENB
+cyBMdGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJk
+LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN
+MDQxMDI5MTMxMzQ3WhcNMDcwNDE3MTMxMzQ3WjCBmjELMAkGA1UEBhMCR0IxEDAO
+BgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENBcyBM
+dGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJkLmV4
+YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwXDANBgkq
+hkiG9w0BAQEFAANLADBIAkEA80VObGfPglkD7z5KicPSpaBIE9has2UfUU/6mybk
+QZkaYYka9IOyToeTK+y12ikoXnRS4RonbrciisCGSZ0YbQIDAQABoxAwDjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAwL7CNh2/1USUCHz55X/5BmySC1UJ
+mImoh0DD0LocIQJUjGsScm3M8HXarGxl6Vfkklt/k2TMAy6YCTv+1/dazw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICOzCCAeWgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnDELMAkGA1UEBhMCR0Ix
+EDAOBgNVBAgTB05vcmZvbGsxEDAOBgNVBAcTB05vcndpY2gxETAPBgNVBAoTCENB
+cyBMdGQuMRkwFwYDVQQLExBGb3VydGggUmFuZG9tIENBMRswGQYDVQQDExJmb3Vy
+dGguZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZzAe
+Fw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGcMQswCQYDVQQGEwJHQjEQ
+MA4GA1UECBMHTm9yZm9sazEQMA4GA1UEBxMHTm9yd2ljaDERMA8GA1UEChMIQ0Fz
+IEx0ZC4xGTAXBgNVBAsTEEZvdXJ0aCBSYW5kb20gQ0ExGzAZBgNVBAMTEmZvdXJ0
+aC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3JnMFww
+DQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP
++psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAaMQMA4w
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAMxApCvWUMkO47bO/Cpy6ow2
+BTb8BQW+NeSXV3EIlu1IPVJ9e1pcfTtVP5z5bseeUQSMcRWwEbV3Aie+a5xsT9Y=
+-----END CERTIFICATE-----
diff --git a/neon/test/chain.pem b/neon/test/chain.pem
new file mode 100644
index 000000000..fbaf061ac
--- /dev/null
+++ b/neon/test/chain.pem
@@ -0,0 +1,56 @@
+-----BEGIN CERTIFICATE-----
+MIICNzCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCVVMx
+EzARBgNVBAgTCkNhbGlmb3JuaWExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoT
+B05lb3NpZ24xFDASBgNVBAsTC1JhbmRvbSBEZXB0MRwwGgYDVQQDExNub3doZXJl
+LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN
+MDQxMDI5MTMxMzQ3WhcNMDcwNDE3MTMxMzQ3WjCBmjELMAkGA1UEBhMCVVMxEzAR
+BgNVBAgTCkNhbGlmb3JuaWExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoTB05l
+b3NpZ24xFDASBgNVBAsTC1JhbmRvbSBEZXB0MRwwGgYDVQQDExNub3doZXJlLmV4
+YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwXDANBgkq
+hkiG9w0BAQEFAANLADBIAkEAxZZK/iqR8mWCs0u3A4Vz58fik0B2RpVLF8LFmXuu
+XCDNR4bjCwcOJpd+zpHrxb7LKaUYIA9Y3g1i90qylbgJBwIDAQABoxAwDjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAEGXxwEqcoz3uXP6/7QqmMhxDhrce
+xAGgVRXo546dK9XW3/E6HL7vM+euPnsF/Anhjv5PSRG5pLTy2hDE+r/QuQ==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: C=US, ST=California, L=Oakland, O=Neosign, OU=Random Dept, CN=nowhere.example.com/emailAddress=neon@webdav.org
+ Validity
+ Not Before: Oct 29 13:13:48 2004 GMT
+ Not After : Apr 17 13:13:48 2007 GMT
+ Subject: C=GB, ST=Cambridgeshire, L=Cambridge, O=Neon Hackers Ltd, OU=Neon QA Dept, CN=localhost/emailAddress=neon@webdav.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (512 bit)
+ Modulus (512 bit):
+ 00:f3:45:4e:6c:67:cf:82:59:03:ef:3e:4a:89:c3:
+ d2:a5:a0:48:13:d8:5a:b3:65:1f:51:4f:fa:9b:26:
+ e4:41:99:1a:61:89:1a:f4:83:b2:4e:87:93:2b:ec:
+ b5:da:29:28:5e:74:52:e1:1a:27:6e:b7:22:8a:c0:
+ 86:49:9d:18:6d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Signature Algorithm: md5WithRSAEncryption
+ 7e:43:47:e7:a6:40:a3:c0:08:ac:79:15:8b:f3:6f:50:5b:27:
+ a4:10:8d:61:b8:17:00:95:1e:bf:93:30:d6:19:99:b1:d6:a2:
+ ef:25:b4:c8:c0:06:0b:84:05:af:ae:75:f1:01:6a:83:b9:01:
+ 35:01:11:8f:8b:35:5b:dc:3b:09
+-----BEGIN CERTIFICATE-----
+MIICOjCCAeSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCVVMx
+EzARBgNVBAgTCkNhbGlmb3JuaWExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoT
+B05lb3NpZ24xFDASBgNVBAsTC1JhbmRvbSBEZXB0MRwwGgYDVQQDExNub3doZXJl
+LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN
+MDQxMDI5MTMxMzQ4WhcNMDcwNDE3MTMxMzQ4WjCBoDELMAkGA1UEBhMCR0IxFzAV
+BgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGTAXBgNV
+BAoTEE5lb24gSGFja2VycyBMdGQxFTATBgNVBAsTDE5lb24gUUEgRGVwdDESMBAG
+A1UEAxMJbG9jYWxob3N0MR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcw
+XDANBgkqhkiG9w0BAQEFAANLADBIAkEA80VObGfPglkD7z5KicPSpaBIE9has2Uf
+UU/6mybkQZkaYYka9IOyToeTK+y12ikoXnRS4RonbrciisCGSZ0YbQIDAQABow0w
+CzAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBBAUAA0EAfkNH56ZAo8AIrHkVi/NvUFsn
+pBCNYbgXAJUev5Mw1hmZsdai7yW0yMAGC4QFr6518QFqg7kBNQERj4s1W9w7CQ==
+-----END CERTIFICATE-----
diff --git a/neon/test/common/ChangeLog b/neon/test/common/ChangeLog
index 7e89c0854..f17d47dba 100644
--- a/neon/test/common/ChangeLog
+++ b/neon/test/common/ChangeLog
@@ -1,3 +1,11 @@
+Wed Aug 25 19:27:26 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * child.c (reset_socket): New function.
+
+Mon Jul 5 18:38:08 2004 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (main): Print 'xfail' for expected failures.
+
Tue Oct 7 21:19:56 2003 Joe Orton <joe@manyfish.co.uk>
* child.c (close_socket): New function.
diff --git a/neon/test/common/child.c b/neon/test/common/child.c
index 5756882cc..ee1ea536f 100644
--- a/neon/test/common/child.c
+++ b/neon/test/common/child.c
@@ -1,6 +1,6 @@
/*
Framework for testing with a server process
- Copyright (C) 2001-2002, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -131,6 +131,17 @@ void minisleep(void)
#endif
}
+int reset_socket(ne_socket *sock)
+{
+#ifdef SO_LINGER
+ /* Stevens' magic trick to send an RST on close(). */
+ struct linger l = {1, 0};
+ return setsockopt(ne_sock_fd(sock), SOL_SOCKET, SO_LINGER, &l, sizeof l);
+#else
+ return 1;
+#endif
+}
+
/* close 'sock', performing lingering close to avoid premature RST. */
static int close_socket(ne_socket *sock)
{
diff --git a/neon/test/common/child.h b/neon/test/common/child.h
index 24121f1c3..39bdb1d68 100644
--- a/neon/test/common/child.h
+++ b/neon/test/common/child.h
@@ -1,6 +1,6 @@
/*
Framework for testing with a server process
- Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,12 +21,26 @@
#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)(nsocket *sock, void *userdata);
+typedef int (*server_fn)(ne_socket *sock, void *userdata);
/* Spawns server child process:
* - forks child process.
@@ -38,10 +52,66 @@ typedef int (*server_fn)(nsocket *sock, void *userdata);
*/
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)))
+
+/* Tries to ensure that the socket will be closed using RST rather
+ * than FIN. */
+int reset_socket(ne_socket *sock);
+
+/* 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/neon/test/common/tests.c b/neon/test/common/tests.c
index 83f0c5271..ad1b8f55e 100644
--- a/neon/test/common/tests.c
+++ b/neon/test/common/tests.c
@@ -1,6 +1,6 @@
/*
Stupidly simple test framework
- Copyright (C) 2001-2002, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2001-2002, 2004, Joe Orton <joe@manyfish.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -41,6 +41,7 @@
#include <errno.h>
#endif
+#include "ne_string.h"
#include "ne_utils.h"
#include "ne_socket.h"
@@ -195,7 +196,7 @@ int main(int argc, char *argv[])
printf("-> running `%s':\n", test_suite);
for (n = 0; !aborted && tests[n].fn != NULL; n++) {
- int result;
+ int result, is_xfail = 0;
#ifdef NEON_MEMLEAK
size_t allocated = ne_alloc_used;
#endif
@@ -233,8 +234,10 @@ int main(int argc, char *argv[])
if (result == OK) {
t_context("test passed but expected failure");
result = FAIL;
- } else if (result == FAIL)
+ } else if (result == FAIL) {
result = OK;
+ is_xfail = 1;
+ }
}
/* align the result column if we've had warnings. */
@@ -244,7 +247,14 @@ int main(int argc, char *argv[])
switch (result) {
case OK:
- COL("32"); printf("pass"); NOCOL;
+ if (is_xfail) {
+ COL("32;07");
+ printf("xfail");
+ } else {
+ COL("32");
+ printf("pass");
+ }
+ NOCOL;
if (warned) {
printf(" (with %d warning%s)", warned, (warned > 1)?"s":"");
}
@@ -266,7 +276,7 @@ int main(int argc, char *argv[])
aborted = 1;
/* fall-through */
case SKIP:
- COL("44"); printf("SKIPPED"); NOCOL;
+ COL("44;37;01"); printf("SKIPPED"); NOCOL;
if (have_context) {
printf(" (%s)", test_context);
}
diff --git a/neon/test/compress.c b/neon/test/compress.c
index cb90c23c3..7e23af166 100644
--- a/neon/test/compress.c
+++ b/neon/test/compress.c
@@ -47,7 +47,7 @@ static int init(void)
#define EXTRA_DEBUG 0 /* disabled by default */
-static void reader(void *ud, const char *block, size_t len)
+static int reader(void *ud, const char *block, size_t len)
{
struct string *b = ud;
@@ -56,7 +56,7 @@ static void reader(void *ud, const char *block, size_t len)
(int)len, block);
#endif
- if (failed == f_mismatch) return;
+ if (failed == f_mismatch) return -1;
if (failed == f_partial && len == 0) {
if (b->len != 0) {
@@ -66,13 +66,14 @@ static void reader(void *ud, const char *block, size_t len)
} else {
failed = f_complete;
}
- return;
+ return 0;
}
if (len > b->len || memcmp(b->data, block, len) != 0) {
NE_DEBUG(NE_DBG_HTTP, "reader: failed, got [[%.*s]] not [[%.*s]]\n",
(int)len, block, (int)b->len, b->data);
failed = f_mismatch;
+ return -1;
} else {
b->data += len;
b->len -= len;
@@ -81,6 +82,8 @@ static void reader(void *ud, const char *block, size_t len)
(int)b->len);
#endif
}
+
+ return 0;
}
static int file2buf(int fd, ne_buffer *buf)
@@ -100,7 +103,7 @@ static int do_fetch(const char *realfn, const char *gzipfn,
{
ne_session *sess;
ne_request *req;
- int fd;
+ int fd, ret;
ne_buffer *buf = ne_buffer_create();
struct serve_file_args sfargs;
ne_decompress *dc;
@@ -130,19 +133,34 @@ static int do_fetch(const char *realfn, const char *gzipfn,
req = ne_request_create(sess, "GET", "/");
dc = ne_decompress_reader(req, ne_accept_2xx, reader, &body);
- ONREQ(ne_request_dispatch(req));
+#ifdef NE_DEBUGGING
+ ne_debug_init(ne_debug_stream, ne_debug_mask & ~NE_DBG_HTTPBODY);
+#endif
+
+ ret = ne_request_dispatch(req);
+
+#ifdef NE_DEBUGGING
+ ne_debug_init(ne_debug_stream, ne_debug_mask | NE_DBG_HTTPBODY);
+#endif
ONN("file not served", ne_get_status(req)->code != 200);
+
+ ONN("decompress succeeded", expect_fail && !ret);
+ ONV(!expect_fail && ret, ("decompress failed: %s", ne_get_error(sess)));
- ONN("decompress succeeded", expect_fail && !ne_decompress_destroy(dc));
- ONV(!expect_fail && ne_decompress_destroy(dc),
- ("decompress failed: %s", ne_get_error(sess)));
+ NE_DEBUG(NE_DBG_HTTP, "session error: %s\n", ne_get_error(sess));
ne_request_destroy(req);
ne_session_destroy(sess);
ne_buffer_destroy(buf);
-
- CALL(await_server());
+
+ if (expect_fail) {
+ /* if the decompress callback fails, the connection may
+ * be aborted and hence the server will abort. */
+ reap_server();
+ } else {
+ CALL(await_server());
+ }
if (!expect_fail) {
ONN("inflated response truncated", failed == f_partial);
@@ -211,6 +229,11 @@ static int fail_trailing(void)
return do_fetch(newsfn, "trailing.gz", 0, 1);
}
+static int fail_trailing_1b(void)
+{
+ return do_fetch(newsfn, "trailing.gz", 1, 1);
+}
+
static int fail_truncate(void)
{
return do_fetch(newsfn, "truncated.gz", 0, 1);
@@ -231,6 +254,16 @@ static int fail_corrupt2(void)
return do_fetch(newsfn, "corrupt2.gz", 0, 1);
}
+static int fail_empty(void)
+{
+ return do_fetch(newsfn, "empty.gz", 0, 1);
+}
+
+static int notcomp_empty(void)
+{
+ return fetch("empty.gz", NULL, 0);
+}
+
static int auth_cb(void *userdata, const char *realm, int tries,
char *un, char *pw)
{
@@ -256,8 +289,7 @@ static int retry_compress_helper(ne_accept_response acceptor,
ONREQ(ne_request_dispatch(req));
- ONV(ne_decompress_destroy(dc),
- ("decompress failed: %s", ne_get_error(sess)));
+ ne_decompress_destroy(dc);
ONN("got bad response body", failed != f_complete);
@@ -342,17 +374,20 @@ ne_test tests[] = {
T(simple),
T(withname),
T(fail_trailing),
+ T(fail_trailing_1b),
T(fail_bad_csum),
T(fail_truncate),
T(fail_corrupt1),
T(fail_corrupt2),
+ T(fail_empty),
+ T(notcomp_empty),
T(chunked_1b),
T(chunked_1b_wn),
T(chunked_12b),
T(chunked_20b),
T(chunked_10b),
T(chunked_10b_wn),
- T(retry_notcompress),
- T(retry_compress),
+ T_XFAIL(retry_notcompress),
+ T_XFAIL(retry_compress),
T(NULL)
};
diff --git a/neon/test/largefile.c b/neon/test/largefile.c
new file mode 100644
index 000000000..c68b4fdaf
--- /dev/null
+++ b/neon/test/largefile.c
@@ -0,0 +1,190 @@
+/*
+ Tests for LFS support in neon
+ Copyright (C) 2004, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include "ne_request.h"
+
+#include "child.h"
+#include "utils.h"
+#include "tests.h"
+
+#ifndef INT64_C
+#define INT64_C(x) x ## LL
+#endif
+
+static const char data[] = "Hello, world.\n";
+static off64_t point = INT64_C(2) << 32;
+#define SPARSE "sparse.bin"
+
+/* make a sparse large file */
+static int make_sparse_file(void)
+{
+ int fd = open64(SPARSE, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+
+ ONN("could not create large file " SPARSE, fd < 0);
+ ONN("seek to point", lseek64(fd, point, SEEK_SET) != point);
+ ONN("could not write to file",
+ write(fd, data, strlen(data)) != (ssize_t)strlen(data));
+ ONN("close failed", close(fd));
+
+ return OK;
+}
+
+/* server function which checks that the request body sent was the
+ * same as the 'data' array. */
+static int serve_check_body(ne_socket *sock, void *userdata)
+{
+ CALL(discard_request(sock));
+
+ if (clength != (ssize_t)strlen(data)) {
+ CALL(discard_body(sock));
+ SEND_STRING(sock, "HTTP/1.0 400 Bad Request Body Length\r\n"
+ "\r\n");
+ } else {
+ char buf[20];
+
+ if (ne_sock_fullread(sock, buf, clength) == 0) {
+ SEND_STRING(sock, "HTTP/1.0 200 OK Then!\r\n\r\n");
+ }
+ }
+ return 0;
+}
+
+/* sends a small segment of the file from a high offset. */
+static int send_high_offset(void)
+{
+ int ret, fd = open64(SPARSE, O_RDONLY);
+ ne_session *sess;
+ ne_request *req;
+
+ ONN("could not open sparse file", fd < 0);
+
+ CALL(make_session(&sess, serve_check_body, NULL));
+
+ req = ne_request_create(sess, "PUT", "/sparse");
+ ne_set_request_body_fd64(req, fd, point, strlen(data));
+ ret = ne_request_dispatch(req);
+ CALL(await_server());
+ ONV(ret != NE_OK || ne_get_status(req)->klass != 2,
+ ("request failed: %s", ne_get_error(sess)));
+ ne_request_destroy(req);
+ ne_session_destroy(sess);
+ close(fd);
+ return OK;
+}
+
+#if 1
+#define RESPSIZE INT64_C(4295008256)
+#define RESPSTR "4295008256"
+#else
+#define RESPSIZE INT64_C(2147491840) /* 2^31+8192 */
+#define RESPSTR "2147491840"
+#endif
+
+/* Reads a request, sends a large response, reads a request, then
+ * sends a little response. */
+static int serve_large_response(ne_socket *sock, void *ud)
+{
+ int n = 0;
+ char empty[8192];
+
+ CALL(discard_request(sock));
+
+ SEND_STRING(sock,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: " RESPSTR "\r\n"
+ "Server: BigFileServerTM\r\n" "\r\n");
+
+ memset(empty, 0, sizeof empty);
+
+ for (n = 0; n < RESPSIZE/sizeof(empty); n++) {
+ if (ne_sock_fullwrite(sock, empty, sizeof empty)) {
+ NE_DEBUG(NE_DBG_SOCKET, "fullwrite failed\n");
+ return 1;
+ }
+ }
+
+ NE_DEBUG(NE_DBG_SOCKET, "Wrote %d lots of %d\n", n, (int)sizeof empty);
+
+ CALL(discard_request(sock));
+
+ SEND_STRING(sock, "HTTP/1.1 200 OK\r\n"
+ "Connection: close\r\n\r\n");
+
+ return 0;
+}
+
+static int read_large_response(void)
+{
+ ne_session *sess;
+ ne_request *req;
+ off64_t count = 0;
+ int ret;
+ char buf[8192];
+
+ CALL(make_session(&sess, serve_large_response, NULL));
+
+ req = ne_request_create(sess, "GET", "/foo");
+
+#ifdef NE_DEBUGGING
+ ne_debug_init(ne_debug_stream, ne_debug_mask & ~(NE_DBG_HTTPBODY|NE_DBG_HTTP));
+#endif
+
+ ret = ne_begin_request(req);
+ if (ret == NE_OK) {
+ while ((ret = ne_read_response_block(req, buf, sizeof buf)) > 0)
+ count += ret;
+ if (ret == NE_OK)
+ ret = ne_end_request(req);
+ }
+
+#ifdef NE_DEBUGGING
+ ne_debug_init(ne_debug_stream, ne_debug_mask & (NE_DBG_HTTPBODY|NE_DBG_HTTP));
+#endif
+
+ ONV(ret, ("request failed: %s", ne_get_error(sess)));
+ ONV(count != RESPSIZE,
+ ("response body was %" NE_FMT_OFF64_T " not %" NE_FMT_OFF64_T,
+ count, RESPSIZE));
+
+ ne_request_destroy(req);
+
+ CALL(any_2xx_request(sess, "/bar"));
+ CALL(await_server());
+ ne_session_destroy(sess);
+ return OK;
+}
+
+ne_test tests[] = {
+ T(make_sparse_file),
+ T(send_high_offset),
+ T(read_large_response),
+ T(NULL),
+};
diff --git a/neon/test/makekeys.sh b/neon/test/makekeys.sh
index d9548b89f..eed5e6f96 100755
--- a/neon/test/makekeys.sh
+++ b/neon/test/makekeys.sh
@@ -8,7 +8,7 @@ CONF=${srcdir}/openssl.conf
REQ="${OPENSSL} req -config ${CONF}"
CA="${OPENSSL} ca -config ${CONF} -batch"
# MKCERT makes a self-signed cert
-MKCERT="${REQ} -x509 -new -days 9000"
+MKCERT="${REQ} -x509 -new -days 900"
REQDN=reqDN
STRMASK=default
@@ -70,9 +70,12 @@ ${REQ} -new -key ${srcdir}/server.key -out altname3.csr
csr_fields "Fourth AltName Dept" localhost | \
${REQ} -new -key ${srcdir}/server.key -out altname4.csr
-csr_fields "Fifth Altname Dept" localhost | \
+csr_fields "Good ipAddress altname Dept" nowhere.example.com | \
${REQ} -new -key ${srcdir}/server.key -out altname5.csr
+csr_fields "Bad ipAddress altname Dept" nowhere.example.com | \
+${REQ} -new -key ${srcdir}/server.key -out altname6.csr
+
csr_fields "Self-Signed" | \
${MKCERT} -key ${srcdir}/server.key -out ssigned.pem
@@ -117,7 +120,7 @@ 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 9000 -in wildcard.csr -out wildcard.cert
+ ${CA} -days 900 -in wildcard.csr -out wildcard.cert
fi
csr_fields "Neon Client Cert" ignored.example.com | \
@@ -138,13 +141,18 @@ 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
+# presume AVAs will come out in least->most specific order still...
+REQDN=reqDN.twoOU
+csr_fields "Second OU Dept
+First OU Dept" | ${REQ} -new -key ${srcdir}/server.key -out twoou.csr
+
### don't put ${REQ} invocations after here
-for f in server client twocn caseless cnfirst missingcn justmail; do
+for f in server client twocn caseless cnfirst missingcn justmail twoou; do
${CA} -days 900 -in ${f}.csr -out ${f}.cert
done
-for n in 1 2 3 4 5; do
+for n in 1 2 3 4 5 6; do
${CA} -extensions altExt${n} -days 900 \
-in altname${n}.csr -out altname${n}.cert
done
diff --git a/neon/test/openssl.conf b/neon/test/openssl.conf
index 827b44e22..390d34d56 100644
--- a/neon/test/openssl.conf
+++ b/neon/test/openssl.conf
@@ -52,6 +52,10 @@ subjectAltName = email:neon@webdav.org
[altExt5]
subjectAltName = IP:127.0.0.1
+# an AltName with a bad IP address
+[altExt6]
+subjectAltName = IP:1.2.3.4
+
[reqDN]
countryName = Country Name
stateOrProvinceName = State or Province Name
@@ -72,6 +76,16 @@ organizationalUnitName = Organizational Unit Name
1.commonName = Common Name
emailAddress = Email Address
+[reqDN.twoOU]
+countryName = Country Name
+stateOrProvinceName = State or Province Name
+localityName = Locality Name
+organizationName = Organization Name
+0.organizationalUnitName = Organizational Unit Name
+1.organizationalUnitName = Organizational Unit Name
+commonName = Common Name (eg, your name or your server\'s hostname)
+emailAddress = Email Address
+
[reqDN.CNfirst]
commonName = Common Name
diff --git a/neon/test/props.c b/neon/test/props.c
index ef0f1e707..4636a5939 100644
--- a/neon/test/props.c
+++ b/neon/test/props.c
@@ -263,8 +263,8 @@ static int run_207_response(char *resp, const char *expected)
CALL(await_server());
- ONV(!ne_xml_valid(p),
- ("response body was invalid: %s", ne_xml_get_error(p)));
+ ONV(ne_xml_failed(p),
+ ("parse error in response body: %s", ne_xml_get_error(p)));
ONV(strcmp(buf->data, expected),
("comparison failed.\n"
@@ -521,7 +521,14 @@ static int pfind_simple(void)
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}
+ 0, 0},
+
+ /* whitespace handling. */
+ { MULTI_207(RESP_207("\r\nhttp://localhost:7777/alpha ",
+ PSTAT_207(PROPS_207(APROP_207("alpha", "beta"))
+ "<D:status>\r\nHTTP/1.1 200 OK </D:status>"))),
+ "results(http://localhost:7777/alpha,prop:[{DAV:,alpha}='beta':{200 OK}];)//",
+ 0, 0}
};
const ne_propname pset1[] = {
{ "DAV:", "fishbone", },
@@ -540,12 +547,51 @@ static int pfind_simple(void)
return OK;
}
+static int unbounded_response(const char *header, const char *repeats)
+{
+ ne_session *sess;
+ struct infinite i = { header, repeats};
+ int dbg;
+
+ CALL(make_session(&sess, serve_infinite, &i));
+
+ dbg = ne_debug_mask;
+
+ ONN("unbounded PROPFIND response did not fail",
+ ne_simple_propfind(sess, "/", 0, NULL,
+ dummy_results, NULL) != NE_ERROR);
+
+ CALL(reap_server());
+ ne_session_destroy(sess);
+ return OK;
+}
+
+static int unbounded_propstats(void)
+{
+ return unbounded_response(
+ RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<multistatus xmlns=\"DAV:\">"
+ "<response><href>/</href>",
+ "<propstat></propstat>");
+}
+
+static int unbounded_props(void)
+{
+ return unbounded_response(
+ RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<multistatus xmlns=\"DAV:\">"
+ "<response><href>/</href><propstat>",
+ "<prop><jim>hello, world</jim></prop>");
+}
+
ne_test tests[] = {
T(two_oh_seven),
T(patch_simple),
T(pfind_simple),
T(regress),
T(patch_regress),
+ T(unbounded_props),
+ T(unbounded_propstats),
T(NULL)
};
diff --git a/neon/test/request.c b/neon/test/request.c
index 4df5c78d2..1dcf7c2f7 100644
--- a/neon/test/request.c
+++ b/neon/test/request.c
@@ -1,6 +1,6 @@
/*
HTTP request handling tests
- Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -69,10 +69,11 @@ static int finish_request(void)
#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)
+static int collector(void *ud, const char *data, size_t len)
{
ne_buffer *buf = ud;
ne_buffer_append(buf, data, len);
+ return 0;
}
typedef ne_request *(*construct_request)(ne_session *sess, void *userdata);
@@ -251,12 +252,6 @@ static int no_body_204(void)
"Content-Length: 5\r\n\r\n");
}
-static int no_body_205(void)
-{
- return expect_no_body("GET", "HTTP/1.1 205 Reset Content\r\n"
- "Content-Length: 5\r\n\r\n");
-}
-
static int no_body_HEAD(void)
{
return expect_no_body("HEAD", "HTTP/1.1 200 OK\r\n"
@@ -830,17 +825,12 @@ static int skip_1xx_hdrs(void)
#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. */
+/* server for expect_100_once: 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);
@@ -857,24 +847,35 @@ static int expect_100_once(void)
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");
+ ne_set_request_expect100(req, 1);
memset(body, 'A', sizeof(body));
ne_set_request_body_buffer(req, body, sizeof(body));
- ONN("request failed", ne_request_dispatch(req));
+ ONREQ(ne_request_dispatch(req));
ne_request_destroy(req);
ne_session_destroy(sess);
CALL(await_server());
return OK;
}
+/* regression test for enabling 100-continue without sending a body. */
+static int expect_100_nobody(void)
+{
+ ne_session *sess;
+ ne_request *req;
+
+ CALL(make_session(&sess, serve_100_once, NULL));
+
+ req = ne_request_create(sess, "GET", "/foo");
+ ne_set_request_expect100(req, 1);
+ ONREQ(ne_request_dispatch(req));
+ ne_request_destroy(req);
+ ne_session_destroy(sess);
+
+ return await_server();
+}
+
struct body {
char *body;
size_t size;
@@ -971,19 +972,6 @@ static int send_bodies(void)
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. */
@@ -1052,7 +1040,8 @@ static int fail_request(int with_body, server_fn fn, void *ud, int forever)
static int unbounded_headers(void)
{
- return fail_request(0, serve_infinite_headers, NULL, 0);
+ struct infinite i = { RESP200, "x-foo: bar\r\n" };
+ return fail_request(0, serve_infinite, &i, 0);
}
static int blank_response(void)
@@ -1074,18 +1063,11 @@ 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);
+ struct infinite i = { "HTTP/1.0 200 OK\r\nFoo: bar\r\n",
+ " hello there.\r\n" };
+ return fail_request(0, serve_infinite, &i, 0);
}
static int serve_close(ne_socket *sock, void *ud)
@@ -1166,11 +1148,13 @@ static enum {
static off_t prog_last = -1, prog_total;
+#define FOFF "%" NE_FMT_OFF_T
+
/* 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",
+ "progress callback: " FOFF "/" FOFF ".\n",
prog, total);
switch (prog_state) {
@@ -1179,19 +1163,19 @@ static void s_progress(void *userdata, off_t prog, off_t total)
return;
case prog_transfer:
if (total != prog_total) {
- t_context("total unexpected: %ld not %ld", total, prog_total);
+ t_context("total unexpected: " FOFF " not " FOFF "", total, prog_total);
prog_state = prog_error;
}
else if (prog > total) {
- t_context("first progress was invalid (%ld/%ld)", prog, total);
+ t_context("first progress was invalid (" FOFF "/" FOFF ")", 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);
+ t_context("progess went backwards: " FOFF " to " FOFF, prog_last, prog);
prog_state = prog_error;
}
else if (prog_last == prog) {
- t_context("no progress made! %ld to %ld", prog_last, prog);
+ t_context("no progress made! " FOFF " to " FOFF, prog_last, prog);
prog_state = prog_error;
}
else if (prog == total) {
@@ -1203,6 +1187,8 @@ static void s_progress(void *userdata, off_t prog, off_t total)
prog_last = prog;
}
+#undef FOFF
+
static ssize_t provide_progress(void *userdata, char *buf, size_t bufsiz)
{
int *count = userdata;
@@ -1580,13 +1566,105 @@ static int dup_method(void)
return OK;
}
+#ifdef NE_HAVE_IDNA
+
+/* "ħêłłø.com" in UTF-8 and ACE form. */
+#define HELLO_DOT_COM "\xc4\xa7" "\xc3\xaa" "\xc5\x82" "\xc5\x82" "\xc3\xb8" ".com"
+#define HELLO_IDNA_ACE "xn--bda2a5j8da.com"
+
+static char *host_hdr = NULL;
+
+static void dup_header(char *hdr)
+{
+ host_hdr = strdup(hdr);
+}
+
+static int serve_check_host(ne_socket *sock, void *userdata)
+{
+ const char *host = userdata;
+
+ want_header = "Host";
+ got_header = dup_header;
+
+ CALL(discard_request(sock));
+
+ if (host_hdr == NULL) {
+ CALL(SEND_STRING(sock, "HTTP/1.0 500 No Host header!?\r\n\r\n"));
+ } else if (strcmp(host_hdr, host) != 0) {
+ CALL(SEND_STRING(sock, "HTTP/1.0 500 Bad Host Header\r\n\r\n"));
+ } else {
+ CALL(SEND_STRING(sock, "HTTP/1.0 200 Good Host Header\r\n\r\n"));
+ }
+
+ return OK;
+}
+
+static int idna_hostname(void)
+{
+ ne_session *sess = ne_session_create("http", HELLO_DOT_COM, 80);
+
+ ne_session_proxy(sess, "localhost", 7777);
+
+ CALL(spawn_server(7777, serve_check_host, HELLO_IDNA_ACE));
+ CALL(any_2xx_request(sess, "/idnafoo"));
+ CALL(await_server());
+
+ ne_session_destroy(sess);
+ return OK;
+}
+#else
+static int idna_hostname(void)
+{
+ t_context("IDNA support not enabled");
+ return SKIP;
+}
+#endif
+
+static int abortive_reader(void *userdata, const char *buf, size_t len)
+{
+ ne_session *sess = userdata;
+ if (len == 5 && strncmp(buf, "abcde", 5) == 0) {
+ ne_set_error(sess, "Reader callback failed");
+ } else {
+ ne_set_error(sess, "Reader callback called with length %" NE_FMT_SIZE_T,
+ len);
+ }
+ return NE_ERROR;
+}
+
+static int abort_reader(void)
+{
+ ne_session *sess;
+ ne_request *req;
+ int ret;
+
+ CALL(make_session(&sess, single_serve_string,
+ RESP200 "Content-Length: 5\r\n\r\n"
+ "abcde"
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 0\r\n\r\n"));
+
+ req = ne_request_create(sess, "GET", "/foo");
+ ne_add_response_body_reader(req, ne_accept_2xx, abortive_reader, sess);
+ ret = ne_request_dispatch(req);
+ ONV(ret != NE_ERROR, ("request did not fail with NE_ERROR: %d", ret));
+ ONV(strcmp(ne_get_error(sess), "Reader callback failed") != 0,
+ ("unexpected session error string: %s", ne_get_error(sess)));
+ ne_request_destroy(req);
+ /* test that the connection was closed. */
+ ONN("connection not closed after aborted response",
+ any_2xx_request(sess, "/failmeplease") == OK);
+ 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_205),
T(no_body_304),
T(no_body_HEAD),
T(no_body_empty_clength),
@@ -1630,6 +1708,7 @@ ne_test tests[] = {
T(skip_1xx_hdrs),
T(send_bodies),
T(expect_100_once),
+ T(expect_100_nobody),
T(unbounded_headers),
T(unbounded_folding),
T(blank_response),
@@ -1652,5 +1731,7 @@ ne_test tests[] = {
T(dup_method),
T(versions),
T(hook_create_req),
+ T(idna_hostname),
+ T(abort_reader),
T(NULL)
};
diff --git a/neon/test/run.sh b/neon/test/run.sh
index 046457123..f05c321be 100755
--- a/neon/test/run.sh
+++ b/neon/test/run.sh
@@ -1,12 +1,13 @@
#!/bin/sh
-rm -f debug.log
-rm -f child.log
+rm -f debug.log child.log
+
+ulimit -c unlimited
+ulimit -v 10240
# enable an safety-checking malloc in glibc which will abort() if
# heap corruption is detected.
MALLOC_CHECK_=2
-
export MALLOC_CHECK_
for f in $*; do
diff --git a/neon/test/socket.c b/neon/test/socket.c
index 687ff1d03..47a86f884 100644
--- a/neon/test/socket.c
+++ b/neon/test/socket.c
@@ -43,14 +43,8 @@
#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;
+ne_ssl_context *server_ctx, *client_ctx;
#endif
static ne_sock_addr *localhost;
@@ -82,25 +76,26 @@ static int multi_init(void)
return OK;
}
-static ne_socket *do_connect(ne_sock_addr *addr, unsigned int port)
+/* Create and connect *sock to address addr on given port. */
+static int do_connect(ne_socket **sock, ne_sock_addr *addr, unsigned int port)
{
- ne_socket *sock = ne_sock_create();
const ne_inet_addr *ia;
- if (!sock) return NULL;
+ *sock = ne_sock_create();
+ ONN("could not create socket", *sock == NULL);
for (ia = ne_addr_first(addr); ia; ia = ne_addr_next(addr)) {
- if (ne_sock_connect(sock, ia, port) == 0)
- return sock;
+ if (ne_sock_connect(*sock, ia, port) == 0)
+ return OK;
}
- ne_sock_close(sock);
- return NULL;
+ t_context("could not connect to server: %s", ne_sock_error(*sock));
+ ne_sock_close(*sock);
+ return FAIL;
}
#ifdef SOCKET_SSL
-/* FIXME: largely cut'n'pasted from ssl.c. */
static int init_ssl(void)
{
char *server_key;
@@ -110,26 +105,23 @@ static int init_ssl(void)
if (test_argc > 1) {
server_key = ne_concat(test_argv[1], "/server.key", NULL);
} else {
- server_key = "server.key";
+ server_key = ne_strdup("server.key");
}
- ONN("sock_init failed.\n", ne_sock_init());
- server_ctx = SSL_CTX_new(SSLv23_server_method());
+ ONN("sock_init failed", ne_sock_init());
+ server_ctx = ne_ssl_context_create(1);
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();
+
+ ne_ssl_context_keypair(server_ctx, "server.cert", server_key);
+
+ client_ctx = ne_ssl_context_create(0);
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);
+ ne_ssl_context_trustcert(client_ctx, cert);
+ ne_free(server_key);
return OK;
}
@@ -160,24 +152,13 @@ struct serve_pair {
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);
+
+ if (ne_sock_accept_ssl(sock, server_ctx)) {
+ NE_DEBUG(NE_DBG_SOCKET, "SSL_accept failed: %s\n", ne_sock_error(sock));
return 1;
}
NE_DEBUG(NE_DBG_SOCKET, "SSL accept okay.\n");
- ne_sock_switch_ssl(sock, ssl);
return pair->fn(sock, pair->userdata);
}
@@ -187,8 +168,7 @@ static int begin(ne_socket **sock, server_fn fn, void *ud)
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);
+ CALL(do_connect(sock, localhost, 7777));
ONV(ne_sock_connect_ssl(*sock, client_ctx),
("SSL negotation failed: %s", ne_sock_error(*sock)));
return OK;
@@ -199,9 +179,7 @@ static int begin(ne_socket **sock, server_fn fn, void *ud)
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;
+ return do_connect(sock, localhost, 7777);
}
#endif
@@ -568,7 +546,8 @@ static int line_closure(void)
ret = ne_sock_readline(sock, buffer, BUFSIZ);
ONV(ret != NE_SOCK_CLOSED,
- ("readline got %" NE_FMT_SSIZE_T " not EOF", ret));
+ ("readline got %" NE_FMT_SSIZE_T " not EOF: %s", ret,
+ ne_sock_error(sock)));
return finish(sock, 0);
}
@@ -792,7 +771,8 @@ static int ssl_closure(void)
ret = ne_sock_fullwrite(sock, "a", 1);
} while (ret == 0);
ONV(ret != NE_SOCK_RESET && ret != NE_SOCK_CLOSED,
- ("write got %" NE_FMT_SSIZE_T " not reset or closure", ret));
+ ("write got %" NE_FMT_SSIZE_T " not reset or closure: %s", ret,
+ ne_sock_error(sock)));
return good_close(sock);
}
@@ -816,13 +796,20 @@ static int ssl_truncate(void)
}
#else
-/* thanks to W Richard Stevens for the precise repro case for getting
- * an RST on demand. */
+
+/* use W Richard Stevens' SO_LINGER trick to elicit a TCP RST */
+static int serve_reset(ne_socket *sock, void *ud)
+{
+ reset_socket(sock);
+ exit(0);
+ return 0;
+}
+
static int write_reset(void)
{
ne_socket *sock;
int ret;
- CALL(begin(&sock, serve_close, NULL));
+ CALL(begin(&sock, serve_reset, NULL));
CALL(full_write(sock, "a", 1));
CALL(await_server());
ret = ne_sock_fullwrite(sock, "a", 1);
@@ -830,7 +817,8 @@ static int write_reset(void)
ne_sock_close(sock);
return SKIP;
}
- ONV(ret != NE_SOCK_RESET, ("write got %d not reset", ret));
+ ONV(ret != NE_SOCK_RESET,
+ ("write got %d not reset: %s", ret, ne_sock_error(sock)));
return good_close(sock);
}
@@ -838,7 +826,7 @@ static int read_reset(void)
{
ne_socket *sock;
ssize_t ret;
- CALL(begin(&sock, serve_close, NULL));
+ CALL(begin(&sock, serve_reset, NULL));
CALL(full_write(sock, "a", 1));
CALL(await_server());
ret = ne_sock_read(sock, buffer, 1);
@@ -846,7 +834,9 @@ static int read_reset(void)
ne_sock_close(sock);
return SKIP;
}
- ONV(ret != NE_SOCK_RESET, ("read got %" NE_FMT_SSIZE_T " not reset", ret));
+ ONV(ret != NE_SOCK_RESET,
+ ("read got %" NE_FMT_SSIZE_T " not reset: %s", ret,
+ ne_sock_error(sock)));
return good_close(sock);
}
#endif
diff --git a/neon/test/ssl.c b/neon/test/ssl.c
index dba1d4fda..0f8683c8d 100644
--- a/neon/test/ssl.c
+++ b/neon/test/ssl.c
@@ -39,7 +39,7 @@
#include "child.h"
#include "utils.h"
-#ifndef NEON_SSL
+#ifndef NE_HAVE_SSL
/* this file shouldn't be built if SSL is not enabled. */
#error SSL not supported
#endif
@@ -725,10 +725,10 @@ static int get_failures(void *userdata, int fs, const ne_ssl_certificate *c)
/* 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,
+static int fail_ssl_request(char *cert, char *cacert, const char *host,
const char *msg, int failures)
{
- ne_session *sess = DEFSESS;
+ ne_session *sess = ne_session_create("https", host, 7777);
int gotf = 0, ret;
ret = any_ssl_request(sess, fail_serve, cert, cacert,
@@ -753,14 +753,14 @@ static int fail_ssl_request(char *cert, char *cacert,
return OK;
}
-/* Note that the certs used for fail_* are all self-signed, so the
+/* Note that the certs used for fail_* are mostly 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",
+ return fail_ssl_request("wrongcn.pem", "wrongcn.pem", "localhost",
"certificate with incorrect CN was accepted",
NE_SSL_IDMISMATCH);
}
@@ -769,8 +769,8 @@ static int fail_wrongCN(void)
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));
+ CALL(fail_ssl_request(c, c, "localhost",
+ "expired certificate was accepted", NE_SSL_EXPIRED));
ne_free(c);
return OK;
}
@@ -778,7 +778,8 @@ static int fail_expired(void)
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",
+ CALL(fail_ssl_request(c, c, "localhost",
+ "not yet valid certificate was accepted",
NE_SSL_NOTYETVALID));
ne_free(c);
return OK;
@@ -788,14 +789,14 @@ static int fail_notvalid(void)
* fail with UNTRUSTED. */
static int fail_untrusted_ca(void)
{
- return fail_ssl_request("server.cert", NULL, "untrusted CA.",
- NE_SSL_UNTRUSTED);
+ return fail_ssl_request("server.cert", NULL, "localhost",
+ "untrusted CA.", NE_SSL_UNTRUSTED);
}
static int fail_self_signed(void)
{
- return fail_ssl_request("ssigned.pem", NULL, "self-signed cert",
- NE_SSL_UNTRUSTED);
+ return fail_ssl_request("ssigned.pem", NULL, "localhost",
+ "self-signed cert", NE_SSL_UNTRUSTED);
}
/* Test for failure when a server cert is presented which has no
@@ -815,6 +816,21 @@ static int fail_missing_CN(void)
return OK;
}
+/* test for a bad ipAddress altname */
+static int fail_bad_ipaltname(void)
+{
+ return fail_ssl_request("altname6.cert", CA_CERT, "127.0.0.1",
+ "bad IP altname cert", NE_SSL_IDMISMATCH);
+}
+
+/* test for a ipAddress which matched against the hostname as per neon
+ * 0.24 behaviour. */
+static int fail_host_ipaltname(void)
+{
+ return fail_ssl_request("altname5.cert", CA_CERT, "localhost",
+ "bad IP altname cert", NE_SSL_IDMISMATCH);
+}
+
/* Test that the SSL session is cached across connections. */
static int session_cache(void)
{
@@ -957,6 +973,7 @@ static int ccert_unencrypted(void)
args.require_cc = 1;
ccert = ne_ssl_clicert_read("unclient.p12");
+ ONN("could not load unclient.p12", ccert == NULL);
ONN("unclient.p12 was encrypted", ne_ssl_clicert_encrypted(ccert));
ne_ssl_set_clicert(sess, ccert);
@@ -1171,6 +1188,7 @@ static int cert_identities(void)
static const struct {
const char *fname, *identity;
} certs[] = {
+ { "ssigned.pem", "localhost" },
{ "twocn.cert", "localhost" },
{ "altname1.cert", "localhost" },
{ "altname2.cert", "nohost.example.com" },
@@ -1270,7 +1288,9 @@ static int dname_readable(void)
{ "justmail.cert", "blah@example.com", NULL },
{ "t61subj.cert", I18N_DNAME, NULL },
{ "bmpsubj.cert", I18N_DNAME, NULL },
- { "utf8subj.cert", I18N_DNAME, NULL }
+ { "utf8subj.cert", I18N_DNAME, NULL },
+ { "twoou.cert", "First OU Dept, Second OU Dept, Neon Hackers Ltd, "
+ "Cambridge, Cambridgeshire, GB" }
};
size_t n;
@@ -1538,6 +1558,8 @@ ne_test tests[] = {
T(fail_untrusted_ca),
T(fail_self_signed),
T(fail_missing_CN),
+ T(fail_host_ipaltname),
+ T(fail_bad_ipaltname),
T(session_cache),
diff --git a/neon/test/string-tests.c b/neon/test/string-tests.c
index 3d0fac183..62e8afe00 100644
--- a/neon/test/string-tests.c
+++ b/neon/test/string-tests.c
@@ -1,6 +1,6 @@
/*
String handling tests
- Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -410,7 +410,7 @@ static int base64(void)
unsigned char bits[256];
size_t n;
-#define B64B(x, l, y) CALL(b64_check(x, l, y))
+#define B64B(x, l, y) CALL(b64_check((unsigned char *)x, l, y))
#define B64(x, y) B64B(x, strlen(x), y)
/* invent these with
@@ -464,6 +464,39 @@ static int unbase64(void)
return OK;
}
+static int printing(void)
+{
+ struct {
+ const char *in, *out;
+ size_t pass, ret;
+ } ts[] = {
+ { "alpha", "alpha", 10, 5 },
+ { "alpha", "alph", 5, 4 },
+ { "foobar", "", 1, 0 },
+ { NULL, NULL, 0, 0}
+ };
+ size_t n;
+
+ for (n = 0; ts[n].in; n++) {
+ char buf[512];
+ size_t ret;
+
+ memset(buf, 'A', sizeof buf);
+
+ ret = ne_snprintf(buf, ts[n].pass, "%s", ts[n].in);
+
+ ONCMP(buf, ts[n].out);
+ ONV(ret != ts[n].ret,
+ ("got return value %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T,
+ ret, ts[n].ret));
+
+ /* byte past the NUL must still be 'A' */
+ ONN("buffer over-ran!", buf[ret + 1] != 'A');
+ }
+
+ return OK;
+}
+
ne_test tests[] = {
T(simple),
T(buf_concat),
@@ -487,6 +520,7 @@ ne_test tests[] = {
T(cleaner),
T(base64),
T(unbase64),
+ T(printing),
T(NULL)
};
diff --git a/neon/test/stubs.c b/neon/test/stubs.c
index 494f8f08e..1c599623b 100644
--- a/neon/test/stubs.c
+++ b/neon/test/stubs.c
@@ -42,11 +42,11 @@
#include "child.h"
#include "utils.h"
-#if defined(NEON_ZLIB) && defined(NEON_SSL)
+#if defined(NE_HAVE_ZLIB) && defined(NE_HAVE_SSL)
#define NO_TESTS 1
#endif
-#ifndef NEON_ZLIB
+#ifndef NE_HAVE_ZLIB
static int sd_result = OK;
static void sd_reader(void *ud, const char *block, size_t len)
@@ -90,7 +90,7 @@ static int stub_decompress(void)
}
#endif
-#ifndef NEON_SSL
+#ifndef NE_HAVE_SSL
static int stub_ssl(void)
{
ne_session *sess = ne_session_create("https", "localhost", 7777);
@@ -156,10 +156,10 @@ static int null_test(void) { return OK; }
#endif
ne_test tests[] = {
-#ifndef NEON_ZLIB
+#ifndef NE_HAVE_ZLIB
T(stub_decompress),
#endif
-#ifndef NEON_SSL
+#ifndef NE_HAVE_SSL
T(stub_ssl),
#endif
/* to prevent failure when SSL and zlib are supported. */
diff --git a/neon/test/util-tests.c b/neon/test/util-tests.c
index 464f33877..0cee94b23 100644
--- a/neon/test/util-tests.c
+++ b/neon/test/util-tests.c
@@ -254,10 +254,30 @@ static int version_string(void)
static int support(void)
{
-#ifdef NEON_SSL
- ONN("SSL support not advertised", !ne_supports_ssl());
+#ifdef NE_HAVE_SSL
+ ONN("SSL support not advertised", !ne_has_support(NE_FEATURE_SSL));
#else
- ONN("SSL support advertised", ne_supports_ssl());
+ ONN("SSL support advertised", ne_has_support(NE_FEATURE_SSL));
+#endif
+#ifdef NE_HAVE_ZLIB
+ ONN("zlib support not advertised", !ne_has_support(NE_FEATURE_ZLIB));
+#else
+ ONN("zlib support advertised", ne_has_support(NE_FEATURE_ZLIB));
+#endif
+#ifdef NE_HAVE_IPV6
+ ONN("IPv6 support not advertised", !ne_has_support(NE_FEATURE_IPV6));
+#else
+ ONN("IPv6 support advertised", ne_has_support(NE_FEATURE_IPV6));
+#endif
+#ifdef NE_HAVE_IDNA
+ ONN("IDNA support not advertised", !ne_has_support(NE_FEATURE_IDNA));
+#else
+ ONN("IDNA support advertised", ne_has_support(NE_FEATURE_IDNA));
+#endif
+#ifdef NE_HAVE_LFS
+ ONN("LFS support not advertised", !ne_has_support(NE_FEATURE_LFS));
+#else
+ ONN("LFS support advertised", ne_has_support(NE_FEATURE_LFS));
#endif
return OK;
}
diff --git a/neon/test/utils.c b/neon/test/utils.c
index c23860abb..0a070b3a0 100644
--- a/neon/test/utils.c
+++ b/neon/test/utils.c
@@ -85,7 +85,7 @@ 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)));
+ ("request failed: %s", ne_get_error(sess)));
ne_request_destroy(req);
return ret;
}
@@ -100,7 +100,7 @@ int any_2xx_request_body(ne_session *sess, const char *uri)
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)));
+ ("request failed: %s", ne_get_error(sess)));
ne_request_destroy(req);
return ret;
}
@@ -132,3 +132,17 @@ int serve_sstring_slowly(ne_socket *sock, void *ud)
return 0;
}
+
+int serve_infinite(ne_socket *sock, void *ud)
+{
+ struct infinite *i = ud;
+
+ CALL(discard_request(sock));
+
+ SEND_STRING(sock, i->header);
+
+ while (server_send(sock, i->repeat, strlen(i->repeat)) == 0)
+ /* nullop */;
+
+ return OK;
+}
diff --git a/neon/test/utils.h b/neon/test/utils.h
index 2b466b6d9..ad31b2ef5 100644
--- a/neon/test/utils.h
+++ b/neon/test/utils.h
@@ -65,4 +65,12 @@ int serve_sstring(ne_socket *sock, void *ud);
/* Serve a struct string slowly. */
int serve_sstring_slowly(ne_socket *sock, void *ud);
+struct infinite {
+ const char *header, *repeat;
+};
+
+/* Pass a "struct infinite *" as userdata, this function sends
+ * ->header and then loops sending ->repeat forever. */
+int serve_infinite(ne_socket *sock, void *ud);
+
#endif /* UTILS_H */
diff --git a/neon/test/xml.c b/neon/test/xml.c
index 9cea6c753..592ba8043 100644
--- a/neon/test/xml.c
+++ b/neon/test/xml.c
@@ -1,6 +1,6 @@
/*
neon test suite
- Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,6 +35,8 @@
#include "child.h"
#include "utils.h"
+#define ABORT (-42) /* magic code for abort handlers */
+
/* A set of SAX handlers which serialize SAX events back into a
* pseudo-XML-like string. */
static int startelm(void *userdata, int state,
@@ -60,7 +62,7 @@ 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);
+ return strncmp(cdata, "!ABORT!", len) == 0 ? ABORT : NE_XML_DECLINE;
}
static int endelm(void *userdata, int state,
@@ -139,7 +141,7 @@ static int startelm_abort(void *buf, int parent,
{
if (strcmp(name, "abort-start") == 0) {
ne_buffer_zappend(buf, "ABORT");
- return NE_XML_ABORT;
+ return ABORT;
} else
return startelm(buf, parent, nspace, name, atts);
}
@@ -149,22 +151,25 @@ static int endelm_abort(void *buf, int state,
{
if (strcmp(name, "abort-end") == 0) {
ne_buffer_zappend(buf, "ABORT");
- return -1;
+ return ABORT;
} else
return 0;
}
+/* Test mode for parse_match: */
enum match_type {
- match_valid = 0,
- match_invalid,
- match_nohands,
- match_encoding
+ match_valid = 0, /* test that the parse succeeds */
+ match_invalid, /* test that the parse fails */
+ match_nohands, /* test with no handlers registered */
+ match_encoding, /* test whether the encoding is equal to the result string */
+ match_chunked /* parse the document one byte at a time */
};
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();
+ int ret;
if (t == match_invalid)
ne_xml_push_handler(p, startelm_abort, chardata, endelm_abort, buf);
@@ -174,13 +179,26 @@ static int parse_match(const char *doc, const char *result, enum match_type t)
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_chunked) {
+ do {
+ ret = ne_xml_parse(p, doc++, 1);
+ } while (ret == 0 && *doc);
+ } else {
+ ret = ne_xml_parse(p, doc, strlen(doc));
+ }
+
+ if (ret == 0) {
+ ne_xml_parse(p, "", 0);
+ }
+
+ ONV(ret != ne_xml_failed(p),
+ ("ne_xml_failed gave %d not %d", ne_xml_failed(p), ret));
+
if (t == match_invalid)
- ONV(ne_xml_valid(p), ("parse did not fail: %s", buf->data));
+ ONV(ret != ABORT,
+ ("parse got %d not abort failure: %s", ret, buf->data));
else
- ONV(!ne_xml_valid(p), ("parse failed: %s", ne_xml_get_error(p)));
+ ONV(ret, ("parse failed: %s", ne_xml_get_error(p)));
if (t == match_encoding) {
const char *enc = ne_xml_doc_encoding(p);
@@ -215,6 +233,11 @@ static int matches(void)
{ PFX "<hello>\r\n<wide> world</wide></hello>",
"<{}hello>\n<{}wide> world</{}wide></{}hello>"},
+ /* UTF-8 XML Byte Order Mark */
+ { "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>" },
+ /* UTF-8 XML Byte Order Mark */
+ { "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>", match_chunked },
+
/*** Tests for namespace handling. ***/
#define NSA "xmlns:foo='bar'"
{ PFX "<foo:widget " NSA "/>",
@@ -329,7 +352,27 @@ static int fail_parse(void)
/* malformed namespace declarations */
PFX "<foo xmlns:D=''/>",
PFX "<foo xmlns:='fish'/>",
+ PFX "<foo xmlns:.bar='fish'/>",
+ PFX "<foo xmlns:-bar='fish'/>",
+ PFX "<foo xmlns:0bar='fish'/>",
+ PFX "<fee xmlns:8baz='bar'/>",
+
+ /* element names which are not valid QNames. */
PFX "<foo: xmlns:foo='bar'/>",
+ PFX "<:fee/>",
+ PFX "<0fish/>",
+ PFX "<foo:0fish xmlns:foo='bar'/>",
+ PFX "<foo:9fish xmlns:foo='bar'/>",
+ PFX "<foo:-fish xmlns:foo='bar'/>",
+ PFX "<foo:.fish xmlns:foo='bar'/>",
+
+#if 0 /* currently disabled to allow SVN to work */
+ PFX "<foo:bar:baz xmlns:foo='bar'/>",
+ PFX "<fee xmlns:baz:bar='bar'/>",
+ PFX "<fee xmlns::bar='bar'/>",
+ PFX "<foo::fish xmlns:foo='bar'/>",
+#endif
+
#if 0
/* 2-byte encoding of '.': */
PFX "<foo>" "\x2F\xC0\xAE\x2E\x2F" "</foo>",
@@ -341,9 +384,14 @@ static int fail_parse(void)
PFX "<foo>" "\x2F\xF8\x80\x80\x80\xAE\x2E\x2F" "</foo>",
/* 6-byte encoding of '.': */
PFX "<foo>" "\x2F\xFC\x80\x80\x80\x80\xAE\x2E\x2F" "</foo>",
+#endif
/* two-byte encoding of '<' must not be parsed as a '<': */
PFX "\xC0\xBC" "foo></foo>",
-#endif
+
+ /* Invalid UTF-8 XML Byte Order Marks */
+ "\xEF\xBB" PFX "<hello/>",
+ "\xEF" PFX "<hello/>",
+
NULL
};
int n;
@@ -354,10 +402,13 @@ static int fail_parse(void)
ne_xml_parse(p, docs[n], strlen(docs[n]));
ne_xml_parse(p, "", 0);
- ONV(ne_xml_valid(p), ("`%s' was valid", docs[n]));
+ ONV(ne_xml_failed(p) <= 0,
+ ("`%s' did not get positive parse error", docs[n]));
err = ne_xml_get_error(p);
- ONV(strstr(err, "parse error") == NULL,
+ NE_DEBUG(NE_DBG_HTTP, "Parse error for '%s': %s\n", docs[n], err);
+ ONV(strstr(err, "parse error") == NULL
+ && strstr(err, "Invalid Byte Order Mark") == NULL,
("bad error %s", err));
ne_xml_destroy(p);
@@ -426,7 +477,7 @@ static int attributes(void)
ne_xml_parse_v(p, doc, strlen(doc));
- ONV(!ne_xml_valid(p), ("parse error: %s", ne_xml_get_error(p)));
+ ONV(ne_xml_failed(p), ("parse error: %s", ne_xml_get_error(p)));
ne_xml_destroy(p);
return OK;