summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--neon/.cvsignore9
-rw-r--r--neon/.package1
-rwxr-xr-xneon/.release.sh14
-rw-r--r--neon/AUTHORS1
-rw-r--r--neon/BUGS7
-rw-r--r--neon/ChangeLog24
-rw-r--r--neon/INSTALL.win3214
-rw-r--r--neon/Makefile.in80
-rw-r--r--neon/NEWS2
-rw-r--r--neon/README25
-rw-r--r--neon/THANKS10
-rw-r--r--neon/TODO104
-rw-r--r--neon/autogen.sh6
-rwxr-xr-xneon/config.guess1034
-rw-r--r--neon/config.hw38
-rw-r--r--neon/config.hw.in53
-rwxr-xr-xneon/config.sub993
-rw-r--r--neon/configure.in42
-rw-r--r--neon/doc/.cvsignore8
-rw-r--r--neon/doc/TODO156
-rw-r--r--neon/doc/html.xsl38
-rw-r--r--neon/doc/man.xsl14
-rw-r--r--neon/doc/manual.css29
-rw-r--r--neon/doc/manual.xml519
-rw-r--r--neon/doc/parsing-xml.txt170
-rw-r--r--neon/doc/ref/alloc.xml88
-rw-r--r--neon/doc/ref/auth.xml114
-rw-r--r--neon/doc/ref/buf.xml53
-rw-r--r--neon/doc/ref/bufapp.xml89
-rw-r--r--neon/doc/ref/bufcr.xml60
-rw-r--r--neon/doc/ref/bufdest.xml81
-rw-r--r--neon/doc/ref/bufutil.xml62
-rw-r--r--neon/doc/ref/err.xml66
-rw-r--r--neon/doc/ref/getst.xml60
-rw-r--r--neon/doc/ref/init.xml55
-rw-r--r--neon/doc/ref/neon.xml75
-rw-r--r--neon/doc/ref/opts.xml126
-rw-r--r--neon/doc/ref/req.xml170
-rw-r--r--neon/doc/ref/reqbody.xml69
-rw-r--r--neon/doc/ref/reqhdr.xml63
-rw-r--r--neon/doc/ref/resolve.xml160
-rw-r--r--neon/doc/ref/sess.xml111
-rw-r--r--neon/doc/ref/shave.xml51
-rw-r--r--neon/doc/ref/sslca.xml81
-rw-r--r--neon/doc/ref/sslcert.xml66
-rw-r--r--neon/doc/ref/ssldname.xml53
-rw-r--r--neon/doc/ref/sslvfy.xml140
-rw-r--r--neon/doc/ref/status.xml75
-rw-r--r--neon/doc/ref/tok.xml76
-rw-r--r--neon/doc/ref/vers.xml61
-rw-r--r--neon/doc/refentry.xml59
-rw-r--r--neon/doc/using-neon.txt132
-rw-r--r--neon/doc/xref-man.xsl46
-rw-r--r--neon/example/.cvsignore2
-rw-r--r--neon/example/nbrowse.glade284
-rw-r--r--neon/example/nbrowse/callbacks.c40
-rw-r--r--neon/example/nbrowse/callbacks.h14
-rw-r--r--neon/example/nbrowse/interface.c240
-rw-r--r--neon/example/nbrowse/interface.h8
-rw-r--r--neon/example/nbrowse/main.c337
-rw-r--r--neon/example/nbrowse/support.c146
-rw-r--r--neon/example/nbrowse/support.h34
-rw-r--r--neon/example/nget.c164
-rw-r--r--neon/example/nserver.c147
-rwxr-xr-xneon/install-sh250
-rw-r--r--neon/lib/basename.c57
-rw-r--r--neon/lib/basename.h2
-rw-r--r--neon/lib/dirname.c75
-rw-r--r--neon/lib/dirname.h31
-rw-r--r--neon/lib/fnmatch.c212
-rw-r--r--neon/lib/fnmatch.h69
-rw-r--r--neon/lib/getopt.c1052
-rw-r--r--neon/lib/getopt.h133
-rw-r--r--neon/lib/getopt1.c190
-rw-r--r--neon/lib/linelen.c57
-rw-r--r--neon/lib/netrc.c423
-rw-r--r--neon/lib/netrc.h65
-rw-r--r--neon/lib/rpmatch.c88
-rw-r--r--neon/lib/snprintf.c835
-rw-r--r--neon/lib/snprintf.h245
-rw-r--r--neon/lib/strcasecmp.c49
-rw-r--r--neon/lib/yesno.c52
-rwxr-xr-xneon/ltconfig2908
-rw-r--r--neon/ltmain.sh3892
-rw-r--r--neon/macros/ChangeLog26
-rw-r--r--neon/macros/ac_c_bigendian_cross.m481
-rw-r--r--neon/macros/acconfig.h4
-rw-r--r--neon/macros/aclocal-include.m416
-rw-r--r--neon/macros/compiler-flags.m4109
-rw-r--r--neon/macros/gnome-common.m414
-rw-r--r--neon/macros/gnome-gnorba-check.m435
-rw-r--r--neon/macros/gnome-orbit-check.m433
-rw-r--r--neon/macros/gnome-print-check.m4171
-rw-r--r--neon/macros/gnome-pthread-check.m416
-rw-r--r--neon/macros/gnome-support.m468
-rw-r--r--neon/macros/gnome-x-checks.m480
-rw-r--r--neon/macros/gnome.m4124
-rw-r--r--neon/macros/neon-checks.m421
-rw-r--r--neon/macros/neon-debug.m441
-rw-r--r--neon/macros/neon-socks.m445
-rw-r--r--neon/macros/neon-ssl.m468
-rw-r--r--neon/macros/neon-test.m424
-rw-r--r--neon/macros/neon-warnings.m438
-rw-r--r--neon/macros/neon-xml-parser.m484
-rw-r--r--neon/macros/neon.m475
-rw-r--r--neon/macros/readline.m447
-rw-r--r--neon/macros/socklen-arg-type.m443
-rw-r--r--neon/macros/strftime.m4146
-rw-r--r--neon/neon-config.in73
-rw-r--r--neon/neon.dsp215
-rw-r--r--neon/neon.dsw30
-rw-r--r--neon/neon.mak404
-rw-r--r--neon/src/.cvsignore2
-rw-r--r--neon/src/COPYING.LIB482
-rw-r--r--neon/src/ChangeLog351
-rw-r--r--neon/src/Makefile.in109
-rw-r--r--neon/src/Makefile.incl45
-rw-r--r--neon/src/README15
-rw-r--r--neon/src/TODO98
-rw-r--r--neon/src/base64.c103
-rw-r--r--neon/src/base64.h32
-rw-r--r--neon/src/dates.c163
-rw-r--r--neon/src/dates.h42
-rw-r--r--neon/src/dav_207.c262
-rw-r--r--neon/src/dav_207.h91
-rw-r--r--neon/src/dav_basic.c232
-rw-r--r--neon/src/dav_basic.h64
-rw-r--r--neon/src/dav_locks.c622
-rw-r--r--neon/src/dav_locks.h86
-rw-r--r--neon/src/dav_props.c192
-rw-r--r--neon/src/dav_props.h89
-rw-r--r--neon/src/hip_xml.c605
-rw-r--r--neon/src/hip_xml.h254
-rw-r--r--neon/src/http_auth.c887
-rw-r--r--neon/src/http_auth.h206
-rw-r--r--neon/src/http_basic.c357
-rw-r--r--neon/src/http_basic.h103
-rw-r--r--neon/src/http_cookies.c143
-rw-r--r--neon/src/http_cookies.h43
-rw-r--r--neon/src/http_private.h176
-rw-r--r--neon/src/http_redirect.c147
-rw-r--r--neon/src/http_redirect.h60
-rw-r--r--neon/src/http_request.c1306
-rw-r--r--neon/src/http_request.h261
-rw-r--r--neon/src/http_utils.c116
-rw-r--r--neon/src/http_utils.h105
-rw-r--r--neon/src/md5.c447
-rw-r--r--neon/src/md5.h157
-rw-r--r--neon/src/ne_207.c378
-rw-r--r--neon/src/ne_207.h100
-rw-r--r--neon/src/ne_acl.c129
-rw-r--r--neon/src/ne_acl.h56
-rw-r--r--neon/src/ne_alloc.c61
-rw-r--r--neon/src/ne_alloc.h34
-rw-r--r--neon/src/ne_auth.c1095
-rw-r--r--neon/src/ne_auth.h53
-rw-r--r--neon/src/ne_basic.c563
-rw-r--r--neon/src/ne_basic.h136
-rw-r--r--neon/src/ne_compress.c332
-rw-r--r--neon/src/ne_compress.h44
-rw-r--r--neon/src/ne_cookies.c142
-rw-r--r--neon/src/ne_cookies.h50
-rw-r--r--neon/src/ne_dates.c191
-rw-r--r--neon/src/ne_dates.h49
-rw-r--r--neon/src/ne_defs.h10
-rw-r--r--neon/src/ne_i18n.c32
-rw-r--r--neon/src/ne_i18n.h37
-rw-r--r--neon/src/ne_locks.c618
-rw-r--r--neon/src/ne_locks.h126
-rw-r--r--neon/src/ne_md5.c460
-rw-r--r--neon/src/ne_md5.h151
-rw-r--r--neon/src/ne_private.h204
-rw-r--r--neon/src/ne_props.c589
-rw-r--r--neon/src/ne_props.h224
-rw-r--r--neon/src/ne_redirect.c194
-rw-r--r--neon/src/ne_redirect.h74
-rw-r--r--neon/src/ne_request.c1340
-rw-r--r--neon/src/ne_request.h271
-rw-r--r--neon/src/ne_session.c264
-rw-r--r--neon/src/ne_session.h194
-rw-r--r--neon/src/ne_socket.c924
-rw-r--r--neon/src/ne_socket.h228
-rw-r--r--neon/src/ne_string.c469
-rw-r--r--neon/src/ne_string.h212
-rw-r--r--neon/src/ne_uri.c313
-rw-r--r--neon/src/ne_uri.h75
-rw-r--r--neon/src/ne_utils.c160
-rw-r--r--neon/src/ne_utils.h133
-rw-r--r--neon/src/ne_xml.c862
-rw-r--r--neon/src/ne_xml.h184
-rw-r--r--neon/src/neon.h3
-rw-r--r--neon/src/neon_config.h8
-rw-r--r--neon/src/neon_defs.h10
-rw-r--r--neon/src/neon_i18n.c30
-rw-r--r--neon/src/neon_i18n.h11
-rw-r--r--neon/src/neon_md5.h157
-rw-r--r--neon/src/nsocket.h192
-rw-r--r--neon/src/socket.c592
-rw-r--r--neon/src/socket.h137
-rw-r--r--neon/src/sslcerts.c476
-rw-r--r--neon/src/string_utils.c447
-rw-r--r--neon/src/string_utils.h198
-rw-r--r--neon/src/uri.c303
-rw-r--r--neon/src/uri.h63
-rw-r--r--neon/src/xalloc.c56
-rw-r--r--neon/src/xalloc.h33
-rw-r--r--neon/test/.cvsignore3
-rw-r--r--neon/test/COPYING339
-rw-r--r--neon/test/ChangeLog51
-rw-r--r--neon/test/Makefile.in55
-rw-r--r--neon/test/README14
-rw-r--r--neon/test/STATUS79
-rw-r--r--neon/test/acl.c101
-rw-r--r--neon/test/auth.c258
-rw-r--r--neon/test/basic.c101
-rw-r--r--neon/test/child.c160
-rw-r--r--neon/test/child.h47
-rw-r--r--neon/test/common/ChangeLog31
-rw-r--r--neon/test/common/README3
-rw-r--r--neon/test/common/child.c202
-rw-r--r--neon/test/common/child.h47
-rwxr-xr-xneon/test/common/run.sh19
-rw-r--r--neon/test/common/tests.c152
-rw-r--r--neon/test/common/tests.h60
-rw-r--r--neon/test/compress.c183
-rw-r--r--neon/test/cookies.c140
-rw-r--r--neon/test/expired.pem20
-rw-r--r--neon/test/htdocs/plain1
-rw-r--r--neon/test/http-tests.c195
-rw-r--r--neon/test/lock.c101
-rwxr-xr-xneon/test/makekeys.sh92
-rw-r--r--neon/test/notvalid.pem20
-rw-r--r--neon/test/openssl.conf21
-rw-r--r--neon/test/props.c64
-rw-r--r--neon/test/redirect.c135
-rw-r--r--neon/test/regress.c87
-rw-r--r--neon/test/request.c423
-rw-r--r--neon/test/resolve.c57
-rwxr-xr-xneon/test/run.sh12
-rw-r--r--neon/test/server.c195
-rw-r--r--neon/test/server.key9
-rw-r--r--neon/test/session.c115
-rw-r--r--neon/test/skeleton.c51
-rw-r--r--neon/test/sock-tests.c70
-rw-r--r--neon/test/socket.c257
-rw-r--r--neon/test/ssl.c131
-rw-r--r--neon/test/string-tests.c89
-rw-r--r--neon/test/stubs.c126
-rw-r--r--neon/test/tests.c103
-rw-r--r--neon/test/tests.h49
-rw-r--r--neon/test/uri-tests.c188
-rw-r--r--neon/test/util-tests.c139
-rw-r--r--neon/test/utils.c40
-rw-r--r--neon/test/utils.h29
-rw-r--r--neon/test/wrongcn.pem20
-rw-r--r--neon/test/xml.c137
-rw-r--r--neon/tools/ChangeLog26
-rw-r--r--neon/tools/README4
-rwxr-xr-xneon/tools/cvsdist143
-rwxr-xr-xneon/tools/mk19
-rwxr-xr-xneon/tools/mklsm37
-rwxr-xr-xneon/tools/reconf6
-rwxr-xr-xneon/tools/update-pot.sh10
263 files changed, 48161 insertions, 0 deletions
diff --git a/neon/.cvsignore b/neon/.cvsignore
new file mode 100644
index 000000000..ba4ab4f6a
--- /dev/null
+++ b/neon/.cvsignore
@@ -0,0 +1,9 @@
+config.h.in
+config.h
+configure
+config.status
+config.log
+Makefile
+aclocal.m4
+nget
+config.cache
diff --git a/neon/.package b/neon/.package
new file mode 100644
index 000000000..493f404ab
--- /dev/null
+++ b/neon/.package
@@ -0,0 +1 @@
+announce-list=neon@webdav.org
diff --git a/neon/.release.sh b/neon/.release.sh
new file mode 100755
index 000000000..78cdcc526
--- /dev/null
+++ b/neon/.release.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+major=`echo $1 | sed "s/\..*//g"`
+minor=`echo $1 | sed "s/^[0-9]*\.\(.*\)\.[0-9]*$/\1/g"`
+rel=`echo $1 | sed "s/^.*\./\1/g"`
+version=$1
+
+for f in config.hw; do
+in=$f.in
+out=$f
+sed -e "s/@VERSION@/$version/g" \
+ -e "s/@MAJOR@/$major/g" \
+ -e "s/@MINOR@/$minor/g" \
+ -e "s/@RELEASE@/$release/g" < $in > $out
+done
diff --git a/neon/AUTHORS b/neon/AUTHORS
new file mode 100644
index 000000000..f64fe1852
--- /dev/null
+++ b/neon/AUTHORS
@@ -0,0 +1 @@
+Joe Orton <joe@orton.demon.co.uk>
diff --git a/neon/BUGS b/neon/BUGS
new file mode 100644
index 000000000..5a4e3e260
--- /dev/null
+++ b/neon/BUGS
@@ -0,0 +1,7 @@
+
+Known problems/bugs in neon -*- text -*-
+--------------------------- Id: BUGS,v 1.2 2000/07/27 20:59:19 joe Exp
+
+1. Each new SSL request is opening a new connection to the server.
+ VERY slow.
+
diff --git a/neon/ChangeLog b/neon/ChangeLog
new file mode 100644
index 000000000..34811560e
--- /dev/null
+++ b/neon/ChangeLog
@@ -0,0 +1,24 @@
+Wed May 10 19:17:24 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * configure.in: Print configuration message, check for ranlib.
+
+Wed May 10 19:16:43 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * example/nget.c (main): Allow output to stdout.
+
+Wed May 10 19:15:56 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * Makefile.in: Added shared, install* targets
+
+Wed May 10 17:47:30 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * example/nget.c (pretty_progress_bar): New function, from
+ cadaver.
+
+Wed May 10 14:42:45 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * example/nget.c: New file.
+
+Wed May 10 14:41:39 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * configure.in, Makefile.in, .cvsignore, install-sh: New files.
diff --git a/neon/INSTALL.win32 b/neon/INSTALL.win32
new file mode 100644
index 000000000..04e161189
--- /dev/null
+++ b/neon/INSTALL.win32
@@ -0,0 +1,14 @@
+
+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.
+
+
diff --git a/neon/Makefile.in b/neon/Makefile.in
new file mode 100644
index 000000000..3389afb73
--- /dev/null
+++ b/neon/Makefile.in
@@ -0,0 +1,80 @@
+
+CFLAGS = -Ilib -I. -Isrc @CFLAGS@ @DEFS@
+LDFLAGS = -L. @LDFLAGS@
+LIBS = @LIBS@
+CC = @CC@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL = @INSTALL@
+AR = ar
+RANLIB = @RANLIB@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir = @libdir@
+man1dir = @mandir@/man1
+# Hmmmmm... the i18n stuff seems to want this:
+datadir = $(prefix)/@DATADIRNAME@
+# Previously I had:
+# datadir = @datadir@/sitecopy
+docdir = $(prefix)/doc/sitecopy
+sc_datadir = $(datadir)/sitecopy
+includedir = @includedir@
+neonincludes = $(includedir)/libneon
+
+top_srcdir = @top_srcdir@
+# Where does top_builddir come from?
+top_builddir = @top_srcdir@
+srcdir = @srcdir@
+# intl stuff
+localedir = $(datadir)/locale
+gnulocaledir = $(prefix)/share/locale
+gettextsrcdir = $(prefix)/share/gettext
+aliaspath = $(localedir):.
+@SET_MAKE@
+
+OBJECTS = src/http_request.o src/http_basic.o src/dav_basic.o src/dav_207.o \
+ src/string_utils.o src/dates.o src/xalloc.o src/hip_xml.o \
+ src/base64.o src/md5.o src/http_utils.o src/uri.o src/socket.o \
+ src/http_auth.o
+
+DIST_HEADERS = http_request.h http_utils.h uri.h socket.h http_basic.h \
+ dav_basic.h dav_207.h dav_props.h hip_xml.h dates.h string_utils.h
+
+libneon.a: $(OBJECTS)
+ $(AR) cru $@ $(OBJECTS)
+ $(RANLIB) $@
+
+shared: libneon.so
+
+libneon.so: $(OBJECTS)
+ gcc -shared -o $@ $(OBJECTS)
+
+examples: nget
+
+nget: libneon.a example/nget.o lib/basename.o
+ $(CC) $(LDFLAGS) -o $@ example/nget.o lib/basename.o -lneon
+
+install: install-static install-headers
+
+install-static: libneon.a
+ $(INSTALL) -d $(libdir)
+ cp libneon.a $(libdir)/libneon.a
+
+install-shared: libneon.so
+ $(INSTALL) -d $(libdir)
+ cp libneon.so $(libdir)/libneon.so
+
+install-headers:
+ $(INSTALL) -d $(neonincludes)
+ @for h in $(DIST_HEADERS); do \
+ echo Installing $$h into $(neonincludes); \
+ $(INSTALL_DATA) src/$$h $(neonincludes)/$$h; \
+ done
+
+neon_dir = src
+config_h = config.h
+
+include src/Makefile.incl
+
diff --git a/neon/NEWS b/neon/NEWS
new file mode 100644
index 000000000..5b96550a5
--- /dev/null
+++ b/neon/NEWS
@@ -0,0 +1,2 @@
+Changes in release 0.1.0
+- Initial release.
diff --git a/neon/README b/neon/README
new file mode 100644
index 000000000..a2a3478c8
--- /dev/null
+++ b/neon/README
@@ -0,0 +1,25 @@
+
+neon is an HTTP and WebDAV client library.
+
+Current features:
+
+ - High-level interface to HTTP and WebDAV methods.
+ - Low-level interface to HTTP request handling, to allow implementing
+ new methods easily.
+ - Persistent connection support (HTTP/1.1 and HTTP/1.0 aware)
+ - Basic and digest authentication (RFC2617) (including auth-int, md5-sess)
+ - Proxy support (including basic/digest authentication)
+ - Generic WebDAV 207 XML response handling mechanism
+ - XML parsing using expat or libxml parser
+ - Easy generation of error messages from 207 error responses
+ - Basic HTTP/1.1 methods: GET, PUT, HEAD, OPTIONS, conditional PUT
+ - WebDAV resource manipulation: MOVE, COPY, DELETE, MKCOL.
+ - WebDAV metadata support: set and remove properties (PROPPATCH), query
+ any set of properties (PROPFIND).
+
+Provides lower-level interfaces to directly implement new HTTP
+methods, and higher-level interfaces so that you don't have to
+worry about the lower-level stuff.
+
+Joe Orton
+<joe@orton.demon.co.uk>
diff --git a/neon/THANKS b/neon/THANKS
new file mode 100644
index 000000000..accdb9753
--- /dev/null
+++ b/neon/THANKS
@@ -0,0 +1,10 @@
+
+Greg Stein <gstein@lyra.org>
+Michael Sobolev <mss@despair.spb.ru>
+
+Originators of stolen code:
+
+Daniel Veillard <Daniel.Veillard@w3.org>
+Eric S Raymond <esr@snark.thyrsus.com>
+Ulrich Drepper <drepper@gnu.org>
+
diff --git a/neon/TODO b/neon/TODO
new file mode 100644
index 000000000..555670638
--- /dev/null
+++ b/neon/TODO
@@ -0,0 +1,104 @@
+
+To Do List for neon -*- text -*-
+------------------- Id: TODO,v 1.3 2000/05/13 20:52:43 joe Exp
+
+Please submit feature requests to <mailto:neon@webdav.org>
+
+1. Support for HTTP-extended authoring methods ala WebRFM etc; using
+ New-URI header etc. Also support the BROWSE and INDEX methods. The
+ protocol is documented at:
+ http://www.ics.uci.edu/pub/ietf/webdav/ns_dav.html
+
+2. Add proper domain support to authentication code. (requires full
+ URI parsing support). Need to tell the auth layer the server
+ details.
+
+3. Add a callback to determine whether a specific request should go
+ through the proxy server or not... based on URI, maybe method too
+ since lots of HTTP/1.0 proxies break on DAV requests?
+
+4. Better cnonce generation for authentication: use /dev/random or
+ whatever like mod_auth_digest.
+
+5. Add request hooks to remove all authentication code from
+ http_request.c.
+
+6. PUT/GET with ranges... http_get_range
+
+7. hip_xml/dav_207/dav_prop might need a revamp.
+
+ hip_xml: use an expat-esque interface; i.e. make hip_xml_parser
+ opaque and add API to set handlers rather than initialize
+ structures directly. Two problems need to be solved more elegantly:
+
+ 1) Allowing "sub-handler" for allowing parsing property element
+ contents independantly of the overally 207 response parse.
+
+ 2) Allow "mixed-mode" parsing of XML which mixes cdata + elements.
+
+ Probably, make hip_xml more of a "utility" layer rather than a
+ "driving" layer; abstract out expat/libxml, namespace lookups,
+ element name -> id mapping, easy CDATA handling...
+
+8. WebDAV class 2 locking. Problems: this requires parsing the XML
+ within a property element. This could be achieved by doing a
+ completely new XML parse of the property value returned by end_prop
+ from the 207 code, but this is a bit noddy. Options:
+
+ a) Ideally: extend hip_xml and the 207 layer to allow doing a
+ proper start/end_element/cdata parse of the XML *within* a
+ property. This is tricky since it means extending hip_xml to
+ *dynamically* determine whether to be in "collect" mode or not. Add
+ another callback for this?
+
+9. DeltaV support (http://www.webdav.org/deltav/). See also the
+ inversion project (http://inversion.tigris.org/) who might build a
+ versioning system over DAV.
+
+10. ACL support (http://www.webdav.org/acl/)
+
+11. DASL support (http://www.webdav.org/dasl/). Xythos have server
+ support for this (www.sharemation.com).
+
+12. SSL/TLS support... make it pluggable so we don't have to export
+ crypto-related code at ALL?
+
+13. Should we really be abort()'ing on out-of-memory? It makes a lot
+ of code MUCH simpler (esp. sbuffer_* usage).
+
+14. Nicer request-header manipulation... some kind of indexed data
+ structure, so we're sure we don't add the same header to the
+ request twice (e.g. Cache-Control). Must be at least as flexible
+ as sbuffer usage, though.
+
+16. Socket status notification (socket.c:sock_register_*) is awful.
+
+17. Should we really be i18n'izing the low-level error messages in
+ http_request.c, dav_207.c ? It seems nice and clever to, so the
+ user REALLY know what is going wrong with the server (probably),
+ but it is maybe a bit frightening.
+
+18. PROPFIND/propnames support.
+
+19. libtool support, for proper shared libraries.
+
+20. Add full URI parser + handling. Or stop pretending we are doing
+ "URI" parsing, and just handle HTTP URL's.
+
+21. Storing multiple authentication "sessions" within an actual
+ http_auth_session, so I log into e.g. /foo/ and /bar/ (which
+ are not in the same authentication domain)
+ switch between them without having to re-enter passwords all the
+ time.
+
+22. Handle PROPFIND property error responses properly.
+
+23. Mechanism for aborting a request mid-response; e.g., when a GET
+ fails due to out of disk space, abort the download.
+
+24. In a PROPFIND response, if a property were to include e.g., a
+ DAV:multistatus element, this would not be handled correctly.
+
+25. A BSD C library has an MD5 implementation in the C Library...
+ support this. (someone who runs a BSD will need to do this)
+
diff --git a/neon/autogen.sh b/neon/autogen.sh
new file mode 100644
index 000000000..2bd9617bb
--- /dev/null
+++ b/neon/autogen.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+rm -f config.cache
+([ ! -d macros ] || (echo -n aclocal...\ && aclocal -I macros)) && \
+([ ! -r acconfig.h ] || (echo -n autoheader...\ && autoheader)) && \
+([ ! -r macros/acconfig.h ] || (echo -n autoheader...\ && autoheader -l macros)) && \
+echo -n autoconf...\ && autoconf
diff --git a/neon/config.guess b/neon/config.guess
new file mode 100755
index 000000000..a1b76ce2f
--- /dev/null
+++ b/neon/config.guess
@@ -0,0 +1,1034 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+# Please send patches to the Autoconf mailing list <autoconf@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# Use $HOST_CC if defined. $CC may point to a cross-compiler
+if test x"$CC_FOR_BUILD" = x; then
+ if test x"$HOST_CC" != x; then
+ CC_FOR_BUILD="$HOST_CC"
+ else
+ if test x"$CC" != x; then
+ CC_FOR_BUILD="$CC"
+ else
+ CC_FOR_BUILD=cc
+ fi
+ fi
+fi
+
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ cat <<EOF >$dummy.s
+ .globl main
+ .ent main
+main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr [[A-Z]] [[a-z]]`
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-cbm-sysv4
+ exit 0;;
+ amiga:NetBSD:*:*)
+ echo m68k-cbm-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ arc64:OpenBSD:*:*)
+ echo mips64el-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hkmips:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ arm32:NetBSD:*:*)
+ echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ SR2?01:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:*|MIS*:OSx*:*:*|MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:NetBSD:*:*)
+ echo m68k-atari-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:NetBSD:*:*)
+ echo m68k-sun-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:NetBSD:*:*)
+ echo m68k-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:NetBSD:*:*)
+ echo powerpc-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy \
+ && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
+ -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i?86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:4)
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=4.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/6?? | 9000/7?? | 9000/80[024] | 9000/8?[136790] | 9000/892 )
+ sed 's/^ //' << EOF >$dummy.c
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ ($CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+ rm -f $dummy.c $dummy
+ esac
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE*:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i?86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo t3e-cray-unicosmk${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ F300:UNIX_System_V:*:*)
+ FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ F301:UNIX_System_V:*:*)
+ echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'`
+ exit 0 ;;
+ hp3[0-9][05]:NetBSD:*:*)
+ echo m68k-hp-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ i?86:BSD/386:*:* | i?86:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ if test -x /usr/bin/objformat; then
+ if test "elf" = "`/usr/bin/objformat`"; then
+ echo ${UNAME_MACHINE}-unknown-freebsdelf`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'`
+ exit 0
+ fi
+ fi
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:NetBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:Linux:*:*)
+ # uname on the ARM produces all sorts of strangeness, and we need to
+ # filter it out.
+ case "$UNAME_MACHINE" in
+ armv*) UNAME_MACHINE=$UNAME_MACHINE ;;
+ arm* | sa110*) UNAME_MACHINE="arm" ;;
+ esac
+
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ ld_help_string=`cd /; ld --help 2>&1`
+ ld_supported_emulations=`echo $ld_help_string \
+ | sed -ne '/supported emulations:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported emulations: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_emulations" in
+ i?86linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" ; exit 0 ;;
+ i?86coff) echo "${UNAME_MACHINE}-pc-linux-gnucoff" ; exit 0 ;;
+ sparclinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ armlinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ m68klinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ elf32ppc) echo "powerpc-unknown-linux-gnu" ; exit 0 ;;
+ esac
+
+ if test "${UNAME_MACHINE}" = "alpha" ; then
+ sed 's/^ //' <<EOF >$dummy.s
+ .globl main
+ .ent main
+ main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ LIBC=""
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+
+ objdump --private-headers $dummy | \
+ grep ld.so.1 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0
+ elif test "${UNAME_MACHINE}" = "mips" ; then
+ cat >$dummy.c <<EOF
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __MIPSEB__
+ printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+ printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ else
+ # Either a pre-BFD a.out linker (linux-gnuoldld)
+ # or one that does not give us useful --help.
+ # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+ # If ld does not provide *any* "supported emulations:"
+ # that means it is gnuoldld.
+ echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:"
+ test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+
+ case "${UNAME_MACHINE}" in
+ i?86)
+ VENDOR=pc;
+ ;;
+ *)
+ VENDOR=unknown;
+ ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+# if __GLIBC__ >= 2
+ printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+ printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+ i?86:DYNIX/ptx:4*:*)
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i?86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ i?86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ i?86:UnixWare:*:*)
+ if /bin/uname -X 2>/dev/null >/dev/null ; then
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ fi
+ echo ${UNAME_MACHINE}-unixware-${UNAME_RELEASE}-${UNAME_VERSION}
+ exit 0 ;;
+ pc:*:*:*)
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i?86:LynxOS:2.*:* | i?86:LynxOS:3.[01]*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:*:6*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R4000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+ printf ("vax-dec-bsd\n"); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
diff --git a/neon/config.hw b/neon/config.hw
new file mode 100644
index 000000000..9b235ff72
--- /dev/null
+++ b/neon/config.hw
@@ -0,0 +1,38 @@
+/*
+ Win32 config.h
+ Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: config.hw,v 1.1 2000/12/13 20:15:06 joe Exp
+*/
+
+#ifdef WIN32
+#define HAVE_STRING_H
+#define HAVE_EXPAT
+#define HAVE_MEMCPY
+
+
+// Win32 uses a underscore, so we use a macro to eliminate that.
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strcasecmp strcmpi
+#define strncasecmp strnicmp
+#define ssize_t int
+
+#include <io.h>
+#define read _read
+#endif
diff --git a/neon/config.hw.in b/neon/config.hw.in
new file mode 100644
index 000000000..3d86b2768
--- /dev/null
+++ b/neon/config.hw.in
@@ -0,0 +1,53 @@
+/*
+ Win32 config.h
+ Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+#ifdef _WIN32
+#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 HAVE_STDLIB_H
+#define HAVE_STRING_H
+#define HAVE_LIMITS_H
+
+/* Win32 uses a underscore, so we use a macro to eliminate that. */
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strcasecmp strcmpi
+#define strncasecmp strnicmp
+#define ssize_t int
+#define inline __inline
+#define off_t _off_t
+
+#include <io.h>
+#define read _read
+
+#endif
diff --git a/neon/config.sub b/neon/config.sub
new file mode 100755
index 000000000..692de9b61
--- /dev/null
+++ b/neon/config.sub
@@ -0,0 +1,993 @@
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+# Copyright (C) 1991, 92-97, 1998 Free Software Foundation, Inc.
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+ echo Configuration name missing. 1>&2
+ echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+ echo "or $0 ALIAS" 1>&2
+ echo where ALIAS is a recognized configuration type. 1>&2
+ exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+ *local*)
+ echo $1
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ linux-gnu*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple)
+ os=
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ tahoe | i860 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
+ | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
+ | 580 | i960 | h8300 \
+ | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w \
+ | alpha | alphaev[4-7] | alphaev56 | alphapca5[67] \
+ | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \
+ | 1750a | dsp16xx | pdp11 | mips64 | mipsel | mips64el \
+ | mips64orion | mips64orionel | mipstx39 | mipstx39el \
+ | sparc | sparclet | sparclite | sparc64 | v850)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i[34567]86)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ vax-* | tahoe-* | i[34567]86-* | i860-* | m32r-* | m68k-* | m68000-* \
+ | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+ | power-* | none-* | 580-* | cray2-* | h8300-* | i960-* \
+ | xmp-* | ymp-* \
+ | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \
+ | alpha-* | alphaev[4-7]-* | alphaev56-* | alphapca5[67] \
+ | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+ | clipper-* | orion-* \
+ | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
+ | sparc64-* | mips64-* | mipsel-* \
+ | mips64el-* | mips64orion-* | mips64orionel-* \
+ | mipstx39-* | mipstx39el-* \
+ | f301-* | armv*-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-cbm
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-cbm
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-cbm
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [ctj]90-cray)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7)
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ os=-mpeix
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ os=-mpeix
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i[34567]86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i[34567]86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i[34567]86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i[34567]86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netwinder)
+ basic_machine=armv4l-corel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | nexen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | k6 | 6x86)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | nexen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | k6-* | 6x86-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=rs6000-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ *mint | *MiNT)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sparc)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -rhapsody* \
+ | -openstep* | -mpeix* | -oskit*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -*MiNT)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-corel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f301-fujitsu)
+ os=-uxpv
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -*mint | -*MiNT)
+ vendor=atari
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
diff --git a/neon/configure.in b/neon/configure.in
new file mode 100644
index 000000000..ba6c745eb
--- /dev/null
+++ b/neon/configure.in
@@ -0,0 +1,42 @@
+
+AC_INIT(src/http_request.c)
+
+AC_CONFIG_HEADER(config.h)
+
+AC_DEFINE([_GNU_SOURCE])
+
+AC_PROG_CC
+AC_PROG_MAKE_SET
+AC_ISC_POSIX
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_LANG_C
+AC_C_INLINE
+AC_C_CONST
+
+AC_HEADER_STDC
+
+AC_EXEEXT
+
+NEON_XML_PARSER
+
+LIBNEON_SOURCE_CHECKS
+
+AC_OUTPUT(Makefile)
+
+cat <<EOF
+
+Using configuration:
+
+ Install prefix: ${prefix}
+ Compiler: ${CC}
+ XML Parser: ${neon_xml_parser_message}
+
+Now run: 'make' to compile the neon library (libneon.a)
+ 'make shared' to compile a shared library with GCC (libneon.so)
+ 'make examples' to compile the example programs
+
+ (Note: You might need to use GNU make if this is not the default 'make'.
+ GNU make could be installed as 'gmake' on your system.)
+
+EOF
diff --git a/neon/doc/.cvsignore b/neon/doc/.cvsignore
new file mode 100644
index 000000000..2c46ef53f
--- /dev/null
+++ b/neon/doc/.cvsignore
@@ -0,0 +1,8 @@
+*.?
+*.html
+*.pdf
+*.ps
+*.tex
+*.sgml
+*.junk
+html
diff --git a/neon/doc/TODO b/neon/doc/TODO
new file mode 100644
index 000000000..febed3054
--- /dev/null
+++ b/neon/doc/TODO
@@ -0,0 +1,156 @@
+/* List of interfaces needing reference documentation. -*- c -*- */
+
+/* ne_session.h */
+
+### DONE: basics
+ne_session *ne_session_create(const char *scheme, const char *hostname, int port);
+void ne_session_destroy(ne_session *sess);
+void ne_close_connection(ne_session *sess);
+void ne_session_proxy(ne_session *sess, const char *hostname, int port);
+
+### DONE: error handling
+void ne_set_error(ne_session *sess, const char *format, ...);
+const char *ne_get_error(ne_session *sess);
+
+### DONE: options
+void ne_set_useragent(ne_session *sess, const char *product);
+void ne_set_expect100(ne_session *sess, int use_expect100);
+void ne_set_persist(ne_session *sess, int persist);
+void ne_set_read_timeout(ne_session *sess, int timeout);
+
+### TODO: progress + status callbcacks
+void ne_set_progress(ne_session *sess, ne_progress progress, void *userdata);
+
+### TODO: status callback
+typedef enum ne_conn_status;
+typedef void (*ne_notify_status)(void *userdata, ne_conn_status status, const char *info);
+void ne_set_status(ne_session *sess, ne_notify_status status, void *userdata);
+
+### DONE: SSL verification
+
+typedef struct ne_ssl_dname;
+char *ne_ssl_readable_dname(const ne_ssl_dname *dn);
+typedef struct ne_ssl_certificate;
+#define NE_SSL_*
+typedef int (*ne_ssl_verify_fn)(void *userdata, int failures,
+ const ne_ssl_certificate *cert);
+void ne_ssl_set_verify(ne_session *sess, ne_ssl_verify_fn fn, void *userdata);
+
+### DONE: SSL server certs
+int ne_ssl_load_ca(ne_session *sess, const char *file);
+int ne_ssl_load_default_ca(ne_session *sess);
+
+### TODO: SSL client certs
+typedef int (*ne_ssl_keypw_fn)(void *userdata, char *pwbuf, size_t len);
+void ne_ssl_keypw_prompt(ne_session *sess, ne_ssl_keypw_fn fn, void *ud);
+int ne_ssl_load_pkcs12(ne_session *sess, const char *fn);
+int ne_ssl_load_pem(ne_session *sess, const char *certfn, const char *keyfn);
+typedef void (*ne_ssl_provide_fn)(void *userdata, ne_session *sess,
+ const ne_ssl_dname *server);
+void ne_ssl_provide_ccert(ne_session *sess, ne_ssl_provide_fn fn, void *userdata);
+
+#ifdef NEON_SSL
+SSL_CTX *ne_ssl_get_context(ne_session *sess);
+X509 *ne_ssl_server_cert(ne_session *req);
+#endif
+
+### TODO: utility functions
+int ne_version_pre_http11(ne_session *sess);
+const char *ne_get_server_hostport(ne_session *sess);
+const char *ne_get_scheme(ne_session *sess);
+void ne_fill_server_uri(ne_session *sess, ne_uri *uri);
+
+/* end of ne_session.h *****************************************/
+
+/* ne_request.h */
+
+### DONE: request basics
+ne_request *ne_request_create(ne_session *sess, const char *method, const char *path);
+int ne_request_dispatch(ne_request *req);
+void ne_request_destroy(ne_request *req);
+
+### DONE: request status
+const ne_status *ne_get_status(ne_request *req);
+
+### TODO: request bodies
+void ne_set_request_body_buffer(ne_request *req, const char *buf, size_t count);
+int ne_set_request_body_fd(ne_request *req, int fd, size_t count);
+
+typedef ssize_t (*ne_provide_body)(void *userdata,
+ char *buffer, size_t buflen);
+void ne_set_request_body_provider(ne_request *req, size_t size,
+ ne_provide_body provider, void *userdata);
+
+### TODO: response bodies
+typedef int (*ne_accept_response)(void *userdata, ne_request *req, ne_status *st);
+int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st);
+int ne_accept_always(void *userdata, ne_request *req, ne_status *st);
+void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
+ ne_block_reader reader, void *userdata);
+
+### TODO: response headers
+typedef void (*ne_header_handler)(void *userdata, const char *value);
+void ne_add_response_header_handler(ne_request *req, const char *name,
+ ne_header_handler hdl, void *userdata);
+void ne_add_response_header_catcher(ne_request *req,
+ ne_header_handler hdl, void *userdata);
+
+void ne_duplicate_header(void *userdata, const char *value);
+void ne_handle_numeric_header(void *userdata, const char *value);
+
+### DONE: request headers
+void ne_add_request_header(ne_request *req, const char *name, const char *value);
+void ne_print_request_header(ne_request *req, const char *name, const char *format, ...);
+
+### TODO: misc
+ne_session *ne_get_session(ne_request *req);
+
+### TODO: caller-pulls request interface
+int ne_begin_request(ne_request *req);
+int ne_end_request(ne_request *req);
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);
+
+### TODO: hooks
+typedef void (*ne_free_hooks)(void *cookie);
+typedef void (*ne_create_request_fn)(void *userdata, ne_request *req,
+ const char *method, const char *path);
+void ne_hook_create_request(ne_session *sess, ne_create_request_fn fn, void *userdata);
+
+typedef void (*ne_pre_send_fn)(void *userdata, ne_buffer *header);
+void ne_hook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);
+
+typedef int (*ne_post_send_fn)(void *userdata, const ne_status *status);
+void ne_hook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata);
+
+typedef void (*ne_destroy_fn)(void *userdata);
+void ne_hook_destroy_request(ne_session *sess, ne_destroy_fn fn, void *userdata);
+
+void ne_hook_destroy_session(ne_session *sess, ne_destroy_fn fn, void *userdata);
+
+typedef void *(*ne_accessor_fn)(void *userdata);
+void ne_hook_session_accessor(ne_session *sess, const char *id, ne_accessor_fn, void *userdata);
+void ne_hook_request_accessor(ne_request *req, const char *id, ne_accessor_fn, void *userdata);
+
+void *ne_null_accessor(void *userdata);
+
+void *ne_session_hook_private(ne_session *sess, const char *id);
+
+void *ne_request_hook_private(ne_request *req, const char *id);
+
+/* ne_207.h */
+/* ne_acl.h */
+/* DONE: ne_alloc.h */
+/* DONE: ne_auth.h */
+/* ne_basic.h */
+/* ne_compress.h */
+/* ne_cookies.h */
+/* ne_dates.h */
+/* ne_locks.h */
+/* ne_props.h */
+/* ne_redirect.h */
+/* ne_socket.h */
+/* MOSTLY DONE: ne_string.h */
+/* ne_uri.h */
+/* ne_utils.h */
+/* ne_xml.h */
+
diff --git a/neon/doc/html.xsl b/neon/doc/html.xsl
new file mode 100644
index 000000000..42fc397ba
--- /dev/null
+++ b/neon/doc/html.xsl
@@ -0,0 +1,38 @@
+<?xml version='1.0'?>
+
+<!-- This file wraps around the DocBook HTML XSL stylesheet to customise
+ - some parameters; add the CSS stylesheet, etc.
+ -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
+
+<xsl:variable name="html.stylesheet">../manual.css</xsl:variable>
+
+<!-- for sections with id attributes, use the id in the filename.
+ This produces meaningful (and persistent) URLs for the sections. -->
+<xsl:variable name="use.id.as.filename" select="1"/>
+
+<!-- enable the shaded verbatim (programlisting etc) blocks. -->
+<!-- <xsl:variable name="shade.verbatim" select="1"/> -->
+
+<!-- add class="programlisting" attribute on the <pre>s from
+ <programlisting>s so that the CSS can style them nicely. -->
+<xsl:attribute-set name="shade.verbatim.style">
+ <xsl:attribute name="class">programlisting</xsl:attribute>
+</xsl:attribute-set>
+
+<!-- use sane ANSI C function prototypes -->
+<xsl:variable name="funcsynopsis.style">ansi</xsl:variable>
+
+<!-- don't generate table of contents within each chapter chunk. -->
+<xsl:variable name="generate.chapter.toc" select="0"/>
+
+<xsl:variable name="generate.appendix.toc" select="0"/>
+
+</xsl:stylesheet>
+
diff --git a/neon/doc/man.xsl b/neon/doc/man.xsl
new file mode 100644
index 000000000..cad974474
--- /dev/null
+++ b/neon/doc/man.xsl
@@ -0,0 +1,14 @@
+<?xml version='1.0'?>
+
+<!-- This file wraps around the xmlto db2man XSL stylesheet to
+customise some parameters. -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'>
+
+<xsl:import href="/usr/share/xmlto/xsl/db2man/docbook.xsl"/>
+
+<!-- use an improved xref -->
+<xsl:import href="./xref-man.xsl"/>
+
+</xsl:stylesheet>
diff --git a/neon/doc/manual.css b/neon/doc/manual.css
new file mode 100644
index 000000000..034d7a7e7
--- /dev/null
+++ b/neon/doc/manual.css
@@ -0,0 +1,29 @@
+
+p, pre.funcsynopsisinfo { margin-left: 0.4em; margin-right: 0.4em; }
+
+div.funcsynopsis { line-height: 0.5em; }
+
+div.legalnotice { font-size: 80%; margin-left: 2em; }
+
+a:visited { color: darkgreen; }
+
+div.navheader { border-top: thin solid #bbf2bb; }
+div.navfooter { border-bottom: thin solid #bbf2bb; }
+
+pre.programlisting {
+ background-color: #dddddd;
+ margin-left: 0.6em; margin-right: 1em;
+ padding: 0.3em; width: 50em;
+}
+
+h1.title { border-bottom: thick solid #bbf2bb; padding-bottom: 0.1em; }
+
+div.toc {
+ border-left: thick solid #bbf2bb; padding-left: 0.5em;
+ }
+
+h2, h3 { padding-left: 0.2em; padding-top: -0.1em; }
+
+h2 { background-color: #bbf2bb; font-size: 110%;
+ padding-bottom: 0.1em; padding-top: 0.1em; }
+
diff --git a/neon/doc/manual.xml b/neon/doc/manual.xml
new file mode 100644
index 000000000..14b8a68e2
--- /dev/null
+++ b/neon/doc/manual.xml
@@ -0,0 +1,519 @@
+<?xml version='1.0'?> <!-- -*- DocBook -*- -->
+
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+
+<!ENTITY fdl SYSTEM "fdl.sgml">
+
+]>
+
+<book>
+ <bookinfo>
+ <title>neon HTTP/WebDAV client library</title>
+ <author>
+ <firstname>Joe</firstname><surname>Orton</surname>
+ <affiliation>
+ <address><email>neon@webdav.org</email></address>
+ </affiliation>
+ </author>
+ <copyright><year>2001</year><holder>Joe Orton</holder></copyright>
+
+ <legalnotice>
+ <para>Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.1
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, with no Front-Cover Texts,
+ and with no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".</para>
+ </legalnotice>
+
+ </bookinfo>
+
+ <chapter>
+ <title>Introduction to neon</title>
+ <para>This is neon, an HTTP and WebDAV client library</para>
+
+ <sect1>
+ <title>How to use neon from your application</title>
+
+ <para>The neon source package is designed to be easily
+ incorporated into applications:</para>
+
+ <itemizedlist>
+ <listitem>
+
+ <para>autoconf macros are distributed in the 'macros'
+ subdirectory of the neon distribution. Use NEON_LIBRARY
+ from your configure.in to check for the presence of the
+ neon library installed on the system. The macro adds an
+ '--with-neon=...' argument to configure, which allows the
+ user to specify a location for the library (the standard
+ /usr and /usr/local directories are checked automatically
+ without having to be specified).</para></listitem>
+
+ <listitem><para>The 'src' directory of the neon package can be
+ imported directly into your application, if you do not wish
+ to add an external dependency. If you wish to bundle, use
+ the NEON_BUNDLED macro to configure neon in your application:
+ here, the neon sources are bundled in a directory called
+ 'libneon':</para>
+
+ <literal>
+ NEON_BUNDLED(libneon, ...)
+ </literal>
+
+
+ <para>If your application supports builds where srcdir != builddir,
+ you should use the NEON_VPATH_BUNDLED macro like this:</para>
+
+ <literal>
+ NEON_VPATH_BUNDLED(${srcdir}/libneon, libneon, ...)
+ </literal>
+
+ <para>If you use this macro, a '--with-included-neon' option
+ will be added to the generated configure script. This
+ allows the user to force the bundled neon to be used in the
+ application, rather than any neon library found on the
+ system. If you allow neon to be configured this way, you
+ must also configure an XML parser. Use the NEON_XML_PARSER
+ macro to do this.</para></listitem>
+
+ <listitem><para>The final argument to the _BUNDLED macros is a
+ set of actions which are executed if the bundled build *is*
+ chosen (rather than an external neon which might have been
+ found on the user's system). In here, use either the
+ NEON_LIBTOOL_BUILD or NEON_NORMAL_BUILD macro to set up the
+ neon Makefile appropriately: including adding the neon source
+ directory to the recursive make.</para></listitem>
+
+ </itemizedlist>
+
+ <para>A full fragment might be:</para>
+
+ <literal>
+ NEON_BUNDLED(libneon, [
+ NEON_NORMAL_BUILD
+ NEON_XML_PARSER
+ SUBDIRS="libneon $SUBDIRS"
+ ])
+ </literal>
+
+ <para>This means the bundled neon source directory (called 'libneon')
+ is used if no neon is found on the system, and the standard XML
+ parser search is used.</para>
+
+ </sect1>
+
+ <sect1>
+ <title>neon API guidelines</title>
+
+ <para>neon reserves the namespace <literal>ne_*</literal>: an
+ application which uses neon may not use symbols within this
+ namespace.</para>
+
+ </sect1>
+
+ <sect1>
+ <title>Protocol compliance</title>
+
+ <para>neon is intended to be compliant with all relevant IETF and W3C
+ standards.</para>
+
+ <sect2><title>RFC2518</title>
+
+ <para>neon is deliberately not compliant with section 23.4.2, and
+ treats property names as a (namespace-URI, name) pair. This is
+ <ulink
+ url="http://lists.w3.org/Archives/Public/w3c-dist-auth/1999OctDec/0343.html">generally
+ considered</ulink> to be the correct behaviour by the WebDAV
+ WG and is likely to change in a future revision of the spec.</para>
+
+ </sect2>
+
+ <sect2>
+ <title>RFC2616</title>
+
+ <para>The redirect interface is deliberately not compliant with
+ section 10.3, and will automatically follow redirects for the
+ <literal>PROPFIND</literal> and <literal>OPTIONS</literal>
+ methods as well as <literal>GET</literal> and
+ <literal>HEAD</literal>. It <ulink url="http://www.apachelabs.org/apache-mbox/200102.mbox/%3C20010224232203.G799@waka.ebuilt.net%3E">has been stated</ulink> that this was the intent of the specification by one of the authors.</para>
+
+ </sect2>
+
+ </sect1>
+
+ </chapter>
+
+ <chapter>
+ <title>The neon API for the C language</title>
+
+ <sect1>
+ <title>Sessions</title>
+
+ <para>An HTTP session is created using the
+ <citerefentry><refentrytitle>ne_session_create</refentrytitle></citerefentry>
+ function</para>
+ </sect1>
+
+ <sect1>
+ <title>Low-level request interface</title>
+ </sect1>
+
+ <sect1>
+ <title>Basic HTTP and WebDAV methods</title>
+ <para>ne_basic.h</para>
+ </sect1>
+
+ <sect1>
+ <title>HTTP authentication</title>
+
+ <para>Authentication is supported using callbacks: using the
+ <citerefentry><refentrytitle>ne_set_server_auth</refentrytitle></citerefentry>
+ function, a callback can be registered which will supply
+ authentication credentials upon demand. In an interactive
+ application, this will typically be done using some form of
+ username/password prompt.</para>
+
+ <para>Two types of authentication are supported: server
+ authentication (via the <function>ne_set_server_auth</function>
+ function), and proxy authentication (via the
+ <function>ne_set_proxy_auth</function> function).</para>
+ </sect1>
+
+ <sect1>
+ <title>Parsing XML</title>
+ <para>ne_xml.h functions</para>
+ </sect1>
+
+ <sect1>
+ <title>WebDAV properties</title>
+ <para>ne_props.h functions</para>
+ </sect1>
+
+ <sect1>
+ <title>WebDAV locking</title>
+ <para>ne_locks.h functions</para>
+ </sect1>
+
+ <sect1>
+ <title>Utility functions</title>
+ <para>stuff</para>
+ <sect2>
+ <title>String handling</title>
+ </sect2>
+ <sect2>
+ <title>Date/time manipulation</title>
+ </sect2>
+ </sect1>
+
+ </chapter>
+
+ <reference>
+
+ <title>neon API reference</title>
+
+ <refentry>
+
+ <refentryinfo>
+ <title>neon</title>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>ne_session_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_session_create</refname>
+ <refname>ne_session_close_connection</refname>
+ <refname>ne_session_server</refname>
+ <refname>ne_session_destroy</refname>
+ <refpurpose>Manipulate HTTP sessions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+ <funcprototype>
+ <funcdef><type>ne_session *</type><function>ne_session_create</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>ne_session_server</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ <paramdef>int <parameter>port</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>ne_close_connection</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_session_destroy</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>An <type>ne_session *</type> object is used to group a
+ sequence of HTTP requests made to a server, enabling the
+ use of a persistent connection to be used across all the
+ requests, shared authentication credentials, and more.</para>
+
+ <para>The <function>ne_session_server</function> call sets the
+ server to be used for the session. This function must be
+ called before any requests are made using the session.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Values</title>
+ <para>On success, <function>ne_session_server</function>
+ returns <returnvalue>0</returnvalue> (<errorname>NE_OK</errorname>),
+ or a non-zero value if an error occurred.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+ <para><errorname>NE_LOOKUP</errorname>: the hostname cannot be resolved.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <para>Create and initialize a session:</para>
+ <programlisting>
+
+ ne_session *sess = ne_session_create();
+ if (ne_session_server(sess, "my.host.name", 80) == NE_LOOKUP) {
+ printf("Host not found!");
+ } else {
+ /* ... use sess ... */
+ }
+ ne_session_destroy(sess);
+ </programlisting>
+ </refsect1>
+ </refentry>
+
+ <refentry>
+
+ <refmeta>
+ <refentrytitle>ne_session_proxy</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_session_proxy</refname>
+ <refname>ne_session_decide_proxy</refname>
+ <refpurpose>Proxy server settings</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcprototype>
+ <funcdef>int <function>ne_session_proxy</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ <paramdef>int <parameter>port</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_error</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>error</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_error</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_scheme</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_server_hostport</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_useragent</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>product</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_expect100</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>use_expect100</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_persist</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>use_persist</parameter></paramdef>
+ </funcprototype>
+
+ <!-- not sure if this is really the best way to represent a
+ function typedef; all the examples in-line it, which is nasty.
+ -->
+ <funcprototype>
+ <funcdef>typedef int (*ne_use_proxy)</funcdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ <paramdef>const char *<parameter>scheme</parameter></paramdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_session_decide_proxy</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>ne_use_proxy <parameter>use_proxy</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>An <type>ne_session *</type> object is a foo.</para>
+ </refsect1>
+
+ </refentry>
+
+ <refentry>
+
+ <refentryinfo><title>neon</title></refentryinfo>
+
+ <refmeta>
+ <refentrytitle>neon-config</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>neon-config</refname>
+ <refpurpose>provide information about installed copy of neon
+ library</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <cmdsynopsis>
+ <command>neon-config</command>
+ <arg choice="opt"><option>--prefix</option></arg><sbr/>
+ <group>
+ <arg><option>--cflags</option></arg>
+ <arg><option>--libs</option></arg>
+ <arg><option>--support</option> <replaceable>feature</replaceable></arg>
+ <arg><option>--help</option></arg>
+ <arg><option>--version</option></arg>
+ </group>
+ </cmdsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <command>neon-config</command> script provides
+information about an installed copy of the neon library. The
+<option>--cflags</option> and <option>--libs</option> options instruct
+how to compile and link an application against the library; the
+<option>--version</option> and <option>--support</option> options can
+help determine whether the library meets the applications
+requirements.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><option>--cflags</option></term>
+ <listitem><para>Print the flags which should be passed to
+the C compiler when compiling object files, when the object files use
+neon header files.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--libs</option></term>
+ <listitem><para>Print the flags which should be passed to
+the linker when linking an application which uses the neon
+library</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--version</option></term>
+ <listitem><para>Print the version of the library</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--prefix</option> <replaceable>dir</replaceable></term>
+ <listitem><para>If <replaceable>dir</replaceable> is given; relocate output of
+<option>--cflags</option> and <option>--libs</option> as if neon was
+installed in given prefix directory. Otherwise, print the
+installation prefix of the library.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--support</option> <replaceable>feature</replaceable></term>
+ <listitem><para>The script exits with success if
+<replaceable>feature</replaceable> is supported by the
+library.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--help</option></term>
+ <listitem><para>Print help message; includes list of known
+ features and whether they are supported or not.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>Below is a Makefile fragment which could be used to
+build an application against an installed neon library, when the
+<command>neon-config</command> script can be found in
+<envar>$PATH</envar>.</para>
+
+ <programlisting>
+CFLAGS = `neon-config --cflags`
+LIBS = `neon-config --libs`
+OBJECTS = myapp.o
+TARGET = myapp
+
+$(TARGET): $(OBJECTS)
+ $(CC) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS)
+
+myapp.o: myapp.c
+ $(CC) $(CFLAGS) -c myapp.c -o myapp.o
+</programlisting>
+
+ </refsect1>
+
+ </refentry>
+
+ </reference>
+
+&fdl;
+
+</book>
diff --git a/neon/doc/parsing-xml.txt b/neon/doc/parsing-xml.txt
new file mode 100644
index 000000000..dc3e467a5
--- /dev/null
+++ b/neon/doc/parsing-xml.txt
@@ -0,0 +1,170 @@
+
+Requirements for XML parsing in neon
+------------------------------------
+
+Before describing the interface given in neon for parsing XML, here
+are the requirements which it must satisfy:
+
+ 1. to support using either libxml or expat as the underlying parser
+ 2. to allow "independent" sections to handle parsing one XML
+ document
+ 3. to map element namespaces/names to an integer for easier
+ comparison.
+
+A description of requirement (2) is useful since it is the "hard"
+requirement, and adds most of the complexity of interface: WebDAV
+PROPFIND responses are made up of a large boilerplate XML
+
+ <multistatus><response><propstat><prop>...</prop></propstat> etc.
+
+neon should handle the parsing of these standard elements, and expose
+the meaning of the response using a convenient interface. But, within
+the <prop> elements, there may also be fragments of XML: neon can
+never know how to parse these, since they are property- and hence
+application-specific. The simplest example of this is the
+DAV:resourcetype property.
+
+So there is requirement (2) that two "independent" sections of code
+can handle the parsing of one XML document.
+
+Callback-based XML parsing
+--------------------------
+
+There are two ways of parsing XML documents commonly used:
+
+ 1. Build an in-memory tree of the document
+ 2. Use callbacks
+
+Where practical, using callbacks is more efficient than building a
+tree, so this is what neon uses. The standard interface for
+callback-based XML parsing is called SAX, so understanding the SAX
+interface is useful to understanding the XML parsing interface
+provided by neon.
+
+The SAX interface works by registering callbacks which are called *as
+the XML is parsed*. The most important callbacks are for 'start
+element' and 'end element'. For instance, if the XML document below is
+parsed by a SAX-like interface:
+
+ <hello>
+ <foobar></foobar>
+ </hello>
+
+Say we have registered callbacks "startelm" for 'start element' and
+"endelm" for 'end element'. Simplified somewhat, the callbacks will
+be called in this order, with these arguments:
+
+ 1. startelm("hello")
+ 2. startelm("foobar")
+ 3. endelm("foobar")
+ 4. endelm("hello")
+
+See the expat 'xmlparse.h' header for a more complete definition of a
+SAX-like interface.
+
+The hip_xml interface
+---------------------
+
+The hip_xml interface satisfies requirement (2) by introducing the
+"handler" concept. A handler is made up of these things:
+
+ - a set of XML elements
+ - a callback to validate an element
+ - a callback which is called when an element is opened
+ - a callback which is called when an element is closed
+ - (optionally, a callback which is called for CDATA)
+
+Registering a handler essentially says:
+
+ "If you encounter any of this set of elements, I want these
+ callbacks to be called."
+
+Handlers are kept in a STACK inside hip_xml. The first handler
+registered becomes the BASE of the stack, subsequent handlers are
+PUSHed on top.
+
+During XML parsing, the handler which is used for an XML element is
+recorded. When a new element is started, the search for a handler for
+this element begins at the handler used for the parent element, and
+carries on up the stack. For the root element, the search always
+starts at the BASE of the stack.
+
+A user's guide to hip_xml
+-------------------------
+
+The first thing to do when using hip_xml is to know what set of XML
+elements you are going to be parsing. This can usually be done by
+looking at the DTD provided for the documents you are going to be
+parsing. The DTD is also very useful in writing the 'validate'
+callback function, since it can tell you what parent/child pairs are
+valid, and which aren't.
+
+In this example, we'll parse XML documents which look like:
+
+<T:list-of-things xmlns:T="http://things.org/">
+ <T:a-thing>foo</T:a-thing>
+ <T:a-thing>bar</T:a-thing>
+</T:list-of-things>
+
+So, given the set of elements, declare the element id's and the
+element array:
+
+#define ELM_listofthings (HIP_ELM_UNUSED)
+#define ELM_a_thing (HIP_ELM_UNUSED + 1)
+
+const static struct my_elms[] = {
+ { "http://things.org/", "list-of-things", ELM_listofthings, 0 },
+ { "http://things.org/", "a-thing", ELM_a_thing, HIP_XML_CDATA },
+ { NULL }
+};
+
+This declares we know about two elements: list-of-things, and a-thing,
+and that the 'a-thing' element contains character data.
+
+The definition of the validation callback is very simple:
+
+static int validate(hip_xml_elmid parent, hip_xml_elmid child)
+{
+ /* Only allow 'list-of-things' as the root element. */
+ if (parent == HIP_ELM_root && child == ELM_listofthings ||
+ parent = ELM_listofthings && child == ELM_a_thing) {
+ return HIP_XML_VALID;
+ } else {
+ return HIP_XML_INVALID;
+ }
+}
+
+For this example, we can ignore the start-element callback, and just
+use the end-element callback:
+
+static int endelm(void *userdata, const struct hip_xml_elm *s,
+ const char *cdata)
+{
+ printf("Got a thing: %s\n", cdata);
+ return 0;
+}
+
+This endelm callback just prints the cdata which was contained in the
+"a-thing" element.
+
+Now, on to parsing. A new parser object is created for parsing each
+XML document. Creating a new parser object is as simple as:
+
+ hip_xml_parser *parser;
+
+ parser = hip_xml_create();
+
+Next register the handler, passing NULL as the start-element callback,
+and also as userdata, which we don't use here.
+
+ hip_xml_push_handler(parser, my_elms,
+ validate, NULL, endelm,
+ NULL);
+
+Finally, call hip_xml_parse, passing the chunks of XML document to the
+hip_xml as you get them. The output should be:
+
+ Got a thing: foo
+ Got a thing: bar
+
+for the XML document.
diff --git a/neon/doc/ref/alloc.xml b/neon/doc/ref/alloc.xml
new file mode 100644
index 000000000..1101a824a
--- /dev/null
+++ b/neon/doc/ref/alloc.xml
@@ -0,0 +1,88 @@
+ <refentry id="refalloc">
+
+ <refmeta>
+ <refentrytitle>ne_malloc</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_malloc">ne_malloc</refname>
+ <refname id="ne_calloc">ne_calloc</refname>
+ <refname id="ne_realloc">ne_realloc</refname>
+ <refname id="ne_strdup">ne_strdup</refname>
+ <refname id="ne_strndup">ne_strndup</refname>
+ <refname id="ne_oom_callback">ne_oom_callback</refname>
+ <refpurpose>memory allocation wrappers</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_alloc.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void *<function>ne_malloc</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void *<function>ne_calloc</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void *<function>ne_realloc</function></funcdef>
+ <paramdef>void * <parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_strdup</function></funcdef>
+ <paramdef>const char *<parameter>s</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_strndup</function></funcdef>
+ <paramdef>const char *<parameter>s</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_oom_callback</function></funcdef>
+ <paramdef>void *(<parameter>callback</parameter>)(void)</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The functions <function>ne_malloc</function>,
+<function>ne_calloc</function>, <function>ne_realloc</function>,
+<function>ne_strdup</function> and <function>ne_strdnup</function>
+provide wrappers for the equivalent functions in the standard C
+library. The wrappers provide the extra guarantee that if the C
+library equivalent returns &null; when no memory is available, an
+optional callback will be called, and the library will then call
+<function>abort</function>().</para>
+
+ <para><function>ne_oom_callback</function> registers a callback
+which will be invoked if an out of memory error is detected.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>If the operating system uses optimistic memory
+allocation, the C library memory allocation routines will not return
+&null;, so it is not possible to gracefully handle memory allocation
+failures.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/auth.xml b/neon/doc/ref/auth.xml
new file mode 100644
index 000000000..f56cea672
--- /dev/null
+++ b/neon/doc/ref/auth.xml
@@ -0,0 +1,114 @@
+ <refentry id="refauth">
+
+ <refmeta>
+ <refentrytitle>ne_set_server_auth</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_set_server_auth">ne_set_server_auth</refname>
+ <refname id="ne_set_proxy_auth">ne_set_proxy_auth</refname>
+ <refname id="ne_forget_auth">ne_forget_auth</refname>
+ <refpurpose>register authentication callbacks</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_auth.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>typedef int (*<function>ne_request_auth</function>)</funcdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ <paramdef>const char *<parameter>realm</parameter></paramdef>
+ <paramdef>int <parameter>attempt</parameter></paramdef>
+ <paramdef>char *<parameter>username</parameter></paramdef>
+ <paramdef>char *<parameter>password</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_server_auth</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>ne_request_auth <parameter>callback</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_proxy_auth</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>ne_request_auth <parameter>callback</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_forget_auth</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <type>ne_request_auth</type> function type defines a
+callback which is invoked when a server or proxy server requires user
+authentication for a particular request. The
+<parameter>realm</parameter> string is supplied by the server. <!--
+FIXME --> The <parameter>attempt</parameter> is a counter giving the
+number of times the request has been retried with different
+authentication credentials. The first time the callback is invoked
+for a particular request, <parameter>attempt</parameter> will be zero.</para>
+
+ <para>To retry the request using new authentication
+credentials, the callback should return zero, and the
+<parameter>username</parameter> and <parameter>password</parameter>
+buffers must contain <literal>NUL</literal>-terminated strings. The
+<literal>NE_ABUFSIZ</literal> constant gives the size of these
+buffers.</para>
+
+ <tip>
+ <para>If you only wish to allow the user one attempt to enter
+credentials, use the value of the <parameter>attempt</parameter>
+parameter as the return value of the callback.</para>
+ </tip>
+
+ <para>To abort the request, the callback should return a
+non-zero value; in which case the contents of the
+<parameter>username</parameter> and <parameter>password</parameter>
+buffers are ignored.</para>
+
+ <para>The <function>ne_forget_auth</function> function can be
+used to discard the cached authentication credentials.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <programlisting>
+/* Function which prompts for a line of user input: */
+extern char *prompt_for(const char *prompt);
+
+static int
+my_auth(void *userdata, const char *realm, int attempts,
+ char *username, char *password)
+{
+ strncpy(username, prompt_for("Username: "), NE_ABUFSIZ);
+ strncpy(password, prompt_for("Password: "), NE_ABUFSIZ);
+ return attempts;
+}
+
+int main(...)
+{
+ &egsess;
+
+ ne_set_server_auth(sess, my_auth, NULL);
+
+ /* ... */
+}</programlisting>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/buf.xml b/neon/doc/ref/buf.xml
new file mode 100644
index 000000000..3f8987cf5
--- /dev/null
+++ b/neon/doc/ref/buf.xml
@@ -0,0 +1,53 @@
+ <refentry id="refbuf">
+
+ <refmeta>
+ <refentrytitle>ne_buffer</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer">ne_buffer</refname>
+ <refpurpose>string buffer handling</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <programlisting>#include &lt;ne_string.h&gt;
+
+typedef struct {
+ char *data;
+ size_t used;
+ size_t length;
+} <type>ne_buffer</type>;</programlisting>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <type>ne_buffer</type> type represents an expandable
+memory buffer for holding &nul;-terminated strings. The
+<structfield>data</structfield> field points to the beginnning of the
+string, the length of which is given by the
+<structfield>used</structfield> field. The current size of memory
+allocated is given by the <structfield>length</structfield> field. It
+is not recommended that the fields of a buffer are manipulated
+directly. The <structfield>data</structfield> pointer may change when
+the buffer is modified.</para>
+
+ <para>A buffer is created using <xref
+linkend="ne_buffer_create"/> or <xref
+linkend="ne_buffer_create_sized"/>, and destroyed using <xref
+linkend="ne_buffer_destroy"/> or <xref linkend="ne_buffer_finish"/>.
+The functions <xref linkend="ne_buffer_append"/>, <xref
+linkend="ne_buffer_zappend"/> and <xref linkend="ne_buffer_concat"/> are
+used to append data to a buffer.</para>
+
+ <para>If the string referenced by the
+<structfield>data</structfield> pointer is modified directly (rather
+than using one of the functions listed above),
+<function>ne_buffer_altered</function> must be called.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/bufapp.xml b/neon/doc/ref/bufapp.xml
new file mode 100644
index 000000000..a75ce8137
--- /dev/null
+++ b/neon/doc/ref/bufapp.xml
@@ -0,0 +1,89 @@
+ <refentry id="refbufapp">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_append</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_append">ne_buffer_append</refname>
+ <refname id="ne_buffer_zappend">ne_buffer_zappend</refname>
+ <refname id="ne_buffer_concat">ne_buffer_concat</refname>
+ <refpurpose>append data to a string buffer</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_append</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>const char *<parameter>string</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_zappend</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>const char *<parameter>string</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_concat</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>const char *<parameter>str</parameter></paramdef>
+ <paramdef>...</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_buffer_append</function> and
+<function>ne_buffer_zappend</function> functions append a string to
+the end of a buffer; extending the buffer as necessary. The
+<parameter>len</parameter> passed to
+<function>ne_buffer_append</function> specifies the length of the
+string to append; there must be no &nul; terminator in the first
+<parameter>len</parameter> bytes of the string.
+<function>ne_buffer_zappend</function> must be passed a
+&nul;-terminated string.</para>
+
+ <para>The <function>ne_buffer_concat</function> function takes
+a variable-length argument list following <parameter>str</parameter>;
+each argument must be a <type>char *</type> pointer to a
+&nul;-terminated string. A &null; pointer must be given as the last
+argument to mark the end of the list. The strings (including
+<parameter>str</parameter>) are appended to the buffer in the order
+given. None of the strings passed to
+<function>ne_buffer_concat</function> are modified.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The following code will output "<literal>Hello, world.
+And goodbye.</literal>".</para>
+
+ <programlisting>ne_buffer *buf = ne_buffer_create();
+ne_buffer_zappend(buf, "Hello");
+ne_buffer_concat(buf, ", world. ", "And ", "goodbye.", NULL);
+puts(buf->data);
+ne_buffer_destroy(buf);</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_buffer"/>, <xref linkend="ne_buffer_create"/>,
+<xref linkend="ne_buffer_destroy"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/bufcr.xml b/neon/doc/ref/bufcr.xml
new file mode 100644
index 000000000..0c572a6ea
--- /dev/null
+++ b/neon/doc/ref/bufcr.xml
@@ -0,0 +1,60 @@
+ <refentry id="refbufcr">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_create">ne_buffer_create</refname>
+ <refname id="ne_buffer_create_sized">ne_buffer_ncreate</refname>
+ <refpurpose>general purpose of group of functions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_alloc.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>ne_buffer *<function>ne_buffer_create</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>ne_buffer *<function>ne_buffer_ncreate</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>ne_buffer_create</function> creates a new
+buffer object, with an implementation-defined initial size.
+<function>ne_buffer_ncreate</function> creates an
+<type>ne_buffer</type> where the minimum initial size is given in the
+<parameter>size</parameter> parameter. The buffer created will
+contain the empty string (<literal>""</literal>).</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>Both functions return a pointer to a new buffer object,
+and never &null;.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_buffer"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/bufdest.xml b/neon/doc/ref/bufdest.xml
new file mode 100644
index 000000000..5dc4dfc4c
--- /dev/null
+++ b/neon/doc/ref/bufdest.xml
@@ -0,0 +1,81 @@
+ <refentry id="refbufdest">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_destroy</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_destroy">ne_buffer_destroy</refname>
+ <refname id="ne_buffer_finish">ne_buffer_finish</refname>
+ <refpurpose>destroy a buffer object</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_destroy</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_buffer_finish</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>ne_buffer_destroy</function> frees all memory
+associated with the buffer. <function>ne_buffer_finish</function>
+frees the buffer structure, but not the actual string stored in the
+buffer, which is returned and must be <function>free</function>()d by
+the caller.</para>
+
+ <para>Any use of the buffer object after calling either of these
+functions gives undefined behaviour.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>ne_buffer_finish</function> returns the
+<function>malloc</function>-allocated string stored in the buffer.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>An example use of <function>ne_buffer_finish</function>;
+the <function>duplicate</function> function returns a string made up of
+<parameter>n</parameter> copies of <parameter>str</parameter>:</para>
+
+ <programlisting>static char *duplicate(int n, const char *str)
+{
+ ne_buffer *buf = ne_buffer_create();
+ while (n--) {
+ ne_buffer_zappend(buf, str);
+ }
+ return ne_buffer_finish(buf);
+}</programlisting>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_buffer"/>, <xref linkend="ne_buffer_create"/>,
+<xref linkend="ne_buffer_zappend"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/bufutil.xml b/neon/doc/ref/bufutil.xml
new file mode 100644
index 000000000..3f3f3c5dc
--- /dev/null
+++ b/neon/doc/ref/bufutil.xml
@@ -0,0 +1,62 @@
+ <refentry id="refbufutil">
+
+ <refmeta>
+ <refentrytitle>ne_buffer_clear</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_buffer_clear">ne_buffer_clear</refname>
+ <refname id="ne_buffer_grow">ne_buffer_grow</refname>
+ <refname id="ne_buffer_altered">ne_buffer_altered</refname>
+ <refpurpose>general purpose of group of functions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_clear</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_altered</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_buffer_grow</function></funcdef>
+ <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_buffer_clear</function> function sets
+the string stored in <parameter>buf</parameter> to be the empty string
+(<literal>""</literal>).</para>
+
+ <para>The <function>ne_buffer_altered</function> function must
+be used after the string stored in the buffer
+<parameter>buf</parameter> is modified by directly rather than using
+<xref linkend="ne_buffer_append"/>, <xref linkend="ne_buffer_zappend"/>
+or <xref linkend="ne_buffer_concat"/>.</para>
+
+ <para>The <function>ne_buffer_grow</function> function
+ensures that at least <parameter>size</parameter> bytes are allocated
+for the string; this can be used if a large amount of data is going to
+be appended to the buffer and may result in more efficient memory
+allocation.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/err.xml b/neon/doc/ref/err.xml
new file mode 100644
index 000000000..50551b5ab
--- /dev/null
+++ b/neon/doc/ref/err.xml
@@ -0,0 +1,66 @@
+ <refentry id="referr">
+
+ <refmeta>
+ <refentrytitle>ne_get_error</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_get_error">ne_get_error</refname>
+ <refname id="ne_set_error">ne_set_error</refname>
+ <refpurpose>error handling for HTTP sessions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_error</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_error</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>...</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The session error string is used to store any
+human-readable error information associated with any errors which
+occur whilst using the HTTP session.</para>
+
+ <para>The <function>ne_get_error</function> function returns the current
+session error string. This string persists only until it is changed by a
+subsequent operation on the session.</para>
+
+ <para>The <function>ne_set_error</function> function can be
+used to set a new session error string, using a
+<function>printf</function>-style format string interface.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <para>Retrieve the current error string:</para>
+ <programlisting>&egsess;
+...
+printf("Error was: %s\n", ne_get_error(sess));</programlisting>
+
+ <para>Set a new error string:</para>
+ <programlisting>&egsess;
+...
+ne_set_error(sess, "Response missing header %s", "somestring");</programlisting>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/getst.xml b/neon/doc/ref/getst.xml
new file mode 100644
index 000000000..9d44277a7
--- /dev/null
+++ b/neon/doc/ref/getst.xml
@@ -0,0 +1,60 @@
+ <refentry id="refgetst">
+
+ <refmeta>
+ <refentrytitle>ne_get_status</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_get_status">ne_get_status</refname>
+ <refpurpose>retrieve HTTP response status for request</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>const ne_status *<function>ne_get_status</function></funcdef>
+ <paramdef>const ne_request *<parameter>request</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_get_status</function> function returns
+a pointer to the HTTP status object giving the result of a request
+(once dispatched). The object persists until the associated request
+is destroyed.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_get_status"/>, <xref
+ linkend="ne_request_create"/></para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>Display the response status code of applying the
+<literal>HEAD</literal> method to some resource.</para>
+
+ <programlisting>ne_request *req = ne_request_create(sess, "HEAD", "/foo/bar");
+if (ne_request_dispatch(req))
+ /* handle errors... */
+else
+ printf("Response status code was %d\n", ne_get_status(req)->code);
+ne_request_destroy(req);</programlisting>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/init.xml b/neon/doc/ref/init.xml
new file mode 100644
index 000000000..32dcda5ba
--- /dev/null
+++ b/neon/doc/ref/init.xml
@@ -0,0 +1,55 @@
+<refentry id="refsockinit">
+
+ <refmeta>
+ <refentrytitle>ne_sock_init</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_sock_init">ne_sock_init</refname>
+ <refpurpose>perform library initialization</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_socket.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>ne_sock_init</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>In some platforms and configurations, &neon; may be using
+ some socket or SSL libraries which require global initialization
+ before use. To perform this initialization, the
+ <function>ne_sock_init</function> function must be called once
+ before any other library functions are used.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>ne_sock_init</function> returns zero on success,
+ or non-zero on error. If an error occurs, no further use of the
+ &neon; library should be attempted.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="refneon"/></para>
+ </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/ref/neon.xml b/neon/doc/ref/neon.xml
new file mode 100644
index 000000000..219f682c0
--- /dev/null
+++ b/neon/doc/ref/neon.xml
@@ -0,0 +1,75 @@
+<refentry id="refneon">
+
+ <refmeta>
+ <refentrytitle>neon</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>neon</refname>
+ <refpurpose>neon API conventions</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>neon is an HTTP and WebDAV client library. The major
+ abstractions exposed are of an HTTP <emphasis>session</emphasis>,
+ created by <xref linkend="ne_session_create"/>; and an HTTP
+ <emphasis>request</emphasis>, created by <xref
+ linkend="ne_request_create"/>. HTTP authentication is handled
+ transparently for server and proxy servers, see <xref
+ linkend="ne_set_server_auth"/>; complete SSL/TLS support is also
+ included, see <xref linkend="ne_ssl_set_verify"/>.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Conventions</title>
+
+ <para>Some conventions are used throughout the neon API, to
+ provide a consistent and simple interface; these are documented
+ below.</para>
+
+ </refsect1>
+
+ <refsect2>
+ <title>URI paths, WebDAV metadata</title>
+
+ <para>The path strings passed to any function must be
+ <emphasis>URI-encoded</emphasis> by the application: neon never
+ performs any URI encoding or decoding automatically. WebDAV
+ property names and values must be used un UTF-8.</para>
+
+ </refsect2>
+
+ <refsect2>
+ <title>Memory handling</title>
+
+ <para>neon does not attempt to cope gracefully with an
+ out-of-memory situation; instead, by default,
+ <function>abort</function> is called to terminate the application.
+ Optionally an application-provided function be called before
+ abort; see <xref linkend="ne_oom_callback"/>.</para>
+
+ </refsect2>
+
+ <refsect2>
+ <title>Callbacks and userdata</title>
+
+ <para>Whenever a callback is registered, a
+ <literal>userdata</literal> variable is also used to allow the
+ application to associate a context with the callback. The
+ userdata is of type <type>void *</type>, allowing any pointer to
+ be used.</para>
+
+ </refsect2>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="refsess"/>, <xref linkend="refalloc"/></para>
+ </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/ref/opts.xml b/neon/doc/ref/opts.xml
new file mode 100644
index 000000000..fe8f368a5
--- /dev/null
+++ b/neon/doc/ref/opts.xml
@@ -0,0 +1,126 @@
+ <refentry id="refopts">
+
+ <refmeta>
+ <refentrytitle>ne_set_useragent</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_set_useragent">ne_set_useragent</refname>
+ <refname id="ne_set_persist">ne_set_persist</refname>
+ <refname id="ne_set_read_timeout">ne_set_read_timeout</refname>
+ <refname id="ne_set_expect100">ne_set_expect100</refname>
+ <refpurpose>common settings for HTTP sessions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_useragent</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>product</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_persist</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_read_timeout</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>timeout</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_expect100</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ </funcprototype>
+
+<!--
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_scheme</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_get_server_hostport</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+-->
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <!-- TODO: intro para? -->
+
+ <para>The <literal>User-Agent</literal> request header is used
+to identify the software which generated the request for statistical
+or debugging purposes. neon does not send a
+<literal>User-Agent</literal> header unless a call is made to the
+<function>ne_set_useragent</function>.
+<function>ne_set_useragent</function> must be passed a product string
+conforming to RFC2616's product token grammar; of the form
+<literal>"Product/Version"</literal>.</para>
+
+ <para>By default neon will use a persistent connection
+whenever possible. For specific applications, or for debugging
+purposes, it is sometimes useful to disable persistent connections.
+The <function>ne_set_persist</function> function will disable
+persistent connections if passed a <parameter>flag</parameter>
+parameter of <literal>0</literal>, and will enable them
+otherwise.</para>
+
+ <para>When neon reads from a socket, by default the read
+operation will time out after 60 seconds, and the request will fail
+giving an <errorcode>NE_TIMEOUT</errorcode> error. To configure this
+timeout interval, call <function>ne_set_read_timeout</function> giving
+the desired number of seconds as the <parameter>timeout</parameter>
+parameter.</para>
+
+ <para>An extension introduced in the HTTP/1.1 specification
+was the use of the <literal>Expect: 100-continue</literal> header.
+This header allows an HTTP client to be informed of the expected
+response status before the request message body is sent: a useful
+optimisation for situations where a large message body is to be sent.
+The <function>ne_set_expect100</function> function can be used to
+enable this feature by passing the <parameter>flag</parameter>
+parameter as any non-zero integer.</para>
+
+<warning><para>Unfortunately, if this header is sent to a server which
+is not fully compliant with the HTTP/1.1 specification, a deadlock
+occurs resulting in a temporarily "hung" connection. neon will
+recover gracefully from this situation, but only after a 15 second
+timeout. It is highly recommended that this option is not enabled
+unless it is known that the server in use correctly implements
+<literal>Expect: 100-continue</literal> support.</para></warning>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <para>Set a user-agent string:</para>
+ <programlisting>&egsess;
+ne_set_useragent(sess, "MyApplication/2.1");</programlisting>
+
+ <para>Disable use of persistent connections:</para>
+ <programlisting>ne_session *sess = ne_session_create(...);
+ne_set_persist(sess, 0);</programlisting>
+
+ <para>Set a 30 second read timeout:</para>
+ <programlisting>&egsess;
+ne_set_read_timeout(sess, 30);</programlisting>
+
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/req.xml b/neon/doc/ref/req.xml
new file mode 100644
index 000000000..85394cb53
--- /dev/null
+++ b/neon/doc/ref/req.xml
@@ -0,0 +1,170 @@
+ <refentry id="refreq">
+
+ <refmeta>
+ <refentrytitle>ne_request_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_request_create">ne_request_create</refname>
+ <refname id="ne_request_dispatch">ne_request_dispatch</refname>
+ <refname id="ne_request_destroy">ne_request_destroy</refname>
+ <refpurpose>low-level HTTP request handling</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>ne_request *<function>ne_request_create</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>method</parameter></paramdef>
+ <paramdef>const char *<parameter>path</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>ne_request_dispatch</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_request_destroy</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>An HTTP request, represented by the
+<type>ne_request</type> type, specifies that some operation is to be
+performed on some resource. The
+<function>ne_request_create</function> function creates a request
+object, specifying the operation in the <parameter>method</parameter>
+parameter. The location of the resource is determined by the server in
+use for the session given by the <parameter>sess</parameter> parameter,
+combined with the <parameter>path</parameter> parameter. The
+<parameter>path</parameter> string used must conform to the
+<literal>abs_path</literal> definition given in RFC2396.</para>
+
+ <para>To dispatch a request, and process the response, the
+<function>ne_request_dispatch</function> function can be used. An
+alternative is to use the (more complex, but more flexible)
+combination of the <function>ne_begin_request</function>,
+<function>ne_end_request</function>, and
+<function>ne_read_response_block</function> functions; see <xref
+linkend="ne_begin_request"/>.</para>
+
+ <para>To add extra headers in the request, the functions <xref
+linkend="ne_add_request_header"/> and <xref
+linkend="ne_print_request_header"/> can be used. To include a message
+body with the request, one of the functions <xref
+linkend="ne_set_request_body_buffer"/>, <xref
+linkend="ne_set_request_body_fd"/>, or <xref
+linkend="ne_set_request_body_provider"/> can be used.</para>
+
+ <para>The return value of
+<function>ne_request_dispatch</function> indicates merely whether the
+request was sent and the response read successfully. To discover the
+result of the operation, use <function><xref linkend="ne_get_status"/></function>, along with
+any processing of the response headers and message body.</para>
+
+ <para>A request can only be dispatched once: calling
+<function>ne_request_dispatch</function> more than once on a single
+<type>ne_request</type> object produces undefined behaviour. Once all
+processing associated a the request object has been completed, use the
+<function>ne_request_destroy</function> function to destroy the
+resources associated with it. Any subsequent use of the request
+object produces undefined behaviour.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>The <function>ne_request_create</function> function
+returns a pointer to a request object (and never &null;).</para>
+
+ <para>The <function>ne_request_dispatch</function> function
+returns zero if the request was dispatched successfully, and a
+non-zero error code otherwise.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para><!-- TODO: abs_path description --></para>
+
+ </refsect1>
+
+
+ <refsect1>
+ <title>Errors</title>
+
+ <variablelist>
+ <varlistentry><term><errorcode>NE_ERROR</errorcode></term>
+ <listitem>
+ <para>Request failed (see session error string)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_LOOKUP</errorcode></term>
+ <listitem>
+ <para>The DNS lookup for the server (or proxy server) failed.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_AUTH</errorcode></term>
+ <listitem>
+ <para>Authentication failed on the server.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_PROXYAUTH</errorcode></term>
+ <listitem>
+ <para>Authentication failed on the proxy server.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_CONNECT</errorcode></term>
+ <listitem>
+ <para>A connection to the server could not be established.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><errorcode>NE_TIMEOUT</errorcode></term>
+ <listitem>
+ <para>A timeout occurred while waiting for the server to respond.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>An example of applying a <literal>MKCOL</literal>
+ operation to the resource at the location
+ <literal>http://www.example.com/foo/bar/</literal>:</para>
+
+ <programlisting>ne_session *sess = ne_session_create("http", "www.example.com", 80);
+ne_request *req = ne_request_create(sess, "MKCOL", "/foo/bar/");
+if (ne_request_dispatch(req)) {
+ printf("Request failed: %s\n", ne_get_error(sess));
+}
+ne_request_destroy(req);</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_get_error"/>, <xref
+linkend="ne_set_error"/>, <xref linkend="ne_get_status"/>, <xref
+linkend="ne_add_request_header"/>, <xref
+linkend="ne_set_request_body_buffer"/>.</para>
+
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/reqbody.xml b/neon/doc/ref/reqbody.xml
new file mode 100644
index 000000000..e2c8eb3c4
--- /dev/null
+++ b/neon/doc/ref/reqbody.xml
@@ -0,0 +1,69 @@
+ <refentry id="refreqbody">
+
+ <refmeta>
+ <refentrytitle>ne_set_request_body_buffer</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_set_request_body_buffer">ne_set_request_body_buffer</refname>
+ <refname id="ne_set_request_body_fd">ne_set_request_body_fd</refname>
+ <refpurpose>include a message body with a request</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_request_body_buffer</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ <paramdef>const char *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>count</parameter></paramdef>
+ </funcprototype>
+
+ <!-- this is a better interface for set_request_body_fd:
+ <funcprototype>
+ <funcdef>int <function>ne_set_request_body_fd</function></funcdef>
+ <paramdef>ne_request *<parameter>req</parameter></paramdef>
+ <paramdef>int <parameter>fd</parameter></paramdef>
+ <paramdef>off_t <parameter>begin</parameter></paramdef>
+ <paramdef>size_t <parameter>count</parameter></paramdef>
+ </funcprototype>
+ -->
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_set_request_body_buffer</function>
+function specifies that a message body should be included with the
+body, which is stored in the <parameter>count</parameter> bytes buffer
+<parameter>buf</parameter>.</para>
+
+<!--
+ <para>The <function>ne_set_request_body_fd</function> function
+can be used to include a message body with a request which is read
+from a file descriptor. The body is read from the file descriptor
+<parameter>fd</parameter>, which must be a associated with a seekable
+file (not a pipe, socket, or FIFO). <parameter>count</parameter>
+bytes are read, beginning at offset <parameter>begin</parameter>
+(passing <parameter>begin</parameter> as zero means the body is read
+from the beginning of the file).</para>
+
+-->
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_request_create"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/reqhdr.xml b/neon/doc/ref/reqhdr.xml
new file mode 100644
index 000000000..4d811b135
--- /dev/null
+++ b/neon/doc/ref/reqhdr.xml
@@ -0,0 +1,63 @@
+ <refentry id="refreqhdr">
+
+ <refmeta>
+ <refentrytitle>ne_add_request_header</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_add_request_header">ne_add_request_header</refname>
+ <refname id="ne_print_request_header">ne_print_request_header</refname>
+ <refpurpose>add headers to a request</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_add_request_header</function></funcdef>
+ <paramdef>ne_request *<parameter>request</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>const char *<parameter>value</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_print_request_header</function></funcdef>
+ <paramdef>ne_request *<parameter>request</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>...</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The functions <function>ne_add_request_header</function>
+and <function>ne_print_request_header</function> can be used to add
+headers to a request, before it is sent.</para>
+
+ <para><function>ne_add_request_header</function> simply adds a
+header of given <parameter>name</parameter>, with given
+<parameter>value</parameter>.</para>
+
+ <para><function>ne_print_request_header</function> adds a
+header of given <parameter>name</parameter>, taking the value from the
+<function>printf</function>-like <parameter>format</parameter> string
+parameter and subsequent variable-length argument list.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_request_create"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/resolve.xml b/neon/doc/ref/resolve.xml
new file mode 100644
index 000000000..9314880b6
--- /dev/null
+++ b/neon/doc/ref/resolve.xml
@@ -0,0 +1,160 @@
+<refentry id="refresolve">
+
+ <refmeta>
+ <refentrytitle>ne_addr_resolve</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_addr_resolve">ne_addr_resolve</refname>
+ <refname id="ne_addr_result">ne_addr_result</refname>
+ <refname id="ne_addr_first">ne_addr_first</refname>
+ <refname id="ne_addr_next">ne_addr_next</refname>
+ <refname id="ne_addr_print">ne_addr_print</refname>
+ <refname id="ne_addr_error">ne_addr_error</refname>
+ <refname id="ne_addr_destroy">ne_addr_destroy</refname>
+ <refpurpose>functions to resolve hostnames to addresses</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_socket.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>ne_sock_addr *<function>ne_addr_resolve</function></funcdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>ne_addr_result</function></funcdef>
+ <paramdef>const ne_sock_addr *<parameter>addr</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>ne_inet_addr *<function>ne_addr_first</function></funcdef>
+ <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>ne_inet_addr *<function>ne_addr_next</function></funcdef>
+ <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_addr_print</function></funcdef>
+ <paramdef>const ne_inet_addr *<parameter>iaddr</parameter></paramdef>
+ <paramdef>char *<parameter>buffer</parameter></paramdef>
+ <paramdef>size_t <parameter>bufsiz</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_addr_error</function></funcdef>
+ <paramdef>const ne_sock_addr *<parameter>addr</parameter></paramdef>
+ <paramdef>char *<parameter>buffer</parameter></paramdef>
+ <paramdef>size_t <parameter>bufsiz</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_addr_destroy</function></funcdef>
+ <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_addr_resolve</function> function resolves
+ the given <parameter>hostname</parameter>, returning an
+ <type>ne_sock_addr</type> object representing the address (or
+ addresses) associated with the hostname. The
+ <parameter>flags</parameter> parameter is currently unused, and
+ should be passed as 0.</para>
+
+ <para>The <parameter>hostname</parameter> passed to
+ <function>ne_addr_resolve</function> can be a DNS hostname
+ (e.g. <literal>www.example.com</literal>) or an IPv4 dotted quad
+ (e.g. <literal>192.0.34.72</literal>); or, on systems which
+ support IPv6, an IPv6 hex address, which may be enclosed in
+ brackets, e.g. <literal>[::1]</literal>.</para>
+
+ <para>To determine whether the hostname was successfully resolved,
+ the <function>ne_addr_result</function> function is used, which
+ returns non-zero if an error occurred. If an error did occur, the
+ <function>ne_addr_error</function> function can be used, which
+ will copy the error string into a given
+ <parameter>buffer</parameter> (of size
+ <parameter>bufsiz</parameter>.</para>
+
+ <para>The functions <function>ne_addr_first</function> and
+ <function>ne_addr_next</function> are used to retrieve the
+ Internet addresses associated with an address object which has
+ been successfully resolved. <function>ne_addr_first</function>
+ returns the first address; <function>ne_addr_next</function>
+ returns the next address after the most recent call to
+ <function>ne_addr_next</function> or
+ <function>ne_addr_first</function>, or &null; if there are no more
+ addresses. The <type>ne_inet_addr</type> pointer returned by
+ these functions can be passed to
+ <function>ne_sock_connect</function> to connect a socket.</para>
+
+ <para>To return the string representation of a particular network
+ address, the <function>ne_addr_print</function> function can be
+ used.</para>
+
+ <para>After the address object has been used, it should be
+ destroyed using <function>ne_addr_destroy</function>.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>ne_addr_resolve</function> returns a pointer to an
+ address object, and never &null;.
+ <function>ne_addr_error</function> and
+ <function>ne_addr_print</function> both return the
+ <parameter>buffer</parameter> parameter .</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The code below prints out the set of addresses associated
+ with the hostname <literal>www.google.com</literal>.</para>
+
+ <programlisting>ne_sock_addr *addr;
+char buf[256];
+
+addr = ne_addr_resolve("www.google.com", 0);
+if (ne_addr_result(addr)) {
+ printf("Could not resolve www.google.com: %s\n",
+ ne_addr_error(addr, buf, sizeof buf));
+} else {
+ ne_inet_addr *ia;
+ printf("www.google.com:");
+ for (ia = ne_addr_first(addr); ia != NULL; ia = ne_addr_next(addr)) {
+ printf(" %s", ne_addr_print(ia, buf, sizeof buf));
+ }
+ putchar('\n');
+}
+ne_addr_destroy(addr);
+</programlisting>
+ </refsect1>
+
+<!--
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_sock_connect"/></para>
+ </refsect1>
+-->
+
+</refentry>
+
diff --git a/neon/doc/ref/sess.xml b/neon/doc/ref/sess.xml
new file mode 100644
index 000000000..b3b0047fc
--- /dev/null
+++ b/neon/doc/ref/sess.xml
@@ -0,0 +1,111 @@
+ <refentry id="refsess">
+
+ <refentryinfo>
+ <title>neon</title>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>ne_session_create</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_session_create">ne_session_create</refname>
+ <refname id="ne_close_connection">ne_close_connection</refname>
+ <refname id="ne_session_proxy">ne_session_proxy</refname>
+ <refname id="ne_session_destroy">ne_session_destroy</refname>
+ <refpurpose>set up HTTP sessions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+ <funcprototype>
+ <funcdef>ne_session *<function>ne_session_create</function></funcdef>
+ <paramdef>const char *<parameter>scheme</parameter></paramdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ <paramdef>int <parameter>port</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_session_proxy</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>hostname</parameter></paramdef>
+ <paramdef>int <parameter>port</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_close_connection</function></funcdef>
+ <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_session_destroy</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>An <type>ne_session</type> object represents an HTTP
+session, which groups a set of HTTP requests made to a certain server.
+Any requests made using the session can use a persistent connection,
+and share cached authentication credentials and other common
+attributes.</para>
+
+ <para>A new HTTP session is created using
+<function>ne_session_create</function>, giving the
+<parameter>hostname</parameter> and <parameter>port</parameter> of the
+server to use, along with the <parameter>scheme</parameter> used to
+contact the server (usually <literal>"http"</literal>).</para>
+
+ <para>To enable SSL/TLS for the session, pass the string
+<literal>"https"</literal> as the <parameter>scheme</parameter>
+parameter, and either register a certificate verification function
+(see <xref linkend="ne_ssl_set_verify"/>) or load the appropriate CA
+certificate (see <xref linkend="ne_ssl_load_ca"/>, <xref
+linkend="ne_ssl_load_default_ca"/>).</para>
+
+ <para>If an HTTP proxy server should be used for the session,
+<function>ne_session_proxy</function> must be called giving the
+hostname and port on which to contact the proxy.</para>
+
+ <para>If it is known that the session will not be used for a
+significant period of time, <function>ne_close_connection</function>
+can be called to close the connection, if one remains open. Use of
+this function is entirely optional, but it must not be called if there
+is a request active using the session.</para>
+
+ <para>Once a session has been completed,
+<function>ne_session_destroy</function> must be called to destroy the
+resources associated with the session. Any subsequent use of the
+session pointer produces undefined behaviour.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return Values</title>
+ <para><function>ne_session_create</function> will return
+ a pointer to a new session object (and never &null;).</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <para>Create and destroy a session:</para>
+ <programlisting>ne_session *sess;
+sess = ne_session_create("http", "host.example.com", 80);
+/* ... use sess ... */
+ne_session_destroy(sess);
+</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><xref linkend="ne_ssl_set_verify"/>, <xref linkend="ne_ssl_load_ca"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/shave.xml b/neon/doc/ref/shave.xml
new file mode 100644
index 000000000..56881c14d
--- /dev/null
+++ b/neon/doc/ref/shave.xml
@@ -0,0 +1,51 @@
+ <refentry id="refshave">
+
+ <refmeta>
+ <refentrytitle>ne_shave</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_shave</refname>
+ <refpurpose>whitespace trimmer</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_shave</function></funcdef>
+ <paramdef>char *<parameter>str</parameter></paramdef>
+ <paramdef>const char *<parameter>whitespace</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>ne_shave</function> returns a portion of
+<parameter>str</parameter> with any leading or trailing characters in
+the <parameter>whitespace</parameter> array removed.
+<parameter>str</parameter> may be modified.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The following code segment will output
+ <literal>"fish"</literal>:</para>
+
+ <programlisting>char s[] = ".!.fish!.!";
+puts(ne_shave(s, ".!"));</programlisting>
+
+ </refsect1>
+
+ </refentry>
+
diff --git a/neon/doc/ref/sslca.xml b/neon/doc/ref/sslca.xml
new file mode 100644
index 000000000..cbac071d0
--- /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_ssl_get_error"/>, <xref
+ linkend="ne_ssl_set_verify"/></para> </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/sslcert.xml b/neon/doc/ref/sslcert.xml
new file mode 100644
index 000000000..9696a4506
--- /dev/null
+++ b/neon/doc/ref/sslcert.xml
@@ -0,0 +1,66 @@
+ <refentry id="refsslcert">
+
+ <refmeta>
+ <refentrytitle>ne_ssl_certificate</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_ssl_certificate">ne_ssl_certificate</refname>
+ <refname id="ne_ssl_dname">ne_ssl_dname</refname>
+ <refpurpose>structures representing SSL certificates</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <programlisting>#include &lt;ne_session.h&gt;
+
+/* A simplified X.509 distinguished name. */
+typedef struct {
+ const char *country, *state, *locality, *organization;
+ const char *organizationalUnit;
+ const char *commonName;
+} <type>ne_ssl_dname</type>;
+
+/* A simplified SSL certificate. */
+typedef struct {
+ const <type>ne_ssl_dname</type> *subject, *issuer;
+ const char *from, *until;
+} <type>ne_ssl_certificate</type>;
+
+</programlisting>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <type>ne_ssl_dname</type> structure is used to
+represent a simplified X.509 distinguished name, as used in SSL
+certificates; a distinguished name is used to uniquely identify an
+entity. Along with the fields giving the geographical and
+organizational location of the entity, the
+<structfield>commonName</structfield> field will be assigned the DNS
+hostname of the entity. The
+<function>ne_ssl_readable_dname</function> function can be used to
+create a single-line string out of an <type>ne_ssl_dname</type>
+structure.</para>
+
+ <para>The <type>ne_ssl_certificate</type> structure is used to
+represent a simplified SSL certificate; containing the distinguished
+names of the <firstterm>issuer</firstterm> and
+<firstterm>subject</firstterm> of the certificate. The issuer is the
+entity which has digitally signed the certificate to guarantee its
+authenticity; the subject is the owner of the certificate. A
+certificate is only valid for a certain period of time: the
+<structfield>from</structfield> and <structfield>until</structfield>
+contain strings giving the validity period.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_set_verify"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/ssldname.xml b/neon/doc/ref/ssldname.xml
new file mode 100644
index 000000000..70df21ae4
--- /dev/null
+++ b/neon/doc/ref/ssldname.xml
@@ -0,0 +1,53 @@
+
+ <refentry id="refssldname">
+
+ <refmeta>
+ <refentrytitle>ne_ssl_readable_dname</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_ssl_readable_dname">ne_ssl_readable_dname</refname> <refpurpose>create a
+ single-line readable string from a distinguised
+ name</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_ssl_readable_dname</function></funcdef>
+ <paramdef>const ne_ssl_dname *<parameter>dname</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_ssl_readable_dname</function> function
+creates a single-line, human-readable string out of an
+<type>ne_ssl_dname</type> object. The returned string is
+<function>malloc</function>()-allocated, and must be
+<function>free</function>()d by the caller.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para><function>malloc</function>-allocated string.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_ssl_certificate"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/sslvfy.xml b/neon/doc/ref/sslvfy.xml
new file mode 100644
index 000000000..ade5028bb
--- /dev/null
+++ b/neon/doc/ref/sslvfy.xml
@@ -0,0 +1,140 @@
+ <refentry id="refsslvfy">
+
+ <refmeta>
+ <refentrytitle>ne_ssl_set_verify</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_ssl_set_verify">ne_ssl_set_verify</refname>
+ <refpurpose>register an SSL certificate verification callback</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+ <!-- hard to put data type declarations here -->
+
+ <funcprototype>
+ <funcdef>typedef int (*<function>ne_ssl_verify_fn</function>)</funcdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ <paramdef>int <parameter>failures</parameter></paramdef>
+ <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>ne_ssl_set_verify</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>ne_ssl_verify_fn <parameter>verify_fn</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>To enable manual SSL certificate verification, a
+callback can be registered using
+<function>ne_ssl_set_verify</function>. If such a callback is not
+registered, when a connection is established to an SSL server which
+does not present a certificate signed by a trusted CA (see <xref
+linkend="ne_ssl_load_ca"/>), or if the certificate presented is invalid in
+some way, the connection will fail.</para>
+
+ <para>When the callback is invoked, the
+<parameter>failures</parameter> parameter gives a bitmask indicating
+in what way the automatic certificate verification failed. The value
+is equal to the bit-wise OR of one or more of the following
+constants (and is guaranteed to be non-zero):</para>
+
+ <variablelist>
+ <varlistentry><term><filename>NE_SSL_NOTYETVALID</filename></term>
+ <listitem>
+ <para>The certificate is not yet valid.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><filename>NE_SSL_EXPIRED</filename></term>
+ <listitem>
+ <para>The certificate has expired.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><filename>NE_SSL_CNMISMATCH</filename></term>
+ <listitem>
+ <para>The hostname used for the session does not match
+the hostname to which the certificate was issued: this could mean that
+the connection has been intercepted.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><filename>NE_SSL_UNKNOWNCA</filename></term>
+ <listitem>
+ <para>The Certificate Authority which signed the certificate
+is not trusted.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>The <parameter>cert</parameter> parameter passed to the
+callback describes the certificate which was presented by the server,
+see <xref linkend="ne_ssl_certificate"/> for more details. The certificate
+object given is only valid until the callback returns.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>The verification callback must return zero to indicate
+that the certificate should be trusted; and non-zero otherwise (in
+which case, the connection will fail).</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>Manual certificate verification:</para>
+ <programlisting>
+static int
+my_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
+{
+ /* this leaks return values of ne_ssl_readable_dname for simplicity! */
+ printf("Issuer: %s\n", ne_ssl_readable_dname(cert->issuer);
+ printf("Subject: %s\n", ne_ssl_readable_dname(cert->subject);
+ if (failures &amp; NE_SSL_CNMISMATCH) {
+ printf("Server certificate was issued to `%s'; "
+ "connection may have been intercepted!\n",
+ cert->subject->commonName);
+ }
+ if (failures &amp; NE_SSL_EXPIRED) {
+ printf("Server certificate expired on %s!", cert->until);
+ }
+ /* ... check for other failures ... */
+ if (prompt_user())
+ return 1; /* fail verification */
+ else
+ return 0; /* trust certificate */
+}
+
+int
+main(...)
+{
+ ne_session *sess = ne_session_create("https", "some.host.name", 443);
+ ne_ssl_set_verify(sess, my_verify, NULL);
+ ...
+}</programlisting>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para><xref linkend="ne_ssl_certificate"/>, <xref linkend="ne_ssl_load_ca"/>,
+ <xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_readable_dname"/></para>
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/status.xml b/neon/doc/ref/status.xml
new file mode 100644
index 000000000..0290ac250
--- /dev/null
+++ b/neon/doc/ref/status.xml
@@ -0,0 +1,75 @@
+ <refentry id="refstatus">
+
+ <refmeta>
+ <refentrytitle>ne_status</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname id="ne_status">ne_status</refname>
+ <refpurpose>HTTP status structure</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <programlisting>#include &lt;ne_utils.h&gt;
+
+typedef struct {
+ int major_version, minor_version;
+ int code, klass;
+ const char *reason_phrase;
+} <type>ne_status</type>;</programlisting>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>An <type>ne_status</type> type represents an HTTP
+response status; used in response messages giving a result of request.
+The <structfield>major_version</structfield> and
+<structfield>minor_version</structfield> fields give the HTTP version
+supported by the server issuing the response. The
+<structfield>code</structfield> field gives the status code of the
+result (lying between 100 and 999 inclusive), and the
+<structfield>klass</structfield> field gives the class, which is equal
+to the most significant digit of the status.</para>
+
+ <para>There are five classes of HTTP status code defined by
+ RFC2616:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>1xx</literal></term>
+ <listitem><para>Informational response.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>2xx</literal></term>
+ <listitem><para>Success: the operation was successful</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>3xx</literal></term>
+ <listitem><para>Redirection</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>4xx</literal></term> <listitem><para>Client
+ error: the request made was incorrect in some
+ manner.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>5xx</literal></term>
+ <listitem><para>Server error</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1> <title>See also</title> <para><xref
+linkend="ne_get_status"/>, <xref
+linkend="ne_parse_status_line"/></para> </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/tok.xml b/neon/doc/ref/tok.xml
new file mode 100644
index 000000000..2e6211f97
--- /dev/null
+++ b/neon/doc/ref/tok.xml
@@ -0,0 +1,76 @@
+ <refentry id="reftok">
+
+ <refmeta>
+ <refentrytitle>ne_token</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_token</refname>
+ <refname>ne_qtoken</refname>
+ <refpurpose>string tokenizers</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_token</function></funcdef>
+ <paramdef>char **<parameter>str</parameter></paramdef>
+ <paramdef>char <parameter>sep</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>char *<function>ne_qtoken</function></funcdef>
+ <paramdef>char **<parameter>str</parameter></paramdef>
+ <paramdef>char <parameter>sep</parameter></paramdef>
+ <paramdef>const char *<parameter>quotes</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <!-- FIXME: italics on tokenize -->
+
+ <para><function>ne_token</function> and
+<function>ne_qtoken</function> tokenize the string at the location
+stored in the pointer <parameter>str</parameter>. Each time the
+function is called, it returns the next token, and modifies the
+<parameter>str</parameter> pointer to point to the remainer of the
+string, or &null; if there are no more tokens in the string. A token
+is delimited by the separator character <parameter>sep</parameter>; if
+<function>ne_qtoken</function> is used any quoted segments of the
+string are skipped when searching for a separator. A quoted segment
+is enclosed in a pair of one of the characters given in the
+<parameter>quotes</parameter> string.</para>
+
+ <para>The string being tokenized is modified each time
+the tokenizing function is called; replacing the next separator
+character with a &nul; terminator.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>The following function prints out each token in a
+comma-separated string <parameter>list</parameter>, which is
+modified in-place:</para>
+
+ <programlisting>static void splitter(char *list)
+{
+ do {
+ printf("Token: %s\n", ne_token(&amp;list, ','));
+ while (list);
+}</programlisting>
+
+ </refsect1>
+
+ </refentry>
diff --git a/neon/doc/ref/vers.xml b/neon/doc/ref/vers.xml
new file mode 100644
index 000000000..9d180feb6
--- /dev/null
+++ b/neon/doc/ref/vers.xml
@@ -0,0 +1,61 @@
+<refentry id="refvers">
+
+ <refmeta>
+ <refentrytitle>ne_version_match</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ne_version_match</refname>
+ <refname>ne_version_string</refname>
+ <refpurpose>library versioning</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_utils.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>ne_version_match</function></funcdef>
+ <paramdef>int <parameter>major</parameter></paramdef>
+ <paramdef>int <parameter>minor</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char *<function>ne_version_string</function></funcdef>
+ <void/>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>ne_version_match</function> function returns
+ non-zero if the library version is not of major version
+ <parameter>major</parameter>, or the minor version is less than
+ <parameter>minor</parameter>.</para>
+
+ <para>The <function>ne_version_string</function> function returns
+ a string giving the library version.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>To require &neon; 1.x, version 1.2 or later:</para>
+
+ <programlisting>if (ne_version_match(1, 2)) {
+ printf("Library version out of date: 1.2 required, found %s.",
+ ne_version_string());
+ exit(1);
+}</programlisting>
+
+ </refsect1>
+
+</refentry>
diff --git a/neon/doc/refentry.xml b/neon/doc/refentry.xml
new file mode 100644
index 000000000..6610ac743
--- /dev/null
+++ b/neon/doc/refentry.xml
@@ -0,0 +1,59 @@
+
+ <!-- ******************************************************************* -->
+
+ <refentry id="refXXXX">
+
+ <refmeta>
+ <refentrytitle>this_api_call</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>this_api_call</refname>
+ <refname>other_api_call</refname>
+ <refpurpose>general purpose of group of functions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+
+ <funcsynopsis>
+
+ <funcsynopsisinfo>#include &lt;ne_header.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>ne_set_useragent</function></funcdef>
+ <paramdef>ne_session *<parameter>session</parameter></paramdef>
+ <paramdef>const char *<parameter>product</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>XXX</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Return value</title>
+
+ <para>XXX</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>XXX</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+
+ <para>XXX</para>
+ </refsect1>
+
+ </refentry>
+
diff --git a/neon/doc/using-neon.txt b/neon/doc/using-neon.txt
new file mode 100644
index 000000000..54751525b
--- /dev/null
+++ b/neon/doc/using-neon.txt
@@ -0,0 +1,132 @@
+
+Guide to neon
+============= Id: using-neon.txt,v 1.4 2000/07/16 16:20:12 joe Exp
+
+Using libneon from applications
+-------------------------------
+
+The neon source package is designed to be easily incorporated into
+applications:
+
+- autoconf macros are distributed in the 'macros' subdirectory of the
+ neon distribution. Use NEON_LIBRARY from your configure.in to check
+ for the presence of the neon library installed on the system. The
+ macro adds an '--with-neon=...' argument to configure, which allows
+ the user to specify a location for the library (the standard /usr
+ and /usr/local directories are checked automatically without having
+ to be specified).
+
+- The 'src' directory of the neon package can be imported directly
+ into your application, if you do not wish to add an external
+ dependency. The NEON_LIBRARY autoconf macro can fall back to
+ compile and link this source code into your application if libneon
+ is not found on the system. Place the source code in a subdirectory
+ of your package named 'libneon', and pass the 'bundled' argument to
+ the macro, as follows:
+
+ NEON_LIBRARY([bundled])
+
+ In your Makefile, $NEONOBJS will be added to $LIBOBJS if the bundled
+ neon is required, so you need to set this variable to the set of
+ neon object files which you require in your application, e.g.:
+
+ NEONOBJS = libneon/http_utils.o libneon/http_request.o \
+ libneon/http_auth.o libneon/string_utils.o \
+ libneon/socket.o libneon/md5.o libneon/dates.o
+ LIBOBJS = @LIBOBJS@
+ LIBS = @LIBS@
+ CFLAGS = @CFLAGS@ etc...
+
+ CFLAGS and LIBS will be adjusted by the NEON_LIBRARY as appropriate
+ for compiling against the bundled sources or the existing library.
+
+The neon API
+============
+
+neon offers two levels of API for use in applications:
+
+- Low-level HTTP request/response handling
+- High-level method invocation
+
+The low-level interface allows for easily designing new method
+handlers, taking care of things like persistent connections,
+authentication, and proxy servers. The high-level interface allows
+you to easily integrate existing HTTP (and WebDAV) methods into your
+application. This document details both interfaces.
+
+N.B.: Documentation is always WRONG. The definitive API reference is in
+src/*.c, with src/*.h is hopefully fairly similar.
+
+An Important Note
+-----------------
+
+Most neon functions which allocate memory with malloc() will call
+abort() if malloc() returns NULL. This is a design decision, the
+rationale being:
+
+- it makes the interfaces cleaner.
+
+- if malloc() DOES return NULL there is not much you can do about it.
+
+- Apparently, malloc() won't return NULL on systems which over-commit
+ memory (e.g. Linux), so it doesn't make any real difference anyway.
+
+The author is open to persuasion on this: mail neon@webdav.org.
+
+The http_session type
+---------------------
+
+The http_session type is used whether you are writing to the low-level
+or the high-level interface. An http_session object is created to
+store data which persists beyond a single HTTP request:
+
+ - Protocol options, e.g. (proxy) server details
+ - Authentication information
+ - Persistent connection
+
+A session is created with the 'http_session_create' call. Before
+creating a request for the session, the server details must be set, as
+follows:
+
+ http_session *sess;
+ /* Initialize the socket library */
+ sock_init();
+ /* Create the session */
+ sess = http_session_create();
+ /* Optionally, set a proxy server */
+ http_session_proxy(sess, "proxy.myisp.com", 8080);
+ /* Set the server */
+ http_session_server(sess, "my.server.com", 80);
+
+The proxy server should be set BEFORE the origin server; otherwise a
+DNS lookup will be performed on the origin server by the
+http_session_server call, which may well fail if the client is
+firewalled. http_session_{proxy,server} will return HTTP_LOOKUP if
+the DNS lookup fails; otherwise HTTP_OK.
+
+The 'http_set_expect100' call can be used to determine whether the
+"Expect: 100-continue" header is sent with requests. This header,
+when supported correctly by the server, means that an error response
+(e.g. 401) can be returned before the request body is sent. This is
+useful if you are doing a PUT with a 100mb request body. Note that
+Apache/1.3.6 and before do not implement this feature correctly, so
+use with care. http_options can tell you whether the Apache
+
+The 'http_set_persist' call can be used to turn off persistent
+connection handling: it is on by default.
+
+The 'http_set_useragent' call can be used to set the User-Agent header
+to be sent with requests. A product token of the form
+"myhttpclient/0.1.2" should be passed, and will have "neon/x.y.z"
+appended in the actual header sent.
+
+When a session has been finished with, it should be destroyed using
+http_session_destroy. Any subsequent operations on the session object
+will have undefined results (i.e. will segfault).
+
+Low-level HTTP Request/Response Handling
+----------------------------------------
+
+...
+
+
diff --git a/neon/doc/xref-man.xsl b/neon/doc/xref-man.xsl
new file mode 100644
index 000000000..249bda5b8
--- /dev/null
+++ b/neon/doc/xref-man.xsl
@@ -0,0 +1,46 @@
+<?xml version='1.0'?>
+<!-- vim:set sts=2 shiftwidth=2 syntax=sgml: -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'>
+
+<!-- THIS FILE IS UNDER WHATEVER LICENSE xmlto's XSL is -->
+
+<xsl:template match="xref">
+ <xsl:variable name="targets" select="id(@linkend)"/>
+ <xsl:variable name="target" select="$targets[1]"/>
+ <xsl:variable name="type" select="local-name($target)"/>
+
+ <xsl:choose>
+ <xsl:when test="$type=''">
+ <xsl:message>
+ <xsl:text>xref to nonexistent id </xsl:text>
+ <xsl:value-of select="@linkend"/>
+ </xsl:message>
+ </xsl:when>
+
+ <xsl:when test="$type='refentry'">
+ <xsl:call-template name="do-citerefentry">
+ <xsl:with-param name="refentrytitle"
+ select="$target/refmeta/refentrytitle[1]"/>
+ <xsl:with-param name="manvolnum"
+ select="$target/refmeta/manvolnum"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="$type='refname'">
+ <xsl:call-template name="do-citerefentry">
+ <xsl:with-param name="refentrytitle" select="$target"/>
+ <xsl:with-param name="manvolnum"
+ select="$target/../../refmeta/manvolnum"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>[xref to </xsl:text>
+ <xsl:value-of select="$type"/>
+ <xsl:text>]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/neon/example/.cvsignore b/neon/example/.cvsignore
new file mode 100644
index 000000000..e19f12339
--- /dev/null
+++ b/neon/example/.cvsignore
@@ -0,0 +1,2 @@
+*.lo
+
diff --git a/neon/example/nbrowse.glade b/neon/example/nbrowse.glade
new file mode 100644
index 000000000..d557925c4
--- /dev/null
+++ b/neon/example/nbrowse.glade
@@ -0,0 +1,284 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+ <name>nbrowse</name>
+ <program_name>nbrowse</program_name>
+ <directory></directory>
+ <source_directory>nbrowse</source_directory>
+ <pixmaps_directory>nbrowse</pixmaps_directory>
+ <language>C</language>
+ <gnome_support>True</gnome_support>
+ <gettext_support>True</gettext_support>
+ <output_support_files>False</output_support_files>
+ <output_build_files>False</output_build_files>
+</project>
+
+<widget>
+ <class>GtkWindow</class>
+ <name>nbrowse</name>
+ <signal>
+ <name>remove</name>
+ <handler>gtk_main_quit</handler>
+ <last_modification_time>Sun, 18 Jun 2000 11:49:12 GMT</last_modification_time>
+ </signal>
+ <title>WebDAV Browser</title>
+ <type>GTK_WINDOW_TOPLEVEL</type>
+ <position>GTK_WIN_POS_CENTER</position>
+ <modal>False</modal>
+ <default_width>400</default_width>
+ <default_height>300</default_height>
+ <allow_shrink>False</allow_shrink>
+ <allow_grow>True</allow_grow>
+ <auto_shrink>False</auto_shrink>
+
+ <widget>
+ <class>GtkVBox</class>
+ <name>vbox1</name>
+ <homogeneous>False</homogeneous>
+ <spacing>0</spacing>
+
+ <widget>
+ <class>GtkMenuBar</class>
+ <name>menubar1</name>
+ <shadow_type>GTK_SHADOW_OUT</shadow_type>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>False</fill>
+ </child>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>file1</name>
+ <stock_item>GNOMEUIINFO_MENU_FILE_TREE</stock_item>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>file1_menu</name>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>change_server1</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_change_server1_activate</handler>
+ <last_modification_time>Thu, 15 Jun 2000 10:47:10 GMT</last_modification_time>
+ </signal>
+ <label>Change server...</label>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator3</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>exit1</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_exit1_activate</handler>
+ <last_modification_time>Thu, 15 Jun 2000 10:46:15 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_EXIT_ITEM</stock_item>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkHPaned</class>
+ <name>hpaned1</name>
+ <handle_size>10</handle_size>
+ <gutter_size>6</gutter_size>
+ <position>149</position>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkScrolledWindow</class>
+ <name>scrolledwindow2</name>
+ <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
+ <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+ <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+ <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+ <child>
+ <shrink>True</shrink>
+ <resize>False</resize>
+ </child>
+
+ <widget>
+ <class>GtkTree</class>
+ <name>tree</name>
+ <selection_mode>GTK_SELECTION_BROWSE</selection_mode>
+ <view_mode>GTK_TREE_VIEW_ITEM</view_mode>
+ <view_line>True</view_line>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkScrolledWindow</class>
+ <name>scrolledwindow1</name>
+ <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
+ <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+ <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+ <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+ <child>
+ <shrink>True</shrink>
+ <resize>True</resize>
+ </child>
+
+ <widget>
+ <class>GnomeIconList</class>
+ <name>iconlist1</name>
+ <can_focus>True</can_focus>
+ <selection_mode>GTK_SELECTION_SINGLE</selection_mode>
+ <icon_width>78</icon_width>
+ <row_spacing>4</row_spacing>
+ <column_spacing>2</column_spacing>
+ <text_spacing>2</text_spacing>
+ <text_editable>False</text_editable>
+ <text_static>False</text_static>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+</widget>
+
+<widget>
+ <class>GnomeDialog</class>
+ <name>enter_url</name>
+ <title>Enter URL</title>
+ <type>GTK_WINDOW_DIALOG</type>
+ <position>GTK_WIN_POS_CENTER</position>
+ <modal>True</modal>
+ <allow_shrink>False</allow_shrink>
+ <allow_grow>False</allow_grow>
+ <auto_shrink>False</auto_shrink>
+ <auto_close>False</auto_close>
+ <hide_on_close>False</hide_on_close>
+
+ <widget>
+ <class>GtkVBox</class>
+ <child_name>GnomeDialog:vbox</child_name>
+ <name>dialog-vbox1</name>
+ <homogeneous>False</homogeneous>
+ <spacing>8</spacing>
+ <child>
+ <padding>4</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkHButtonBox</class>
+ <child_name>GnomeDialog:action_area</child_name>
+ <name>dialog-action_area1</name>
+ <layout_style>GTK_BUTTONBOX_END</layout_style>
+ <spacing>8</spacing>
+ <child_min_width>85</child_min_width>
+ <child_min_height>27</child_min_height>
+ <child_ipad_x>7</child_ipad_x>
+ <child_ipad_y>0</child_ipad_y>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>True</fill>
+ <pack>GTK_PACK_END</pack>
+ </child>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>button1</name>
+ <can_default>True</can_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_OK</stock_button>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>button3</name>
+ <can_default>True</can_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_CANCEL</stock_button>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkHBox</class>
+ <name>hbox1</name>
+ <homogeneous>False</homogeneous>
+ <spacing>0</spacing>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GnomePixmap</class>
+ <name>pixmap1</name>
+ <filename>gnome-question.png</filename>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkVBox</class>
+ <name>vbox2</name>
+ <border_width>7</border_width>
+ <homogeneous>False</homogeneous>
+ <spacing>6</spacing>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>label1</name>
+ <label>Enter a URL to browse:
+(for example, http://www.driveway.com/user.myname/)</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>False</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkEntry</class>
+ <name>entry1</name>
+ <can_focus>True</can_focus>
+ <editable>True</editable>
+ <text_visible>True</text_visible>
+ <text_max_length>0</text_max_length>
+ <text></text>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>False</fill>
+ </child>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/neon/example/nbrowse/callbacks.c b/neon/example/nbrowse/callbacks.c
new file mode 100644
index 000000000..3e9f7e942
--- /dev/null
+++ b/neon/example/nbrowse/callbacks.c
@@ -0,0 +1,40 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <gnome.h>
+
+#include "callbacks.h"
+#include "interface.h"
+#include "support.h"
+
+
+void change_server()
+{
+
+}
+
+
+void
+on_change_server1_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+
+}
+
+
+void
+on_exit1_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+
+}
+
+
+void
+on_about1_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+
+}
+
diff --git a/neon/example/nbrowse/callbacks.h b/neon/example/nbrowse/callbacks.h
new file mode 100644
index 000000000..e3db59c3c
--- /dev/null
+++ b/neon/example/nbrowse/callbacks.h
@@ -0,0 +1,14 @@
+#include <gnome.h>
+
+
+void
+on_change_server1_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_exit1_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_about1_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
diff --git a/neon/example/nbrowse/interface.c b/neon/example/nbrowse/interface.c
new file mode 100644
index 000000000..6332abdad
--- /dev/null
+++ b/neon/example/nbrowse/interface.c
@@ -0,0 +1,240 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <gnome.h>
+
+#include "callbacks.h"
+#include "interface.h"
+#include "support.h"
+
+GtkWidget *tree;
+GtkWidget *iconlist;
+
+static GnomeUIInfo file1_menu_uiinfo[] =
+{
+ {
+ GNOME_APP_UI_ITEM, N_("Change server..."),
+ NULL,
+ on_change_server1_activate, NULL, NULL,
+ GNOME_APP_PIXMAP_NONE, NULL,
+ 0, 0, NULL
+ },
+ GNOMEUIINFO_SEPARATOR,
+ GNOMEUIINFO_MENU_EXIT_ITEM (on_exit1_activate, NULL),
+ GNOMEUIINFO_END
+};
+
+static GnomeUIInfo menubar1_uiinfo[] =
+{
+ GNOMEUIINFO_MENU_FILE_TREE (file1_menu_uiinfo),
+ GNOMEUIINFO_END
+};
+
+GtkWidget*
+create_nbrowse (void)
+{
+ GtkWidget *nbrowse;
+ GtkWidget *vbox1;
+ GtkWidget *menubar1;
+ GtkWidget *hpaned1;
+ GtkWidget *scrolledwindow2;
+ GtkWidget *scrolledwindow1;
+
+ nbrowse = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_object_set_data (GTK_OBJECT (nbrowse), "nbrowse", nbrowse);
+ gtk_window_set_title (GTK_WINDOW (nbrowse), _("neon WebDAV Browser"));
+ /*gtk_window_set_position (GTK_WINDOW (nbrowse), GTK_WIN_POS_CENTER); */
+ gtk_window_set_default_size (GTK_WINDOW (nbrowse), 400, 300);
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_widget_ref (vbox1);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "vbox1", vbox1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (vbox1);
+ gtk_container_add (GTK_CONTAINER (nbrowse), vbox1);
+
+ menubar1 = gtk_menu_bar_new ();
+ gtk_widget_ref (menubar1);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "menubar1", menubar1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (menubar1);
+ gtk_box_pack_start (GTK_BOX (vbox1), menubar1, FALSE, FALSE, 0);
+ gnome_app_fill_menu (GTK_MENU_SHELL (menubar1), menubar1_uiinfo,
+ NULL, FALSE, 0);
+
+ gtk_widget_ref (menubar1_uiinfo[0].widget);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "file1",
+ menubar1_uiinfo[0].widget,
+ (GtkDestroyNotify) gtk_widget_unref);
+
+ gtk_widget_ref (file1_menu_uiinfo[0].widget);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "change_server1",
+ file1_menu_uiinfo[0].widget,
+ (GtkDestroyNotify) gtk_widget_unref);
+
+ gtk_widget_ref (file1_menu_uiinfo[1].widget);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "separator3",
+ file1_menu_uiinfo[1].widget,
+ (GtkDestroyNotify) gtk_widget_unref);
+
+ gtk_widget_ref (file1_menu_uiinfo[2].widget);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "exit1",
+ file1_menu_uiinfo[2].widget,
+ (GtkDestroyNotify) gtk_widget_unref);
+
+ hpaned1 = gtk_hpaned_new ();
+ gtk_widget_ref (hpaned1);
+ gtk_paned_set_gutter_size( GTK_HPANED(hpaned1), 12 );
+
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "hpaned1", hpaned1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hpaned1);
+ gtk_box_pack_start (GTK_BOX (vbox1), hpaned1, TRUE, TRUE, 0);
+ gtk_paned_set_position (GTK_PANED (hpaned1), 149);
+
+ scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_ref (scrolledwindow2);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "scrolledwindow2", scrolledwindow2,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow2),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+ gtk_widget_show (scrolledwindow2);
+ gtk_paned_pack1 (GTK_PANED (hpaned1), scrolledwindow2, FALSE, TRUE);
+
+ tree = gtk_tree_new ();
+ gtk_widget_ref (tree);
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "tree", tree,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (tree);
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwindow2), tree);
+ gtk_tree_set_selection_mode (GTK_TREE (tree), GTK_SELECTION_SINGLE);
+ gtk_tree_set_view_mode (GTK_TREE (tree), GTK_TREE_VIEW_ITEM);
+
+ scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_ref (scrolledwindow1);
+ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow1),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "scrolledwindow1", scrolledwindow1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (scrolledwindow1);
+ gtk_paned_pack2 (GTK_PANED (hpaned1), scrolledwindow1, TRUE, TRUE);
+
+ iconlist = gnome_icon_list_new_flags (78, NULL, 0);
+ gtk_widget_ref (iconlist);
+ gnome_icon_list_set_selection_mode( GNOME_ICON_LIST(iconlist), GTK_SELECTION_MULTIPLE );
+ gtk_object_set_data_full (GTK_OBJECT (nbrowse), "iconlist", iconlist,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (iconlist);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow1), iconlist);
+
+ gtk_signal_connect (GTK_OBJECT (nbrowse), "remove",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ NULL);
+
+ return nbrowse;
+}
+
+GtkWidget*
+create_enter_url (void)
+{
+ GtkWidget *enter_url;
+ GtkWidget *dialog_vbox1;
+ GtkWidget *hbox1;
+ gchar *pixmap1_filename;
+ GtkWidget *pixmap1;
+ GtkWidget *vbox2;
+ GtkWidget *label1;
+ GtkWidget *entry1;
+ GtkWidget *dialog_action_area1;
+ GtkWidget *button1;
+ GtkWidget *button3;
+
+ enter_url = gnome_dialog_new (_("Enter URL"), NULL);
+ gtk_object_set_data (GTK_OBJECT (enter_url), "enter_url", enter_url);
+ GTK_WINDOW (enter_url)->type = GTK_WINDOW_DIALOG;
+ gtk_window_set_position (GTK_WINDOW (enter_url), GTK_WIN_POS_CENTER);
+ gtk_window_set_modal (GTK_WINDOW (enter_url), TRUE);
+ gtk_window_set_policy (GTK_WINDOW (enter_url), FALSE, FALSE, FALSE);
+
+ dialog_vbox1 = GNOME_DIALOG (enter_url)->vbox;
+ gtk_object_set_data (GTK_OBJECT (enter_url), "dialog_vbox1", dialog_vbox1);
+ gtk_widget_show (dialog_vbox1);
+
+ hbox1 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox1);
+ gtk_object_set_data_full (GTK_OBJECT (enter_url), "hbox1", hbox1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hbox1);
+
+ gtk_box_pack_start (GTK_BOX (dialog_vbox1), hbox1, TRUE, TRUE, 0);
+
+ pixmap1 = gtk_type_new (gnome_pixmap_get_type ());
+ pixmap1_filename = gnome_pixmap_file ("nbrowse/gnome-question.png");
+ if (pixmap1_filename)
+ gnome_pixmap_load_file (GNOME_PIXMAP (pixmap1), pixmap1_filename);
+ else
+ g_warning (_("Couldn't find pixmap file: %s"), "gnome-question.png");
+ g_free (pixmap1_filename);
+ gtk_widget_ref (pixmap1);
+ gtk_object_set_data_full (GTK_OBJECT (enter_url), "pixmap1", pixmap1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (pixmap1);
+ gtk_box_pack_start (GTK_BOX (hbox1), pixmap1, TRUE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 6);
+ gtk_widget_ref (vbox2);
+ gtk_object_set_data_full (GTK_OBJECT (enter_url), "vbox2", vbox2,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (vbox2);
+ gtk_box_pack_start (GTK_BOX (hbox1), vbox2, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox2), 7);
+
+ label1 = gtk_label_new (_("Enter a URL to browse:\n(for example, http://www.driveway.com/user.myname/)"));
+ gtk_widget_ref (label1);
+ gtk_object_set_data_full (GTK_OBJECT (enter_url), "label1", label1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (label1);
+ gtk_box_pack_start (GTK_BOX (vbox2), label1, FALSE, FALSE, 0);
+
+ entry1 = gtk_entry_new ();
+ gtk_widget_ref (entry1);
+ gtk_object_set_data_full (GTK_OBJECT (enter_url), "entry1", entry1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (entry1);
+ gtk_box_pack_start (GTK_BOX (vbox2), entry1, FALSE, FALSE, 0);
+
+ dialog_action_area1 = GNOME_DIALOG (enter_url)->action_area;
+ gtk_object_set_data (GTK_OBJECT (enter_url), "dialog_action_area1", dialog_action_area1);
+ gtk_widget_show (dialog_action_area1);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END);
+ gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog_action_area1), 8);
+
+ gnome_dialog_append_button (GNOME_DIALOG (enter_url), GNOME_STOCK_BUTTON_OK);
+ button1 = g_list_last (GNOME_DIALOG (enter_url)->buttons)->data;
+ gtk_widget_ref (button1);
+ gtk_object_set_data_full (GTK_OBJECT (enter_url), "button1", button1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (button1);
+ GTK_WIDGET_SET_FLAGS (button1, GTK_CAN_DEFAULT);
+
+ gnome_dialog_append_button (GNOME_DIALOG (enter_url), GNOME_STOCK_BUTTON_CANCEL);
+ button3 = g_list_last (GNOME_DIALOG (enter_url)->buttons)->data;
+ gtk_widget_ref (button3);
+ gtk_object_set_data_full (GTK_OBJECT (enter_url), "button3", button3,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (button3);
+ GTK_WIDGET_SET_FLAGS (button3, GTK_CAN_DEFAULT);
+
+ return enter_url;
+}
+
diff --git a/neon/example/nbrowse/interface.h b/neon/example/nbrowse/interface.h
new file mode 100644
index 000000000..71bb80803
--- /dev/null
+++ b/neon/example/nbrowse/interface.h
@@ -0,0 +1,8 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+GtkWidget* create_nbrowse (void);
+GtkWidget* create_enter_url (void);
+extern GtkWidget *tree;
+extern GtkWidget *iconlist;
diff --git a/neon/example/nbrowse/main.c b/neon/example/nbrowse/main.c
new file mode 100644
index 000000000..c5a2a20b8
--- /dev/null
+++ b/neon/example/nbrowse/main.c
@@ -0,0 +1,337 @@
+/*
+ nbrowse, dummy WebDAV browsing in GNOME
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Id: main.c,v 1.3 2000/07/16 16:15:44 joe Exp
+*/
+
+/*
+ Aims to demonstrate that a blocking-IO based HTTP library can be
+ easily integrated with a GUI polling loop.
+
+ TODO:
+ - Stop it crashing all the time
+ - Allow one PROPFIND at a time, i.e., ignore clicks once
+ already loading a tree
+ - Fetch lockdiscovery and display "locked" icons
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <signal.h>
+
+#include <pthread.h>
+
+#include <gnome.h>
+
+#include <gtk/gtktree.h>
+
+
+#include <neon_config.h>
+
+#include <dav_props.h>
+#include <dav_basic.h>
+#include <uri.h>
+#include <xalloc.h>
+
+#include "basename.h"
+#include "interface.h"
+#include "support.h"
+
+#define ELM_getcontentlength (1001)
+#define ELM_resourcetype (1002)
+#define ELM_getlastmodified (1003)
+#define ELM_executable (1004)
+#define ELM_collection (1005)
+#define ELM_displayname (1006)
+
+enum resource_type {
+ res_normal,
+ res_collection,
+ res_reference,
+};
+
+http_session *sess;
+/* since we let >1 thread spawn at a time,
+ * enforce mutual exclusion over access to the session state
+ */
+pthread_mutex_t sess_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct browser {
+ const char *uri;
+ GtkTree *tree;
+ GnomeIconList *list;
+ unsigned int thread_running:1;
+ pthread_t thread;
+};
+
+struct resource {
+ char *uri;
+ char *displayname;
+ enum resource_type type;
+ size_t size;
+ time_t modtime;
+ int is_executable;
+ struct resource *next;
+};
+
+static int start_propfind( struct browser *b );
+
+static int check_context( hip_xml_elmid child, hip_xml_elmid parent )
+{
+ /* FIXME */
+ return 0;
+}
+
+/* Does a PROPFIND to load URI at tree */
+static void load_tree( GtkWidget *tree, const char *uri )
+{
+ struct browser *b = xmalloc(sizeof(struct browser));
+
+ /* Create the tree */
+ b->tree = GTK_TREE(tree);
+ b->list = GNOME_ICON_LIST(iconlist);
+ b->uri = uri;
+
+ start_propfind(b);
+}
+
+static void select_node_cb( GtkTreeItem *item )
+{
+ char *uri = gtk_object_get_data( GTK_OBJECT(item), "uri" );
+ static char *loaded = "l";
+
+ printf( "selected: %s\n", uri );
+
+#if 0
+ if( gtk_object_get_data( GTK_OBJECT(item), "is_loaded" ) == NULL ) {
+ gtk_object_set_data( GTK_OBJECT(item), "is_loaded", loaded );
+ } else {
+ printf( "already loaded this node.\n" );
+ return;
+ }
+#endif
+
+ gnome_icon_list_clear(GNOME_ICON_LIST(iconlist));
+ gtk_tree_remove_items (GTK_TREE(item->subtree), GTK_TREE(item->subtree)->children);
+ load_tree( item->subtree, uri );
+}
+
+static int end_element( void *userdata, const struct hip_xml_elm *elm, const char *cdata )
+{
+ struct resource *r;
+ r = dav_propfind_get_current_resource( userdata );
+ if( r == NULL ) {
+ return 0;
+ }
+ DEBUG( DEBUG_HTTP, "In resource %s\n", r->uri );
+ DEBUG( DEBUG_HTTP, "Property [%d], %s@@%s = %s\n", elm->id,
+ elm->nspace, elm->name, cdata?cdata:"undefined" );
+ switch( elm->id ) {
+ case ELM_collection:
+ r->type = res_collection;
+ break;
+ case ELM_getcontentlength:
+ if( cdata ) {
+ r->size = atoi(cdata);
+ }
+ break;
+ case ELM_displayname:
+ if( cdata ) {
+ r->displayname = xstrdup(cdata);
+ }
+ break;
+ case ELM_getlastmodified:
+ if( cdata ) {
+ r->modtime = http_dateparse(cdata);
+ /* will be -1 on error */
+ }
+ break;
+ case ELM_executable:
+ if( cdata ) {
+ if( strcasecmp( cdata, "T" ) == 0 ) {
+ r->is_executable = 1;
+ } else {
+ r->is_executable = 0;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static void *start_resource( void *userdata, const char *href )
+{
+ struct resource *res = xmalloc(sizeof(struct resource));
+ memset(res,0,sizeof(struct resource));
+ res->uri = xstrdup(href);
+ return res;
+}
+
+static void end_resource( void *userdata, void *resource,
+ const char *status_line, const http_status *status,
+ const char *description )
+{
+ struct browser *b = userdata;
+ char *abspath;
+ struct resource *res = resource;
+ char *leaf;
+
+ if( res == NULL ) {
+ return;
+ }
+
+ DEBUG( DEBUG_HTTP, "Href: %s\n", res->uri );
+ /* char * cast is valid since res->uri is char * */
+ abspath = (char *) uri_abspath( res->uri );
+
+ if( uri_compare( abspath, b->uri ) == 0 ) {
+ return;
+ }
+
+ if( uri_has_trailing_slash(abspath) ) {
+ abspath[strlen(abspath)-1] = '\0';
+ }
+
+ leaf = base_name(abspath);
+
+ gdk_threads_enter();
+
+ if( res->type == res_collection ) {
+ GtkWidget *sub, *item;
+
+ item = gtk_tree_item_new_with_label( leaf );
+ gtk_tree_append( b->tree, item );
+
+ sub = gtk_tree_new();
+ gtk_tree_item_set_subtree( GTK_TREE_ITEM(item), sub );
+ gtk_object_set_data_full( GTK_OBJECT(item), "uri",
+ xstrdup(res->uri), free );
+ gtk_signal_connect (GTK_OBJECT(item), "select",
+ GTK_SIGNAL_FUNC(select_node_cb), item);
+ gtk_signal_connect (GTK_OBJECT(item), "expand",
+ GTK_SIGNAL_FUNC(select_node_cb), item);
+ gtk_widget_show( item );
+
+ gnome_icon_list_append( b->list, "/usr/share/pixmaps/mc/i-directory.png", leaf );
+
+ } else {
+ gnome_icon_list_append( b->list, "/usr/share/pixmaps/mc/i-regular.png", leaf );
+ }
+
+ gdk_threads_leave();
+
+ free( res->uri);
+ free( res );
+}
+
+/* Run in a separate thread */
+static void *do_propfind( void *ptr )
+{
+ dav_propfind_handler *ph;
+ static const dav_propname props[] = {
+ { "DAV:", "getcontentlength" },
+ { "DAV:", "resourcetype" },
+ { "DAV:", "getlastmodified" },
+ { "DAV:", "displayname" },
+ { "http://apache.org/dav/props/", "executable" },
+ { NULL }
+ };
+ static const struct hip_xml_elm elms[] = {
+ { "DAV:", "getcontentlength", ELM_getcontentlength, HIP_XML_CDATA },
+ { "DAV:", "resourcetype", ELM_resourcetype, 0 },
+ { "DAV:", "getlastmodified", ELM_getlastmodified, HIP_XML_CDATA },
+ { "http://apache.org/dav/props/", "executable", ELM_executable, HIP_XML_CDATA },
+ { "DAV:", "collection", ELM_collection, 0 },
+ { "DAV:", "displayname", ELM_displayname, HIP_XML_CDATA },
+ { NULL }
+ };
+ struct browser *b = ptr;
+
+ pthread_detach(pthread_self());
+
+ pthread_mutex_lock( &sess_mutex );
+
+ ph = dav_propfind_create (sess, b->uri, DAV_DEPTH_ONE );
+
+ dav_propfind_set_resource_handlers (ph, start_resource, end_resource);
+
+ hip_xml_add_handler (dav_propfind_get_parser(ph), elms,
+ check_context, NULL, end_element, ph);
+
+ if (dav_propfind_named (ph, props, ptr) != HTTP_OK) {
+ printf( "PROPFIND failed: %s\n", http_get_error(sess) );
+ }
+
+ pthread_mutex_unlock( &sess_mutex );
+
+ free( b );
+
+ return NULL;
+}
+
+
+static int start_propfind( struct browser *b )
+{
+ b->thread_running = 1;
+ return pthread_create( &b->thread, NULL, do_propfind, b );
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *nbrowse;
+
+ if( argc < 3 ) {
+ printf("nbrowse: Usage 'nbrowse server.name.com /path/'.\n");
+ return -1;
+ }
+
+#ifdef ENABLE_NLS
+ bindtextdomain (PACKAGE, PACKAGE_LOCALE_DIR);
+ textdomain (PACKAGE);
+#endif
+
+ /* Initialize threading */
+ g_thread_init(NULL);
+
+ signal( SIGPIPE, SIG_IGN );
+
+ gnome_init ("nbrowse", NEON_VERSION, argc, argv);
+
+ /* Create the main window */
+ nbrowse = create_nbrowse ();
+ gtk_widget_show (nbrowse);
+
+ /* Create a new session. */
+ sess = http_session_create();
+
+ printf( "Using server: %s - path %s\n", argv[1], argv[2] );
+
+ http_session_server( sess, argv[1], 80 );
+ http_set_useragent( sess, "nbrowser/" NEON_VERSION );
+
+ load_tree( tree, argv[2] );
+
+ gdk_threads_enter();
+ gtk_main();
+ gdk_threads_leave();
+ return 0;
+}
+
diff --git a/neon/example/nbrowse/support.c b/neon/example/nbrowse/support.c
new file mode 100644
index 000000000..c76f33308
--- /dev/null
+++ b/neon/example/nbrowse/support.c
@@ -0,0 +1,146 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <gnome.h>
+
+#include "support.h"
+
+/* This is an internally used function to create pixmaps. */
+static GtkWidget* create_dummy_pixmap (GtkWidget *widget,
+ gboolean gnome_pixmap);
+
+GtkWidget*
+lookup_widget (GtkWidget *widget,
+ const gchar *widget_name)
+{
+ GtkWidget *parent, *found_widget;
+
+ for (;;)
+ {
+ if (GTK_IS_MENU (widget))
+ parent = gtk_menu_get_attach_widget (GTK_MENU (widget));
+ else
+ parent = widget->parent;
+ if (parent == NULL)
+ break;
+ widget = parent;
+ }
+
+ found_widget = (GtkWidget*) gtk_object_get_data (GTK_OBJECT (widget),
+ widget_name);
+ if (!found_widget)
+ g_warning ("Widget not found: %s", widget_name);
+ return found_widget;
+}
+
+/* This is a dummy pixmap we use when a pixmap can't be found. */
+static char *dummy_pixmap_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"1 1 1 1",
+" c None",
+/* pixels */
+" ",
+" "
+};
+
+/* This is an internally used function to create pixmaps. */
+static GtkWidget*
+create_dummy_pixmap (GtkWidget *widget,
+ gboolean gnome_pixmap)
+{
+ GdkColormap *colormap;
+ GdkPixmap *gdkpixmap;
+ GdkBitmap *mask;
+ GtkWidget *pixmap;
+
+ if (gnome_pixmap)
+ {
+ return gnome_pixmap_new_from_xpm_d (dummy_pixmap_xpm);
+ }
+
+ colormap = gtk_widget_get_colormap (widget);
+ gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask,
+ NULL, dummy_pixmap_xpm);
+ if (gdkpixmap == NULL)
+ g_error ("Couldn't create replacement pixmap.");
+ pixmap = gtk_pixmap_new (gdkpixmap, mask);
+ gdk_pixmap_unref (gdkpixmap);
+ gdk_bitmap_unref (mask);
+ return pixmap;
+}
+
+/* This is an internally used function to create pixmaps. */
+GtkWidget*
+create_pixmap (GtkWidget *widget,
+ const gchar *filename,
+ gboolean gnome_pixmap)
+{
+ GtkWidget *pixmap;
+ GdkColormap *colormap;
+ GdkPixmap *gdkpixmap;
+ GdkBitmap *mask;
+ gchar *pathname;
+
+ if (!filename || !filename[0])
+ return create_dummy_pixmap (widget, gnome_pixmap);
+
+ pathname = gnome_pixmap_file (filename);
+ if (!pathname)
+ {
+ g_warning (_("Couldn't find pixmap file: %s"), filename);
+ return create_dummy_pixmap (widget, gnome_pixmap);
+ }
+
+ if (gnome_pixmap)
+ {
+ pixmap = gnome_pixmap_new_from_file (pathname);
+ g_free (pathname);
+ return pixmap;
+ }
+
+ colormap = gtk_widget_get_colormap (widget);
+ gdkpixmap = gdk_pixmap_colormap_create_from_xpm (NULL, colormap, &mask,
+ NULL, pathname);
+ if (gdkpixmap == NULL)
+ {
+ g_warning (_("Couldn't create pixmap from file: %s"), pathname);
+ g_free (pathname);
+ return create_dummy_pixmap (widget, gnome_pixmap);
+ }
+ g_free (pathname);
+
+ pixmap = gtk_pixmap_new (gdkpixmap, mask);
+ gdk_pixmap_unref (gdkpixmap);
+ gdk_bitmap_unref (mask);
+ return pixmap;
+}
+
+/* This is an internally used function to create imlib images. */
+GdkImlibImage*
+create_image (const gchar *filename)
+{
+ GdkImlibImage *image;
+ gchar *pathname;
+
+ pathname = gnome_pixmap_file (filename);
+ if (!pathname)
+ {
+ g_warning (_("Couldn't find pixmap file: %s"), filename);
+ return NULL;
+ }
+
+ image = gdk_imlib_load_image (pathname);
+ g_free (pathname);
+ return image;
+}
+
diff --git a/neon/example/nbrowse/support.h b/neon/example/nbrowse/support.h
new file mode 100644
index 000000000..d9bb0728a
--- /dev/null
+++ b/neon/example/nbrowse/support.h
@@ -0,0 +1,34 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+#include <gnome.h>
+
+/*
+ * Public Functions.
+ */
+
+/*
+ * This function returns a widget in a component created by Glade.
+ * Call it with the toplevel widget in the component (i.e. a window/dialog),
+ * or alternatively any widget in the component, and the name of the widget
+ * you want returned.
+ */
+GtkWidget* lookup_widget (GtkWidget *widget,
+ const gchar *widget_name);
+
+/* get_widget() is deprecated. Use lookup_widget instead. */
+#define get_widget lookup_widget
+
+
+/*
+ * Private Functions.
+ */
+
+/* This is used to create the pixmaps in the interface. */
+GtkWidget* create_pixmap (GtkWidget *widget,
+ const gchar *filename,
+ gboolean gnome_pixmap);
+
+GdkImlibImage* create_image (const gchar *filename);
+
diff --git a/neon/example/nget.c b/neon/example/nget.c
new file mode 100644
index 000000000..3cc43dc5c
--- /dev/null
+++ b/neon/example/nget.c
@@ -0,0 +1,164 @@
+/*
+ nget, neon HTTP GET tester
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Id: nget.c,v 1.3 2000/05/10 18:16:56 joe Exp
+*/
+
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "http_request.h"
+#include "http_basic.h"
+#include "uri.h"
+#include "socket.h"
+
+#include "basename.h"
+
+int in_progress = 0, use_stdout = 0;
+
+static void pretty_progress_bar( void *ud, size_t progress, size_t total );
+
+static const char *use_filename( struct uri *uri )
+{
+ const char *fname;
+
+ fname = base_name( uri->path );
+ if( strcmp( fname, "/" ) == 0 || strlen( fname ) == 0 ) {
+ fname = "index.html";
+ }
+
+ return fname;
+}
+
+int main( int argc, char **argv )
+{
+ http_session *sess;
+ struct uri uri = {0}, defaults = {0};
+ struct stat st;
+ int ret;
+ const char *fname;
+ FILE *f;
+
+ if( argc < 2 || argc > 3 ) {
+ printf( "nget: Usage: \n"
+ " `nget url': download url using appropriate filename\n"
+ " `nget url filename': download url to given filename\n"
+ " `nget url -': download url and display on stdout\n" );
+ return -1;
+ }
+
+ sock_register_progress( pretty_progress_bar, NULL );
+
+ defaults.port = 80;
+
+ if( uri_parse( argv[1], &uri, &defaults ) ||
+ !uri.path || !uri.host || uri.port == -1 ) {
+ printf( "nget: Invalid URL.\n" );
+ return -1;
+ }
+
+ if( argc == 3 ) {
+ fname = argv[2];
+ } else {
+ fname = use_filename( &uri );
+ }
+ if( strcmp( fname, "-" ) == 0 ) {
+ f = stdout;
+ use_stdout = 1;
+ } else if( stat( fname, &st ) == 0 ) {
+ printf( "nget: File `%s' already exists.\n", fname );
+ return -1;
+ } else {
+ f = fopen( fname, "w" );
+ if( f == NULL ) {
+ printf( "nget: Could not open %s: %s\n", fname, strerror(errno) );
+ return -1;
+ }
+ }
+
+ sess = http_session_init();
+
+ http_session_server( sess, uri.host, uri.port );
+
+ if( !use_stdout ) {
+ printf( "nget: Downloading %s to %s\n", argv[1], fname );
+ }
+ ret = http_get( sess, uri.path, f );
+
+ if( in_progress ) {
+ printf( "\n" );
+ }
+
+ if( ret == HTTP_OK ) {
+ if( !use_stdout ) printf( "nget: Download complete.\n" );
+ } else {
+ fprintf( stderr,
+ "nget: Download error: %s\n", http_get_error(sess));
+ }
+
+ if( !use_stdout )
+ fclose( f );
+
+ return ret;
+}
+
+/* Smooth progress bar from cadaver.
+ * Doesn't update the bar more than once every 100ms, since this
+ * might give flicker, and would be bad if we are displaying on
+ * a slow link anyway.
+ */
+static void pretty_progress_bar( void *ud, size_t progress, size_t total )
+{
+ int len, n;
+ double pc;
+ static struct timeval last_call = {0};
+ struct timeval this_call;
+ if( use_stdout ) return;
+ in_progress = 1;
+ if( total == -1 ) {
+ printf( "\rProgress: %d bytes", progress );
+ return;
+ }
+ if( progress < total && gettimeofday( &this_call, NULL ) == 0 ) {
+ struct timeval diff;
+ timersub( &this_call, &last_call, &diff );
+ if( diff.tv_sec == 0 && diff.tv_usec < 100000 ) {
+ return;
+ }
+ last_call = this_call;
+ }
+ if( progress == 0 || total == 0 ) {
+ pc = 0;
+ } else {
+ pc = (double)progress / total;
+ }
+ len = pc * 30;
+ printf( "\rProgress: [" );
+ for( n = 0; n<30; n++ ) {
+ putchar( (n<len-1)?'=':
+ (n==(len-1)?'>':' ') );
+ }
+ printf( "] %5.1f%% of %d bytes", pc*100, total );
+ fflush( stdout );
+}
+
diff --git a/neon/example/nserver.c b/neon/example/nserver.c
new file mode 100644
index 000000000..7b8bf168c
--- /dev/null
+++ b/neon/example/nserver.c
@@ -0,0 +1,147 @@
+/*
+ nserver, neon Server checker.
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Id: nserver.c,v 1.2 2000/07/16 16:15:44 joe Exp
+*/
+
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <neon_config.h>
+#include <http_request.h>
+#include <http_basic.h>
+#include <uri.h>
+#include <socket.h>
+
+#include <getopt.h>
+
+#include "basename.h"
+
+static void conn_notify( void *userdata, sock_status status, const char *info )
+{
+ switch( status ) {
+ case sock_namelookup:
+ case sock_connecting:
+ case sock_connected:
+ break;
+ case sock_secure_details:
+ fprintf( stderr, "nserver: Using a secure connection (%s)\n", info );
+ break;
+ }
+}
+
+int main( int argc, char **argv )
+{
+ struct uri uri = {0}, defaults = {0};
+ http_session *sess;
+ http_req *req;
+ http_status status;
+ char *server = NULL, *pnt;
+ int ret;
+
+ neon_debug_init( stderr, 0 );
+
+ sess = http_session_create();
+
+ defaults.path = "/";
+ defaults.port = 0;
+ defaults.host = NULL;
+ defaults.scheme = "http";
+
+ if( argc < 2 ) {
+ printf( "nserver: Usage: %s http[s]://server.host.name[:port]\n",
+ argv[0] );
+ return -1;
+ }
+
+ pnt = strchr( argv[1], '/' );
+ if( pnt == NULL ) {
+ uri.host = argv[1];
+ pnt = strchr( uri.host, ':' );
+ if( pnt == NULL ) {
+ uri.port = 80;
+ } else {
+ uri.port = atoi(pnt+1);
+ *pnt = '\0';
+ }
+ uri.path = "/";
+ uri.scheme = "http";
+ } else {
+ if( uri_parse( argv[1], &uri, &defaults ) || !uri.host ) {
+ printf( "nserver: Could not parse URL `%s'\n", argv[1] );
+ return -1;
+ }
+ if( strcasecmp( uri.scheme, "https" ) == 0 ) {
+ if( uri.port == 0 ) {
+ uri.port = 443;
+ }
+ if( http_set_secure( sess, 1 ) ) {
+ fprintf( stderr, "nserver: SSL not supported.\n" );
+ exit( -1 );
+ }
+ }
+ }
+
+ sock_init();
+ sock_register_notify( conn_notify, NULL );
+
+ if( uri.port == 0 ) {
+ uri.port = 80;
+ }
+
+ printf( "nserver: Retrieving server string for server at %s (port %d):\n",
+ uri.host, uri.port );
+
+ req = http_request_create(sess, "HEAD", uri.path );
+ if( http_session_server( sess, uri.host, uri.port ) != HTTP_OK ) {
+ printf( "nserver: Hostname `%s' not found.\n", uri.host );
+ return -1;
+ }
+
+ /* Use a standard strdup-er handler */
+ http_add_response_header_handler( req, "Server",
+ http_duplicate_header, &server );
+
+ switch( http_request_dispatch( req, &status ) ) {
+ case HTTP_OK:
+ if( server == NULL ) {
+ printf( "nserver: No server string was given.\n" );
+ ret = 1;
+ } else {
+ printf( "Server string: %s\n", server );
+ ret = 0;
+ }
+ break;
+ default:
+ printf( "nserver: Failed: %s\n", http_get_error(sess) );
+ ret = -1;
+ break;
+ }
+
+ http_request_destroy( req );
+ http_session_destroy( sess );
+
+ return ret;
+}
diff --git a/neon/install-sh b/neon/install-sh
new file mode 100755
index 000000000..ebc66913e
--- /dev/null
+++ b/neon/install-sh
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/neon/lib/basename.c b/neon/lib/basename.c
new file mode 100644
index 000000000..358f5e8d0
--- /dev/null
+++ b/neon/lib/basename.c
@@ -0,0 +1,57 @@
+/* basename.c -- return the last element in a path
+ Copyright (C) 1990, 1998, 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <basename.h>
+
+#ifndef FILESYSTEM_PREFIX_LEN
+# define FILESYSTEM_PREFIX_LEN(Filename) 0
+#endif
+
+#ifndef ISSLASH
+# define ISSLASH(C) ((C) == '/')
+#endif
+
+/* In general, we can't use the builtin `basename' function if available,
+ since it has different meanings in different environments.
+ In some environments the builtin `basename' modifies its argument.
+ If NAME is all slashes, be sure to return `/'. */
+
+char *
+base_name (char const *name)
+{
+ char const *base = name += FILESYSTEM_PREFIX_LEN (name);
+ int all_slashes = 1;
+ char const *p;
+
+ for (p = name; *p; p++)
+ {
+ if (ISSLASH (*p))
+ base = p + 1;
+ else
+ all_slashes = 0;
+ }
+
+ /* If NAME is all slashes, arrange to return `/'. */
+ if (*base == '\0' && ISSLASH (*name) && all_slashes)
+ --base;
+
+ return (char *) base;
+}
diff --git a/neon/lib/basename.h b/neon/lib/basename.h
new file mode 100644
index 000000000..6b022126e
--- /dev/null
+++ b/neon/lib/basename.h
@@ -0,0 +1,2 @@
+
+char *base_name (char const *name);
diff --git a/neon/lib/dirname.c b/neon/lib/dirname.c
new file mode 100644
index 000000000..c8cca9ede
--- /dev/null
+++ b/neon/lib/dirname.c
@@ -0,0 +1,75 @@
+/* dirname.c -- return all but the last element in a path
+ Copyright (C) 1990, 1998 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/*******************************************************
+ * THIS IS A MODIFIED dirname IMPLEMENTATION:
+ * - sitecopy wants "" if there is no directory name,
+ * standard GNU implementation gives us "."
+ * - sitecopy wants the trailing slash.
+ *******************************************************/
+
+#include "xalloc.h"
+
+#if defined STDC_HEADERS || defined HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+# ifndef strrchr
+# define strrchr rindex
+# endif
+#endif
+
+#include "dirname.h"
+
+/* Return the leading directories part of PATH,
+ allocated with malloc. If out of memory, return 0.
+ Assumes that trailing slashes have already been
+ removed. */
+
+char *
+dir_name (const char *path)
+{
+ char *newpath;
+ char *slash;
+ int length; /* Length of result, not including NUL. */
+
+ slash = strrchr (path, '/');
+ if (slash == 0)
+ {
+ /* File is in the current directory. */
+ path = "";
+ length = 0;
+ }
+ else
+ {
+ /* Remove any trailing slashes from the result.
+ while (slash > path && *slash == '/')
+ --slash; */
+
+ length = slash - path + 1;
+ }
+ newpath = (char *) xmalloc (length + 1);
+ if (newpath == 0)
+ return 0;
+ strncpy (newpath, path, length);
+ newpath[length] = 0;
+ return newpath;
+}
diff --git a/neon/lib/dirname.h b/neon/lib/dirname.h
new file mode 100644
index 000000000..fc4669960
--- /dev/null
+++ b/neon/lib/dirname.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 1998 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef DIRNAME_H_
+# define DIRNAME_H_ 1
+
+# ifndef PARAMS
+# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
+# define PARAMS(Args) Args
+# else
+# define PARAMS(Args) ()
+# endif
+# endif
+
+char *
+dir_name PARAMS ((const char *path));
+
+#endif /* not DIRNAME_H_ */
diff --git a/neon/lib/fnmatch.c b/neon/lib/fnmatch.c
new file mode 100644
index 000000000..4bc7cd9f8
--- /dev/null
+++ b/neon/lib/fnmatch.c
@@ -0,0 +1,212 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ NOTE: The canonical source of this file is maintained with the GNU C
+ Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <ctype.h>
+
+#if defined (STDC_HEADERS) || !defined (isascii)
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#define ISUPPER(c) (ISASCII (c) && isupper (c))
+
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+# ifndef errno
+extern int errno;
+# endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ it matches, nonzero if not. */
+int
+fnmatch (pattern, string, flags)
+ const char *pattern;
+ const char *string;
+ int flags;
+{
+ register const char *p = pattern, *n = string;
+ register char c;
+
+/* Note that this evalutes C many times. */
+# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
+
+ while ((c = *p++) != '\0')
+ {
+ c = FOLD (c);
+
+ switch (c)
+ {
+ case '?':
+ if (*n == '\0')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_FILE_NAME) && *n == '/')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+ break;
+
+ case '\\':
+ if (!(flags & FNM_NOESCAPE))
+ {
+ c = *p++;
+ c = FOLD (c);
+ }
+ if (FOLD (*n) != c)
+ return FNM_NOMATCH;
+ break;
+
+ case '*':
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+ if (((flags & FNM_FILE_NAME) && *n == '/') ||
+ (c == '?' && *n == '\0'))
+ return FNM_NOMATCH;
+
+ if (c == '\0')
+ return 0;
+
+ {
+ char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+ c1 = FOLD (c1);
+ for (--p; *n != '\0'; ++n)
+ if ((c == '[' || FOLD (*n) == c1) &&
+ fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+ return 0;
+ return FNM_NOMATCH;
+ }
+
+ case '[':
+ {
+ /* Nonzero if the sense of the character class is inverted. */
+ register int not;
+
+ if (*n == '\0')
+ return FNM_NOMATCH;
+
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ not = (*p == '!' || *p == '^');
+ if (not)
+ ++p;
+
+ c = *p++;
+ for (;;)
+ {
+ register char cstart = c, cend = c;
+
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ cstart = cend = *p++;
+
+ cstart = cend = FOLD (cstart);
+
+ if (c == '\0')
+ /* [ (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ c = FOLD (c);
+
+ if ((flags & FNM_FILE_NAME) && c == '/')
+ /* [/] can never match. */
+ return FNM_NOMATCH;
+
+ if (c == '-' && *p != ']')
+ {
+ cend = *p++;
+ if (!(flags & FNM_NOESCAPE) && cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return FNM_NOMATCH;
+ cend = FOLD (cend);
+
+ c = *p++;
+ }
+
+ if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
+ goto matched;
+
+ if (c == ']')
+ break;
+ }
+ if (!not)
+ return FNM_NOMATCH;
+ break;
+
+ matched:;
+ /* Skip the rest of the [...] that already matched. */
+ while (c != ']')
+ {
+ if (c == '\0')
+ /* [... (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ /* XXX 1003.2d11 is unclear if this is right. */
+ ++p;
+ }
+ if (not)
+ return FNM_NOMATCH;
+ }
+ break;
+
+ default:
+ if (c != FOLD (*n))
+ return FNM_NOMATCH;
+ }
+
+ ++n;
+ }
+
+ if (*n == '\0')
+ return 0;
+
+ if ((flags & FNM_LEADING_DIR) && *n == '/')
+ /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
+ return 0;
+
+ return FNM_NOMATCH;
+
+# undef FOLD
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
diff --git a/neon/lib/fnmatch.h b/neon/lib/fnmatch.h
new file mode 100644
index 000000000..af1dcf523
--- /dev/null
+++ b/neon/lib/fnmatch.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+NOTE: The canonical source of this file is maintained with the GNU C Library.
+Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _FNMATCH_H
+
+#define _FNMATCH_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef __P
+#define __P(protos) protos
+#else /* Not C++ or ANSI C. */
+#undef __P
+#define __P(protos) ()
+/* We can get away without defining `const' here only because in this file
+ it is used only inside the prototype for `fnmatch', which is elided in
+ non-ANSI C where `const' is problematical. */
+#endif /* C++ or ANSI C. */
+
+
+/* We #undef these before defining them because some losing systems
+ (HP-UX A.08.07 for example) define these in <unistd.h>. */
+#undef FNM_PATHNAME
+#undef FNM_NOESCAPE
+#undef FNM_PERIOD
+
+/* Bits set in the FLAGS argument to `fnmatch'. */
+#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
+#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
+#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
+
+#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE)
+#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
+#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
+#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN. */
+#define FNM_NOMATCH 1
+
+/* Match STRING against the filename pattern PATTERN,
+ returning zero if it matches, FNM_NOMATCH if not. */
+extern int fnmatch __P ((const char *__pattern, const char *__string,
+ int __flags));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/neon/lib/getopt.c b/neon/lib/getopt.c
new file mode 100644
index 000000000..6557bc1c3
--- /dev/null
+++ b/neon/lib/getopt.c
@@ -0,0 +1,1052 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to drepper@gnu.org
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
+ Free Software Foundation, Inc.
+
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+# ifndef const
+# define const
+# endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+# define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library. */
+
+#ifdef VMS
+# include <unixlib.h>
+# if HAVE_STRING_H - 0
+# include <string.h>
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+# ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid) gettext (msgid)
+# else
+# define _(msgid) (msgid)
+# endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+# include <string.h>
+# define my_index strchr
+#else
+
+# if HAVE_STRING_H
+# include <string.h>
+# else
+# include <strings.h>
+# endif
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+ is valid for the getopt call we must make sure that the ARGV passed
+ to getopt is that one passed to the process. */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+ /* XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+ original_argc = argc;
+ original_argv = argv;
+}
+# ifdef text_set_element
+text_set_element (__libc_subinit, store_args_and_env);
+# endif /* text_set_element */
+
+# define SWAP_FLAGS(ch1, ch2) \
+ if (nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+#if defined __STDC__ && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#ifdef _LIBC
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = xmalloc (top + 1);
+ if (new_str == NULL)
+ nonoption_flags_len = nonoption_flags_max_len = 0;
+ else
+ {
+ memset (__mempcpy (new_str, __getopt_nonoption_flags,
+ nonoption_flags_max_len),
+ '\0', top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#ifdef _LIBC
+ if (posixly_correct == NULL
+ && argc == original_argc && argv == original_argv)
+ {
+ if (nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen (orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) xmalloc (nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+ '\0', nonoption_flags_max_len - len);
+ }
+ }
+ nonoption_flags_len = nonoption_flags_max_len;
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (optind == 0 || !__getopt_initialized)
+ {
+ if (optind == 0)
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring);
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#ifdef _LIBC
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/neon/lib/getopt.h b/neon/lib/getopt.h
new file mode 100644
index 000000000..c4adc30bb
--- /dev/null
+++ b/neon/lib/getopt.h
@@ -0,0 +1,133 @@
+/* Declarations for getopt.
+ Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* getopt.h */
diff --git a/neon/lib/getopt1.c b/neon/lib/getopt1.c
new file mode 100644
index 000000000..4ce10655d
--- /dev/null
+++ b/neon/lib/getopt1.c
@@ -0,0 +1,190 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+ Free Software Foundation, Inc.
+
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/neon/lib/linelen.c b/neon/lib/linelen.c
new file mode 100644
index 000000000..15c6ebe55
--- /dev/null
+++ b/neon/lib/linelen.c
@@ -0,0 +1,57 @@
+/* Line length calculation routine.
+ Taken from fileutils-4.0i/src/ls.c
+ Copyright (C) 85, 88, 90, 91, 1995-1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Id: linelen.c,v 1.1 1999/06/25 19:26:46 joe Exp
+ */
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef GWINSZ_IN_SYS_IOCTL
+# include <sys/ioctl.h>
+#endif
+
+#ifdef WINSIZE_IN_PTEM
+# include <sys/stream.h>
+# include <sys/ptem.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* For STDOUT_FILENO */
+#endif
+
+#define DEFAULT_LINE_LENGTH 80
+
+int get_line_length( void ) {
+ int ret = DEFAULT_LINE_LENGTH;
+
+/* joe: Some systems do this with a 'struct ttysize'?
+ * There is code in tin to do it, but tin has a dodgy license.
+ * DIY, if you have such a system.
+ */
+
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+
+ if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
+ ret = ws.ws_col;
+
+#endif
+ return ret;
+}
diff --git a/neon/lib/netrc.c b/neon/lib/netrc.c
new file mode 100644
index 000000000..b6af1c323
--- /dev/null
+++ b/neon/lib/netrc.c
@@ -0,0 +1,423 @@
+/* netrc.c -- parse the .netrc file to get hosts, accounts, and passwords
+
+ Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ For license terms, see the file COPYING in this directory.
+
+ Compile with -DSTANDALONE to test this module. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <config.h>
+#include "netrc.h"
+
+#include "xalloc.h"
+
+/* Normally defined in xmalloc.c */
+# define xrealloc realloc
+
+#define POPBUFSIZE BUFSIZ
+
+/* Maybe add NEWENTRY to the account information list, LIST. NEWENTRY is
+ set to a ready-to-use netrc_entry, in any event. */
+static void
+maybe_add_to_list (netrc_entry **newentry, netrc_entry **list)
+{
+ netrc_entry *a, *l;
+ a = *newentry;
+ l = *list;
+
+ /* We need an account name in order to add the entry to the list. */
+ if (a && ! a->account)
+ {
+ /* Free any allocated space. */
+ if (a->host)
+ free (a->host);
+ if (a->password)
+ free (a->password);
+ }
+ else
+ {
+ if (a)
+ {
+ /* Add the current machine into our list. */
+ a->next = l;
+ l = a;
+ }
+
+ /* Allocate a new netrc_entry structure. */
+ a = (netrc_entry *) xmalloc (sizeof (netrc_entry));
+ }
+
+ /* Zero the structure, so that it is ready to use. */
+ memset (a, 0, sizeof(*a));
+
+ /* Return the new pointers. */
+ *newentry = a;
+ *list = l;
+ return;
+}
+
+
+/* Parse FILE as a .netrc file (as described in ftp(1)), and return a
+ list of entries. NULL is returned if the file could not be
+ parsed. */
+netrc_entry *
+parse_netrc (file)
+ char *file;
+{
+ FILE *fp;
+ char buf[POPBUFSIZE+1], *p, *tok;
+ const char *premature_token;
+ netrc_entry *current, *retval;
+ int ln;
+
+ /* The latest token we've seen in the file. */
+ enum
+ {
+ tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
+ } last_token = tok_nothing;
+
+ current = retval = NULL;
+
+ fp = fopen (file, "r");
+ if (!fp)
+ {
+ /* Just return NULL if we can't open the file. */
+ return NULL;
+ }
+
+ /* Initialize the file data. */
+ ln = 0;
+ premature_token = NULL;
+
+ /* While there are lines in the file... */
+ while (fgets(buf, POPBUFSIZE, fp))
+ {
+ ln++;
+
+ /* Strip trailing CRLF */
+ for (p = buf + strlen(buf) - 1; (p >= buf) && isspace((unsigned)*p); p--)
+ *p = '\0';
+
+ /* Parse the line. */
+ p = buf;
+
+ /* If the line is empty... */
+ if (!*p) {
+ if (last_token == tok_macdef) { /* end of macro */
+ last_token = tok_nothing;
+ } else {
+ continue; /* otherwise ignore it */
+ }
+ }
+
+ /* If we are defining macros, then skip parsing the line. */
+ while (*p && last_token != tok_macdef)
+ {
+ char quote_char = 0;
+ char *pp;
+
+ /* Skip any whitespace. */
+ while (*p && isspace ((unsigned)*p))
+ p++;
+
+ /* Discard end-of-line comments. */
+ if (*p == '#')
+ break;
+
+ tok = pp = p;
+
+ /* Find the end of the token. */
+ while (*p && (quote_char || !isspace ((unsigned)*p)))
+ {
+ if (quote_char)
+ {
+ if (quote_char == *p)
+ {
+ quote_char = 0;
+ p ++;
+ }
+ else
+ {
+ *pp = *p;
+ p ++;
+ pp ++;
+ }
+ }
+ else
+ {
+ if (*p == '"' || *p == '\'')
+ quote_char = *p;
+ else
+ {
+ *pp = *p;
+ pp ++;
+ }
+ p ++;
+ }
+ }
+ /* Null-terminate the token, if it isn't already. */
+ if (*p)
+ *p ++ = '\0';
+ *pp = 0;
+
+ switch (last_token)
+ {
+ case tok_login:
+ if (current)
+ current->account = (char *) xstrdup (tok);
+ else
+ premature_token = "login";
+ break;
+
+ case tok_machine:
+ /* Start a new machine entry. */
+ maybe_add_to_list (&current, &retval);
+ current->host = (char *) xstrdup (tok);
+ break;
+
+ case tok_password:
+ if (current)
+ current->password = (char *) xstrdup (tok);
+ else
+ premature_token = "password";
+ break;
+
+ /* We handle most of tok_macdef above. */
+ case tok_macdef:
+ if (!current)
+ premature_token = "macdef";
+ break;
+
+ /* We don't handle the account keyword at all. */
+ case tok_account:
+ if (!current)
+ premature_token = "account";
+ break;
+
+ /* We handle tok_nothing below this switch. */
+ case tok_nothing:
+ break;
+ }
+
+ if (premature_token)
+ {
+#ifdef HAVE_ERROR
+ error_at_line (0, file, ln,
+ "warning: found \"%s\" before any host names",
+ premature_token);
+#else
+ fprintf (stderr,
+ "%s:%d: warning: found \"%s\" before any host names\n",
+ file, ln, premature_token);
+#endif
+ premature_token = NULL;
+ }
+
+ if (last_token != tok_nothing)
+ /* We got a value, so reset the token state. */
+ last_token = tok_nothing;
+ else
+ {
+ /* Fetch the next token. */
+ if (!strcmp (tok, "default"))
+ {
+ maybe_add_to_list (&current, &retval);
+ }
+ else if (!strcmp (tok, "login"))
+ last_token = tok_login;
+
+ else if (!strcmp (tok, "user"))
+ last_token = tok_login;
+
+ else if (!strcmp (tok, "macdef"))
+ last_token = tok_macdef;
+
+ else if (!strcmp (tok, "machine"))
+ last_token = tok_machine;
+
+ else if (!strcmp (tok, "password"))
+ last_token = tok_password;
+
+ else if (!strcmp (tok, "passwd"))
+ last_token = tok_password;
+
+ else if (!strcmp (tok, "account"))
+ last_token = tok_account;
+
+ else
+ {
+ fprintf (stderr, "%s:%d: warning: unknown token \"%s\"\n",
+ file, ln, tok);
+ }
+ }
+ }
+ }
+
+ fclose (fp);
+
+ /* Finalize the last machine entry we found. */
+ maybe_add_to_list (&current, &retval);
+ free (current);
+
+ /* Reverse the order of the list so that it appears in file order. */
+ current = retval;
+ retval = NULL;
+ while (current)
+ {
+ netrc_entry *saved_reference;
+
+ /* Change the direction of the pointers. */
+ saved_reference = current->next;
+ current->next = retval;
+
+ /* Advance to the next node. */
+ retval = current;
+ current = saved_reference;
+ }
+
+ return retval;
+}
+
+
+/* Return the netrc entry from LIST corresponding to HOST. NULL is
+ returned if no such entry exists. */
+netrc_entry *
+search_netrc (list, host)
+ netrc_entry *list;
+ const char *host;
+{
+ /* Look for the HOST in LIST. */
+ while (list)
+ {
+ if (!list->host)
+ /* We hit the default entry. */
+ break;
+
+ else if (!strcmp (list->host, host))
+ /* We found a matching entry. */
+ break;
+
+ list = list->next;
+ }
+
+ /* Return the matching entry, or NULL. */
+ return list;
+}
+
+
+#ifdef STANDALONE
+#include <sys/types.h>
+#include <sys/stat.h>
+
+extern int errno;
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ struct stat sb;
+ char *program_name, *file, *target;
+ netrc_entry *head, *a;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "Usage: %s NETRC [HOSTNAME]...\n", argv[0]);
+ exit (1);
+ }
+
+ program_name = argv[0];
+ file = argv[1];
+ target = argv[2];
+
+ if (stat (file, &sb))
+ {
+ fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
+ strerror (errno));
+ exit (1);
+ }
+
+ head = parse_netrc (file);
+ if (!head)
+ {
+ fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
+ exit (1);
+ }
+
+ if (argc > 2)
+ {
+ int i, status;
+ status = 0;
+ for (i = 2; i < argc; i++)
+ {
+ /* Print out the host that we are checking for. */
+ fputs (argv[i], stdout);
+
+ a = search_netrc (head, argv[i]);
+ if (a)
+ {
+ /* Print out the account and password (if any). */
+ fputc (' ', stdout);
+ fputs (a->account, stdout);
+ if (a->password)
+ {
+ fputc (' ', stdout);
+ fputs (a->password, stdout);
+ }
+ }
+ else
+ status = 1;
+
+ fputc ('\n', stdout);
+ }
+ exit (status);
+ }
+
+ /* Print out the entire contents of the netrc. */
+ a = head;
+ while (a)
+ {
+ /* Print the host name. */
+ if (a->host)
+ fputs (a->host, stdout);
+ else
+ fputs ("DEFAULT", stdout);
+
+ fputc (' ', stdout);
+
+ /* Print the account name. */
+ fputs (a->account, stdout);
+
+ if (a->password)
+ {
+ /* Print the password, if there is any. */
+ fputc (' ', stdout);
+ fputs (a->password, stdout);
+ }
+
+ fputc ('\n', stdout);
+ a = a->next;
+ }
+
+ exit (0);
+}
+#endif /* STANDALONE */
diff --git a/neon/lib/netrc.h b/neon/lib/netrc.h
new file mode 100644
index 000000000..66916b0e1
--- /dev/null
+++ b/neon/lib/netrc.h
@@ -0,0 +1,65 @@
+/* netrc.h -- declarations for netrc.c
+ Copyright (C) 1996, Free Software Foundation, Inc.
+ Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _NETRC_H_
+#define _NETRC_H_ 1
+
+# undef __BEGIN_DECLS
+# undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+#undef __P
+#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) || defined(__cplusplus)
+# define __P(protos) protos
+#else
+# define __P(protos) ()
+#endif
+
+/* The structure used to return account information from the .netrc. */
+typedef struct _netrc_entry {
+ /* The exact host name given in the .netrc, NULL if default. */
+ char *host;
+
+ /* The name of the account. */
+ char *account;
+
+ /* Password for the account (NULL, if none). */
+ char *password;
+
+ /* Pointer to the next entry in the list. */
+ struct _netrc_entry *next;
+} netrc_entry;
+
+__BEGIN_DECLS
+/* Parse FILE as a .netrc file (as described in ftp(1)), and return a
+ list of entries. NULL is returned if the file could not be
+ parsed. */
+netrc_entry *parse_netrc __P((char *file));
+
+/* Return the netrc entry from LIST corresponding to HOST. NULL is
+ returned if no such entry exists. */
+netrc_entry *search_netrc __P((netrc_entry *list, const char *host));
+__END_DECLS
+
+#endif /* _NETRC_H_ */
diff --git a/neon/lib/rpmatch.c b/neon/lib/rpmatch.c
new file mode 100644
index 000000000..aff3832ca
--- /dev/null
+++ b/neon/lib/rpmatch.c
@@ -0,0 +1,88 @@
+/* Determine whether string value is affirmation or negative response
+ according to current locale's data.
+ Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if STDC_HEADERS || _LIBC
+# include <stddef.h>
+# include <stdlib.h>
+#else
+# ifndef NULL
+# define NULL 0
+# endif
+#endif
+
+#if defined(ENABLE_NLS) && defined(HAVE_REGEX_H)
+#define RP_USE_REGEX
+#endif
+
+#ifdef RP_USE_REGEX
+# include <sys/types.h>
+# include <regex.h>
+# include <libintl.h>
+# define _(Text) gettext (Text)
+
+static int
+try (const char *response, const char *pattern, const int match,
+ const int nomatch, const char **lastp, regex_t *re)
+{
+ if (pattern != *lastp)
+ {
+ /* The pattern has changed. */
+ if (*lastp)
+ {
+ /* Free the old compiled pattern. */
+ regfree (re);
+ *lastp = NULL;
+ }
+ /* Compile the pattern and cache it for future runs. */
+ if (regcomp (re, pattern, REG_EXTENDED) != 0)
+ return -1;
+ *lastp = pattern;
+ }
+
+ /* See if the regular expression matches RESPONSE. */
+ return regexec (re, response, 0, NULL, 0) == 0 ? match : nomatch;
+}
+#endif
+
+
+int
+rpmatch (const char *response)
+{
+#ifdef RP_USE_REGEX
+ /* Match against one of the response patterns, compiling the pattern
+ first if necessary. */
+
+ /* We cache the response patterns and compiled regexps here. */
+ static const char *yesexpr, *noexpr;
+ static regex_t yesre, nore;
+ int result;
+
+ return ((result = try (response, _("^[yY]"), 1, 0,
+ &yesexpr, &yesre))
+ ? result
+ : try (response, _("^[nN]"), 0, -1, &noexpr, &nore));
+#else
+ /* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */
+ return (*response == 'y' || *response == 'Y' ? 1
+ : *response == 'n' || *response == 'N' ? 0 : -1);
+#endif
+}
diff --git a/neon/lib/snprintf.c b/neon/lib/snprintf.c
new file mode 100644
index 000000000..49841177b
--- /dev/null
+++ b/neon/lib/snprintf.c
@@ -0,0 +1,835 @@
+
+/*
+ Unix snprintf implementation.
+ Version 1.1
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+ sitecopy changes:
+ renamed dtoa -> doubletoa to avoid dtoa conflict with cygwin.
+
+
+ 1.1:
+ * added changes from Miles Bader
+ * corrected a bug with %f
+ * added support for %#g
+ * added more comments :-)
+ 1.0:
+ * supporting must ANSI syntaxic_sugars
+ 0.0:
+ * suppot %s %c %d
+
+ THANKS(for the patches and ideas):
+ Miles Bader
+ Cyrille Rustom
+ Jacek Slabocewiz
+ Mike Parker(mouse)
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "snprintf.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h> /* for strlen() */
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* for atoi() */
+#endif
+#include <ctype.h>
+
+
+/*
+ * Find the nth power of 10
+ */
+PRIVATE double
+#ifdef __STDC__
+pow_10(int n)
+#else
+pow_10(n)
+int n;
+#endif
+{
+ int i;
+ double P;
+
+ if (n < 0)
+ for (i = 1, P = 1., n = -n ; i <= n ; i++) {P *= .1;}
+ else
+ for (i = 1, P = 1. ; i <= n ; i++) {P *= 10.0;}
+ return P;
+}
+
+/*
+ * Find the integral part of the log in base 10
+ * Note: this not a real log10()
+ I just need and approximation(integerpart) of x in:
+ 10^x ~= r
+ * log_10(200) = 2;
+ * log_10(250) = 2;
+ */
+PRIVATE int
+#ifdef __STDC__
+log_10(double r)
+#else
+log_10(r)
+double r;
+#endif
+{
+ int i = 0;
+ double result = 1.;
+
+ if (r < 0.)
+ r = -r;
+
+ if (r < 1.) {
+ while (result >= r) {result *= .1; i++;}
+ return (-i);
+ } else {
+ while (result <= r) {result *= 10.; i++;}
+ return (i - 1);
+ }
+}
+
+/*
+ * This function return the fraction part of a double
+ * and set in ip the integral part.
+ * In many ways it resemble the modf() found on most Un*x
+ */
+PRIVATE double
+#ifdef __STDC__
+integral(double real, double * ip)
+#else
+integral(real, ip)
+double real;
+double * ip;
+#endif
+{
+ int j;
+ double i, s, p;
+ double real_integral = 0.;
+
+/* take care of the obvious */
+/* equal to zero ? */
+ if (real == 0.) {
+ *ip = 0.;
+ return (0.);
+ }
+
+/* negative number ? */
+ if (real < 0.)
+ real = -real;
+
+/* a fraction ? */
+ if ( real < 1.) {
+ *ip = 0.;
+ return real;
+ }
+/* the real work :-) */
+ for (j = log_10(real); j >= 0; j--) {
+ p = pow_10(j);
+ s = (real - real_integral)/p;
+ i = 0.;
+ while (i + 1. <= s) {i++;}
+ real_integral += i*p;
+ }
+ *ip = real_integral;
+ return (real - real_integral);
+}
+
+#define PRECISION 1.e-6
+/*
+ * return an ascii representation of the integral part of the number
+ * and set fract to be an ascii representation of the fraction part
+ * the container for the fraction and the integral part or staticly
+ * declare with fix size
+ */
+PRIVATE char *
+#ifdef __STDC__
+numtoa(double number, int base, int precision, char ** fract)
+#else
+numtoa(number, base, precision, fract)
+double number;
+int base;
+int precision;
+char ** fract;
+#endif
+{
+ register int i, j;
+ double ip, fp; /* integer and fraction part */
+ double fraction;
+ int digits = MAX_INT - 1;
+ static char integral_part[MAX_INT];
+ static char fraction_part[MAX_FRACT];
+ double sign;
+ int ch;
+
+/* taking care of the obvious case: 0.0 */
+ if (number == 0.) {
+ integral_part[0] = '0';
+ integral_part[1] = '\0';
+ fraction_part[0] = '0';
+ fraction_part[1] = '\0';
+ return integral_part;
+ }
+
+/* for negative numbers */
+ if ((sign = number) < 0.) {
+ number = -number;
+ digits--; /* sign consume one digit */
+ }
+
+ fraction = integral(number, &ip);
+ number = ip;
+/* do the integral part */
+ if ( ip == 0.) {
+ integral_part[0] = '0';
+ i = 1;
+ } else {
+ for ( i = 0; i < digits && number != 0.; ++i) {
+ number /= base;
+ fp = integral(number, &ip);
+ ch = (int)((fp + PRECISION)*base); /* force to round */
+ integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
+ if (! isxdigit(integral_part[i])) /* bail out overflow !! */
+ break;
+ number = ip;
+ }
+ }
+
+/* Oh No !! out of bound, ho well fill it up ! */
+ if (number != 0.)
+ for (i = 0; i < digits; ++i)
+ integral_part[i] = '9';
+
+/* put the sign ? */
+ if (sign < 0.)
+ integral_part[i++] = '-';
+
+ integral_part[i] = '\0';
+
+/* reverse every thing */
+ for ( i--, j = 0; j < i; j++, i--)
+ SWAP_INT(integral_part[i], integral_part[j]);
+
+/* the fractionnal part */
+ for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
+ fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
+ if (! isdigit(fraction_part[i])) /* underflow ? */
+ break;
+ fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
+ }
+ fraction_part[i] = '\0';
+
+ if (fract != (char **)0)
+ *fract = fraction_part;
+
+ return integral_part;
+
+}
+
+/* for %d and friends, it puts in holder
+ * the representation with the right padding
+ */
+PRIVATE void
+#ifdef __STDC__
+decimal(struct DATA *p, double d)
+#else
+decimal(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+ char *tmp;
+
+ tmp = itoa(d);
+ p->width -= strlen(tmp);
+ PAD_RIGHT(p);
+ PUT_PLUS(d, p);
+ PUT_SPACE(d, p);
+ while (*tmp) { /* the integral */
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+ PAD_LEFT(p);
+}
+
+/* for %o octal representation */
+PRIVATE void
+#ifdef __STDC__
+octal(struct DATA *p, double d)
+#else
+octal(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+ char *tmp;
+
+ tmp = otoa(d);
+ p->width -= strlen(tmp);
+ PAD_RIGHT(p);
+ if (p->square == FOUND) /* had prefix '0' for octal */
+ PUT_CHAR('0', p);
+ while (*tmp) { /* octal */
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+ PAD_LEFT(p);
+}
+
+/* for %x %X hexadecimal representation */
+PRIVATE void
+#ifdef __STDC__
+hexa(struct DATA *p, double d)
+#else
+hexa(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+ char *tmp;
+
+ tmp = htoa(d);
+ p->width -= strlen(tmp);
+ PAD_RIGHT(p);
+ if (p->square == FOUND) { /* prefix '0x' for hexa */
+ PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
+ }
+ while (*tmp) { /* hexa */
+ PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
+ tmp++;
+ }
+ PAD_LEFT(p);
+}
+
+/* %s strings */
+PRIVATE void
+#ifdef __STDC__
+strings(struct DATA *p, char *tmp)
+#else
+strings(p, tmp)
+struct DATA *p;
+char *tmp;
+#endif
+{
+ int i;
+
+ i = strlen(tmp);
+ if (p->precision != NOT_FOUND) /* the smallest number */
+ i = (i < p->precision ? i : p->precision);
+ p->width -= i;
+ PAD_RIGHT(p);
+ while (i-- > 0) { /* put the sting */
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+ PAD_LEFT(p);
+}
+
+/* %f or %g floating point representation */
+PRIVATE void
+#ifdef __STDC__
+floating(struct DATA *p, double d)
+#else
+floating(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+ char *tmp, *tmp2;
+ int i;
+
+ DEF_PREC(p);
+ d = ROUND(d, p);
+ tmp = doubletoa(d, p->precision, &tmp2);
+ /* calculate the padding. 1 for the dot */
+ p->width = p->width -
+ ((d > 0. && p->justify == RIGHT) ? 1:0) -
+ ((p->space == FOUND) ? 1:0) -
+ strlen(tmp) - p->precision - 1;
+ PAD_RIGHT(p);
+ PUT_PLUS(d, p);
+ PUT_SPACE(d, p);
+ while (*tmp) { /* the integral */
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+ if (p->precision != 0 || p->square == FOUND)
+ PUT_CHAR('.', p); /* put the '.' */
+ if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
+ for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
+ tmp2[i] = '\0';
+ for (; *tmp2; tmp2++)
+ PUT_CHAR(*tmp2, p); /* the fraction */
+
+ PAD_LEFT(p);
+}
+
+/* %e %E %g exponent representation */
+PRIVATE void
+#ifdef __STDC__
+exponent(struct DATA *p, double d)
+#else
+exponent(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+ char *tmp, *tmp2;
+ int j, i;
+
+ DEF_PREC(p);
+ j = log_10(d);
+ d = d / pow_10(j); /* get the Mantissa */
+ d = ROUND(d, p);
+ tmp = doubletoa(d, p->precision, &tmp2);
+ /* 1 for unit, 1 for the '.', 1 for 'e|E',
+ * 1 for '+|-', 3 for 'exp' */
+ /* calculate how much padding need */
+ p->width = p->width -
+ ((d > 0. && p->justify == RIGHT) ? 1:0) -
+ ((p->space == FOUND) ? 1:0) - p->precision - 7;
+ PAD_RIGHT(p);
+ PUT_PLUS(d, p);
+ PUT_SPACE(d, p);
+ while (*tmp) {/* the integral */
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+ if (p->precision != 0 || p->square == FOUND)
+ PUT_CHAR('.', p); /* the '.' */
+ if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
+ for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
+ tmp2[i] = '\0';
+ for (; *tmp2; tmp2++)
+ PUT_CHAR(*tmp2, p); /* the fraction */
+
+ if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
+ PUT_CHAR('e', p);
+ } else
+ PUT_CHAR('E', p);
+ if (j > 0) { /* the sign of the exp */
+ PUT_CHAR('+', p);
+ } else {
+ PUT_CHAR('-', p);
+ j = -j;
+ }
+ tmp = itoa((double)j);
+ if (j < 9) { /* need to pad the exponent with 0 '000' */
+ PUT_CHAR('0', p); PUT_CHAR('0', p);
+ } else if (j < 99)
+ PUT_CHAR('0', p);
+ while (*tmp) { /* the exponent */
+ PUT_CHAR(*tmp, p);
+ tmp++;
+ }
+ PAD_LEFT(p);
+}
+
+/* initialize the conversion specifiers */
+PRIVATE void
+#ifdef __STDC__
+conv_flag(char * s, struct DATA * p)
+#else
+conv_flag(s, p)
+char * s;
+struct DATA * p;
+#endif
+{
+ char number[MAX_FIELD/2];
+ int i;
+
+ p->precision = p->width = NOT_FOUND;
+ p->star_w = p->star_p = NOT_FOUND;
+ p->square = p->space = NOT_FOUND;
+ p->a_long = p->justify = NOT_FOUND;
+ p->pad = ' ';
+
+ for(;s && *s ;s++) {
+ switch(*s) {
+ case ' ': p->space = FOUND; break;
+ case '#': p->square = FOUND; break;
+ case '*': if (p->width == NOT_FOUND)
+ p->width = p->star_w = FOUND;
+ else
+ p->precision = p->star_p = FOUND;
+ break;
+ case '+': p->justify = RIGHT; break;
+ case '-': p->justify = LEFT; break;
+ case '.': if (p->width == NOT_FOUND)
+ p->width = 0;
+ break;
+ case '0': p->pad = '0'; break;
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9': /* gob all the digits */
+ for (i = 0; isdigit(*s); i++, s++)
+ if (i < MAX_FIELD/2 - 1)
+ number[i] = *s;
+ number[i] = '\0';
+ if (p->width == NOT_FOUND)
+ p->width = atoi(number);
+ else
+ p->precision = atoi(number);
+ s--; /* went to far go back */
+ break;
+ }
+ }
+}
+
+PUBLIC int
+#ifdef __STDC__
+vsnprintf(char *string, size_t length, const char * format, va_list args)
+#else
+vsnprintf(string, length, format, args)
+char *string;
+size_t length;
+char * format;
+va_list args;
+#endif
+{
+ struct DATA data;
+ char conv_field[MAX_FIELD];
+ double d; /* temporary holder */
+ int state;
+ int i;
+
+ data.length = length - 1; /* leave room for '\0' */
+ data.holder = string;
+ data.pf = format;
+ data.counter = 0;
+
+
+/* sanity check, the string must be > 1 */
+ if (length < 1)
+ return -1;
+
+
+ for (; *data.pf && (data.counter < data.length); data.pf++) {
+ if ( *data.pf == '%' ) { /* we got a magic % cookie */
+ conv_flag((char *)0, &data); /* initialise format flags */
+ for (state = 1; *data.pf && state;) {
+ switch (*(++data.pf)) {
+ case '\0': /* a NULL here ? ? bail out */
+ *data.holder = '\0';
+ return data.counter;
+ break;
+ case 'f': /* float, double */
+ STAR_ARGS(&data);
+ d = va_arg(args, double);
+ floating(&data, d);
+ state = 0;
+ break;
+ case 'g':
+ case 'G':
+ STAR_ARGS(&data);
+ DEF_PREC(&data);
+ d = va_arg(args, double);
+ i = log_10(d);
+ /*
+ * for '%g|%G' ANSI: use f if exponent
+ * is in the range or [-4,p] exclusively
+ * else use %e|%E
+ */
+ if (-4 < i && i < data.precision)
+ floating(&data, d);
+ else
+ exponent(&data, d);
+ state = 0;
+ break;
+ case 'e':
+ case 'E': /* Exponent double */
+ STAR_ARGS(&data);
+ d = va_arg(args, double);
+ exponent(&data, d);
+ state = 0;
+ break;
+ case 'u':
+ case 'd': /* decimal */
+ STAR_ARGS(&data);
+ if (data.a_long == FOUND)
+ d = va_arg(args, long);
+ else
+ d = va_arg(args, int);
+ decimal(&data, d);
+ state = 0;
+ break;
+ case 'o': /* octal */
+ STAR_ARGS(&data);
+ if (data.a_long == FOUND)
+ d = va_arg(args, long);
+ else
+ d = va_arg(args, int);
+ octal(&data, d);
+ state = 0;
+ break;
+ case 'x':
+ case 'X': /* hexadecimal */
+ STAR_ARGS(&data);
+ if (data.a_long == FOUND)
+ d = va_arg(args, long);
+ else
+ d = va_arg(args, int);
+ hexa(&data, d);
+ state = 0;
+ break;
+ case 'c': /* character */
+ d = va_arg(args, int);
+ PUT_CHAR(d, &data);
+ state = 0;
+ break;
+ case 's': /* string */
+ STAR_ARGS(&data);
+ strings(&data, va_arg(args, char *));
+ state = 0;
+ break;
+ case 'n':
+ *(va_arg(args, int *)) = data.counter; /* what's the count ? */
+ state = 0;
+ break;
+ case 'l':
+ data.a_long = FOUND;
+ break;
+ case 'h':
+ break;
+ case '%': /* nothing just % */
+ PUT_CHAR('%', &data);
+ state = 0;
+ break;
+ case '#': case ' ': case '+': case '*':
+ case '-': case '.': case '0': case '1':
+ case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ /* initialize width and precision */
+ for (i = 0; isflag(*data.pf); i++, data.pf++)
+ if (i < MAX_FIELD - 1)
+ conv_field[i] = *data.pf;
+ conv_field[i] = '\0';
+ conv_flag(conv_field, &data);
+ data.pf--; /* went to far go back */
+ break;
+ default:
+ /* is this an error ? maybe bail out */
+ state = 0;
+ break;
+ } /* end switch */
+ } /* end of for state */
+ } else { /* not % */
+ PUT_CHAR(*data.pf, &data); /* add the char the string */
+ }
+ }
+
+ *data.holder = '\0'; /* the end ye ! */
+
+ return data.counter;
+}
+
+#ifndef HAVE_SNPRINTF
+
+PUBLIC int
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+snprintf(char *string, size_t length, const char * format, ...)
+#else
+snprintf(string, length, format, va_alist)
+char *string;
+size_t length;
+char * format;
+va_dcl
+#endif
+{
+ int rval;
+ va_list args;
+
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+ va_start(args, format);
+#else
+ va_start(args);
+#endif
+
+ rval = vsnprintf (string, length, format, args);
+
+ va_end(args);
+
+ return rval;
+}
+
+#endif /* HAVE_SNPRINTF */
+
+
+#ifdef DRIVER
+
+#include <stdio.h>
+
+/* set of small tests for snprintf() */
+void main()
+{
+ char holder[100];
+ int i;
+
+/*
+ printf("Suite of test for snprintf:\n");
+ printf("a_format\n");
+ printf("printf() format\n");
+ printf("snprintf() format\n\n");
+*/
+/* Checking the field widths */
+
+ printf("/%%d/, 336\n");
+ snprintf(holder, sizeof holder, "/%d/\n", 336);
+ printf("/%d/\n", 336);
+ printf("%s\n", holder);
+
+ printf("/%%2d/, 336\n");
+ snprintf(holder, sizeof holder, "/%2d/\n", 336);
+ printf("/%2d/\n", 336);
+ printf("%s\n", holder);
+
+ printf("/%%10d/, 336\n");
+ snprintf(holder, sizeof holder, "/%10d/\n", 336);
+ printf("/%10d/\n", 336);
+ printf("%s\n", holder);
+
+ printf("/%%-10d/, 336\n");
+ snprintf(holder, sizeof holder, "/%-10d/\n", 336);
+ printf("/%-10d/\n", 336);
+ printf("%s\n", holder);
+
+
+/* floating points */
+
+ printf("/%%f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
+ printf("/%f/\n", 1234.56);
+ printf("%s\n", holder);
+
+ printf("/%%e/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
+ printf("/%e/\n", 1234.56);
+ printf("%s\n", holder);
+
+ printf("/%%4.2f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
+ printf("/%4.2f/\n", 1234.56);
+ printf("%s\n", holder);
+
+ printf("/%%3.1f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
+ printf("/%3.1f/\n", 1234.56);
+ printf("%s\n", holder);
+
+ printf("/%%10.3f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
+ printf("/%10.3f/\n", 1234.56);
+ printf("%s\n", holder);
+
+ printf("/%%10.3e/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
+ printf("/%10.3e/\n", 1234.56);
+ printf("%s\n", holder);
+
+ printf("/%%+4.2f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
+ printf("/%+4.2f/\n", 1234.56);
+ printf("%s\n", holder);
+
+ printf("/%%010.2f/, 1234.56\n");
+ snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
+ printf("/%010.2f/\n", 1234.56);
+ printf("%s\n", holder);
+
+#define BLURB "Outstanding acting !"
+/* strings precisions */
+
+ printf("/%%2s/, \"%s\"\n", BLURB);
+ snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
+ printf("/%2s/\n", BLURB);
+ printf("%s\n", holder);
+
+ printf("/%%22s/ %s\n", BLURB);
+ snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
+ printf("/%22s/\n", BLURB);
+ printf("%s\n", holder);
+
+ printf("/%%22.5s/ %s\n", BLURB);
+ snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
+ printf("/%22.5s/\n", BLURB);
+ printf("%s\n", holder);
+
+ printf("/%%-22.5s/ %s\n", BLURB);
+ snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
+ printf("/%-22.5s/\n", BLURB);
+ printf("%s\n", holder);
+
+/* see some flags */
+
+ printf("%%x %%X %%#x, 31, 31, 31\n");
+ snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
+ printf("%x %X %#x\n", 31, 31, 31);
+ printf("%s\n", holder);
+
+ printf("**%%d**%% d**%% d**, 42, 42, -42\n");
+ snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
+ printf("**%d**% d**% d**\n", 42, 42, -42);
+ printf("%s\n", holder);
+
+/* other flags */
+
+ printf("/%%g/, 31.4\n");
+ snprintf(holder, sizeof holder, "/%g/\n", 31.4);
+ printf("/%g/\n", 31.4);
+ printf("%s\n", holder);
+
+ printf("/%%.6g/, 31.4\n");
+ snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
+ printf("/%.6g/\n", 31.4);
+ printf("%s\n", holder);
+
+ printf("/%%.1G/, 31.4\n");
+ snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
+ printf("/%.1G/\n", 31.4);
+ printf("%s\n", holder);
+
+ printf("abc%%n\n");
+ printf("abc%n", &i); printf("%d\n", i);
+ snprintf(holder, sizeof holder, "abc%n", &i);
+ printf("%s", holder); printf("%d\n\n", i);
+
+ printf("%%*.*s --> 10.10\n");
+ snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
+ printf("%*.*s\n", 10, 10, BLURB);
+ printf("%s\n", holder);
+
+ printf("%%%%%%%%\n");
+ snprintf(holder, sizeof holder, "%%%%\n");
+ printf("%%%%\n");
+ printf("%s\n", holder);
+
+#define BIG "Hello this is a too big string for the buffer"
+/* printf("A buffer to small of 10, trying to put this:\n");*/
+ printf("<%%>, %s\n", BIG);
+ i = snprintf(holder, 10, "%s\n", BIG);
+ printf("<%s>\n", BIG);
+ printf("<%s>\n", holder);
+}
+#endif
diff --git a/neon/lib/snprintf.h b/neon/lib/snprintf.h
new file mode 100644
index 000000000..511decabf
--- /dev/null
+++ b/neon/lib/snprintf.h
@@ -0,0 +1,245 @@
+/*
+ Unix snprintf implementation.
+ Version 1.1
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+ sitecopy changes:
+ added sys/types.h include to pick up size_t define.
+ renamed dtoa -> doubletoa to avoid dtoa conflict with cygwin.
+
+ 1.1:
+ * added changes from Miles Bader
+ * corrected a bug with %f
+ * added support for %#g
+ * added more comments :-)
+ 1.0:
+ * supporting must ANSI syntaxic_sugars(see below)
+ 0.0:
+ * suppot %s %c %d
+
+ it understands:
+ Integer:
+ %lu %lu %u
+ %hd %ld %d decimal
+ %ho %lo %o octal
+ %hx %lx %x %X hexa
+ Floating points:
+ %g %G %e %E %f double
+ Strings:
+ %s %c string
+ %% %
+
+ Formating conversion flags:
+ - justify left
+ + Justify right or put a plus if number
+ # prefix 0x, 0X for hexa and 0 for octal
+ * precision/witdth is specify as an (int) in the arguments
+ ' ' leave a blank for number with no sign
+ l the later should be a long
+ h the later should be a short
+
+format:
+ snprintf(holder, sizeof_holder, format, ...)
+
+Return values:
+ (sizeof_holder - 1)
+
+
+ THANKS(for the patches and ideas):
+ Miles Bader
+ Cyrille Rustom
+ Jacek Slabocewiz
+ Mike Parker(mouse)
+
+Alain Magloire: alainm@rcsm.ee.mcgill.ca
+*/
+
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+/**** changed for sitecopy ****/
+#include <sys/types.h>
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * For the FLOATING POINT FORMAT :
+ * the challenge was finding a way to
+ * manipulate the Real numbers without having
+ * to resort to mathematical function(it
+ * would require to link with -lm) and not
+ * going down to the bit pattern(not portable)
+ *
+ * so a number, a real is:
+
+ real = integral + fraction
+
+ integral = ... + a(2)*10^2 + a(1)*10^1 + a(0)*10^0
+ fraction = b(1)*10^-1 + b(2)*10^-2 + ...
+
+ where:
+ 0 <= a(i) => 9
+ 0 <= b(i) => 9
+
+ from then it was simple math
+ */
+
+/*
+ * size of the buffer for the integral part
+ * and the fraction part
+ */
+#define MAX_INT 99 + 1 /* 1 for the null */
+#define MAX_FRACT 29 + 1
+
+/*
+ * numtoa() uses PRIVATE buffers to store the results,
+ * So this function is not reentrant
+ */
+#define itoa(n) numtoa(n, 10, 0, (char **)0)
+#define otoa(n) numtoa(n, 8, 0, (char **)0)
+#define htoa(n) numtoa(n, 16, 0, (char **)0)
+#define doubletoa(n, p, f) numtoa(n, 10, p, f)
+
+#define SWAP_INT(a,b) {int t; t = (a); (a) = (b); (b) = t;}
+
+/* this struct holds everything we need */
+struct DATA {
+ int length;
+ char *holder;
+ int counter;
+#ifdef __STDC__
+ const char *pf;
+#else
+ char *pf;
+#endif
+/* FLAGS */
+ int width, precision;
+ int justify; char pad;
+ int square, space, star_w, star_p, a_long ;
+};
+
+#define PRIVATE static
+#define PUBLIC extern
+/* signature of the functions */
+#ifdef __STDC__
+/* the floating point stuff */
+ PRIVATE double pow_10(int);
+ PRIVATE int log_10(double);
+ PRIVATE double integral(double, double *);
+ PRIVATE char * numtoa(double, int, int, char **);
+
+/* for the format */
+ PRIVATE void conv_flag(char *, struct DATA *);
+ PRIVATE void floating(struct DATA *, double);
+ PRIVATE void exponent(struct DATA *, double);
+ PRIVATE void decimal(struct DATA *, double);
+ PRIVATE void octal(struct DATA *, double);
+ PRIVATE void hexa(struct DATA *, double);
+ PRIVATE void strings(struct DATA *, char *);
+
+#else
+/* the floating point stuff */
+ PRIVATE double pow_10();
+ PRIVATE int log_10();
+ PRIVATE double integral();
+ PRIVATE char * numtoa();
+
+/* for the format */
+ PRIVATE void conv_flag();
+ PRIVATE void floating();
+ PRIVATE void exponent();
+ PRIVATE void decimal();
+ PRIVATE void octal();
+ PRIVATE void hexa();
+ PRIVATE void strings();
+#endif
+
+/* those are defines specific to snprintf to hopefully
+ * make the code clearer :-)
+ */
+#define RIGHT 1
+#define LEFT 0
+#define NOT_FOUND -1
+#define FOUND 1
+#define MAX_FIELD 15
+
+/* the conversion flags */
+#define isflag(c) ((c) == '#' || (c) == ' ' || \
+ (c) == '*' || (c) == '+' || \
+ (c) == '-' || (c) == '.' || \
+ isdigit(c))
+
+/* round off to the precision */
+#define ROUND(d, p) \
+ (d < 0.) ? \
+ d - pow_10(-(p)->precision) * 0.5 : \
+ d + pow_10(-(p)->precision) * 0.5
+
+/* set default precision */
+#define DEF_PREC(p) \
+ if ((p)->precision == NOT_FOUND) \
+ (p)->precision = 6
+
+/* put a char */
+#define PUT_CHAR(c, p) \
+ if ((p)->counter < (p)->length) { \
+ *(p)->holder++ = (c); \
+ (p)->counter++; \
+ }
+
+#define PUT_PLUS(d, p) \
+ if ((d) > 0. && (p)->justify == RIGHT) \
+ PUT_CHAR('+', p)
+
+#define PUT_SPACE(d, p) \
+ if ((p)->space == FOUND && (d) > 0.) \
+ PUT_CHAR(' ', p)
+
+/* pad right */
+#define PAD_RIGHT(p) \
+ if ((p)->width > 0 && (p)->justify != LEFT) \
+ for (; (p)->width > 0; (p)->width--) \
+ PUT_CHAR((p)->pad, p)
+
+/* pad left */
+#define PAD_LEFT(p) \
+ if ((p)->width > 0 && (p)->justify == LEFT) \
+ for (; (p)->width > 0; (p)->width--) \
+ PUT_CHAR((p)->pad, p)
+
+/* if width and prec. in the args */
+#define STAR_ARGS(p) \
+ if ((p)->star_w == FOUND) \
+ (p)->width = va_arg(args, int); \
+ if ((p)->star_p == FOUND) \
+ (p)->precision = va_arg(args, int)
+
+
+PUBLIC int
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+snprintf(char *string, size_t length, const char * format, ...);
+#else
+snprintf(string, length, format, va_alist);
+char *string;
+size_t length;
+char * format;
+va_dcl
+#endif
+
+
+
diff --git a/neon/lib/strcasecmp.c b/neon/lib/strcasecmp.c
new file mode 100644
index 000000000..366bcf2e9
--- /dev/null
+++ b/neon/lib/strcasecmp.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+/* Compare S1 and S2, ignoring case, returning less than, equal to or
+ greater than zero if S1 is lexiographically less than,
+ equal to or greater than S2. */
+int
+strcasecmp (s1, s2)
+ const char *s1;
+ const char *s2;
+{
+ register const unsigned char *p1 = (const unsigned char *) s1;
+ register const unsigned char *p2 = (const unsigned char *) s2;
+ unsigned char c1, c2;
+
+ if (p1 == p2)
+ return 0;
+
+ do
+ {
+ c1 = tolower (*p1++);
+ c2 = tolower (*p2++);
+ if (c1 == '\0')
+ break;
+ }
+ while (c1 == c2);
+
+ return c1 - c2;
+}
diff --git a/neon/lib/yesno.c b/neon/lib/yesno.c
new file mode 100644
index 000000000..8aaaf3d4b
--- /dev/null
+++ b/neon/lib/yesno.c
@@ -0,0 +1,52 @@
+/* yesno.c -- read a yes/no response from stdin
+ Copyright (C) 1990, 1998 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#include <stdio.h>
+
+/* Read one line from standard input
+ and return nonzero if that line begins with y or Y,
+ otherwise return 0. */
+
+int rpmatch ();
+
+int
+yesno ()
+{
+ /* We make some assumptions here:
+ a) leading white space in the response are not vital
+ b) the first 128 characters of the answer are enough (the rest can
+ be ignored)
+ I cannot think for a situation where this is not ok. --drepper@gnu */
+ char buf[128];
+ int len = 0;
+ int c;
+
+ while ((c = getchar ()) != EOF && c != '\n')
+ if ((len > 0 && len < 127) || (len == 0 && !isspace (c)))
+ buf[len++] = c;
+ buf[len] = '\0';
+
+ return rpmatch (buf) == 1;
+}
diff --git a/neon/ltconfig b/neon/ltconfig
new file mode 100755
index 000000000..e3c5a9544
--- /dev/null
+++ b/neon/ltconfig
@@ -0,0 +1,2908 @@
+#! /bin/sh
+
+# ltconfig - Create a system-specific libtool.
+# Copyright (C) 1996-1999 Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# A lot of this script is taken from autoconf-2.10.
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+echo=echo
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+ # Yippee, $echo works!
+ :
+else
+ # Restart under the correct shell.
+ exec "$SHELL" "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<EOF
+$*
+EOF
+ exit 0
+fi
+
+# Find the correct PATH separator. Usually this is `:', but
+# DJGPP uses `;' like DOS.
+if test "X${PATH_SEPARATOR+set}" != "Xset"; then
+ UNAME=${UNAME-`uname 2>/dev/null`}
+ case X$UNAME in
+ *-DOS) PATH_SEPARATOR=';' ;;
+ *) PATH_SEPARATOR=':' ;;
+ esac
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "${CDPATH+set}" = set; then CDPATH=; export CDPATH; fi
+
+if test "X${echo_test_string+set}" != "Xset"; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if (echo_test_string="`eval $cmd`") 2>/dev/null &&
+ echo_test_string="`eval $cmd`" &&
+ (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null; then
+ break
+ fi
+ done
+fi
+
+if test "X`($echo '\t') 2>/dev/null`" != 'X\t' ||
+ test "X`($echo "$echo_test_string") 2>/dev/null`" != X"$echo_test_string"; then
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+ for dir in $PATH /usr/ucb; do
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ test "X`($dir/echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+ echo="$dir/echo"
+ break
+ fi
+ done
+ IFS="$save_ifs"
+
+ if test "X$echo" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' &&
+ test "X`(print -r "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ echo='print -r'
+ elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running ltconfig again with it.
+ ORIGINAL_CONFIG_SHELL="${CONFIG_SHELL-/bin/sh}"
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" --no-reexec ${1+"$@"}
+ else
+ # Try using printf.
+ echo='printf "%s\n"'
+ if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+ test "X`($echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif test "X`("$ORIGINAL_CONFIG_SHELL" "$0" --fallback-echo '\t') 2>/dev/null`" = 'X\t' &&
+ test "X`("$ORIGINAL_CONFIG_SHELL" "$0" --fallback-echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+ CONFIG_SHELL="$ORIGINAL_CONFIG_SHELL"
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ echo="$CONFIG_SHELL $0 --fallback-echo"
+ elif test "X`("$CONFIG_SHELL" "$0" --fallback-echo '\t') 2>/dev/null`" = 'X\t' &&
+ test "X`("$CONFIG_SHELL" "$0" --fallback-echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+ echo="$CONFIG_SHELL $0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+ if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null; then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "$0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec "${ORIGINAL_CONFIG_SHELL}" "$0" ${1+"$@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ echo=echo
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e s/^X//'
+sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# The name of this program.
+progname=`$echo "X$0" | $Xsed -e 's%^.*/%%'`
+
+# Constants:
+PROGRAM=ltconfig
+PACKAGE=libtool
+VERSION=1.3
+TIMESTAMP=" (1.385.2.117 1999/04/29 13:07:13)"
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.c 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.c $LIBS 1>&5'
+rm="rm -f"
+
+help="Try \`$progname --help' for more information."
+
+# Global variables:
+default_ofile=libtool
+can_build_shared=yes
+enable_shared=yes
+# All known linkers require a `.a' archive for static linking.
+enable_static=yes
+enable_fast_install=yes
+enable_dlopen=unknown
+enable_win32_dll=no
+ltmain=
+silent=
+srcdir=
+ac_config_guess=
+ac_config_sub=
+host=
+nonopt=
+ofile="$default_ofile"
+verify_host=yes
+with_gcc=no
+with_gnu_ld=no
+need_locks=yes
+ac_ext=c
+objext=o
+libext=a
+cache_file=
+
+old_AR="$AR"
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
+old_LDFLAGS="$LDFLAGS"
+old_LD="$LD"
+old_LN_S="$LN_S"
+old_LIBS="$LIBS"
+old_NM="$NM"
+old_RANLIB="$RANLIB"
+old_DLLTOOL="$DLLTOOL"
+old_OBJDUMP="$OBJDUMP"
+old_AS="$AS"
+
+# Parse the command line options.
+args=
+prev=
+for option
+do
+ case "$option" in
+ -*=*) optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ eval "$prev=\$option"
+ prev=
+ continue
+ fi
+
+ case "$option" in
+ --help) cat <<EOM
+Usage: $progname [OPTION]... [HOST [LTMAIN]]
+
+Generate a system-specific libtool script.
+
+ --debug enable verbose shell tracing
+ --disable-shared do not build shared libraries
+ --disable-static do not build static libraries
+ --disable-fast-install do not optimize for fast installation
+ --enable-dlopen enable dlopen support
+ --enable-win32-dll enable building dlls on win32 hosts
+ --help display this help and exit
+ --no-verify do not verify that HOST is a valid host type
+-o, --output=FILE specify the output file [default=$default_ofile]
+ --quiet same as \`--silent'
+ --silent do not print informational messages
+ --srcdir=DIR find \`config.guess' in DIR
+ --version output version information and exit
+ --with-gcc assume that the GNU C compiler will be used
+ --with-gnu-ld assume that the C compiler uses the GNU linker
+ --disable-lock disable file locking
+ --cache-file=FILE configure cache file
+
+LTMAIN is the \`ltmain.sh' shell script fragment or \`ltmain.c' program
+that provides basic libtool functionality.
+
+HOST is the canonical host system name [default=guessed].
+EOM
+ exit 0
+ ;;
+
+ --debug)
+ echo "$progname: enabling shell trace mode"
+ set -x
+ ;;
+
+ --disable-shared) enable_shared=no ;;
+
+ --disable-static) enable_static=no ;;
+
+ --disable-fast-install) enable_fast_install=no ;;
+
+ --enable-dlopen) enable_dlopen=yes ;;
+
+ --enable-win32-dll) enable_win32_dll=yes ;;
+
+ --quiet | --silent) silent=yes ;;
+
+ --srcdir) prev=srcdir ;;
+ --srcdir=*) srcdir="$optarg" ;;
+
+ --no-verify) verify_host=no ;;
+
+ --output | -o) prev=ofile ;;
+ --output=*) ofile="$optarg" ;;
+
+ --version) echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP"; exit 0 ;;
+
+ --with-gcc) with_gcc=yes ;;
+ --with-gnu-ld) with_gnu_ld=yes ;;
+
+ --disable-lock) need_locks=no ;;
+
+ --cache-file=*) cache_file="$optarg" ;;
+
+ -*)
+ echo "$progname: unrecognized option \`$option'" 1>&2
+ echo "$help" 1>&2
+ exit 1
+ ;;
+
+ *)
+ if test -z "$ltmain"; then
+ ltmain="$option"
+ elif test -z "$host"; then
+# This generates an unnecessary warning for sparc-sun-solaris4.1.3_U1
+# if test -n "`echo $option| sed 's/[-a-z0-9.]//g'`"; then
+# echo "$progname: warning \`$option' is not a valid host type" 1>&2
+# fi
+ host="$option"
+ else
+ echo "$progname: too many arguments" 1>&2
+ echo "$help" 1>&2
+ exit 1
+ fi ;;
+ esac
+done
+
+if test -z "$ltmain"; then
+ echo "$progname: you must specify a LTMAIN file" 1>&2
+ echo "$help" 1>&2
+ exit 1
+fi
+
+if test ! -f "$ltmain"; then
+ echo "$progname: \`$ltmain' does not exist" 1>&2
+ echo "$help" 1>&2
+ exit 1
+fi
+
+# Quote any args containing shell metacharacters.
+ltconfig_args=
+for arg
+do
+ case "$arg" in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ltconfig_args="$ltconfig_args '$arg'" ;;
+ *) ltconfig_args="$ltconfig_args $arg" ;;
+ esac
+done
+
+# A relevant subset of AC_INIT.
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 5 compiler messages saved in config.log
+# 6 checking for... messages and results
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>>./config.log
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+
+if test -n "$cache_file" && test -r "$cache_file"; then
+ echo "loading cache $cache_file within ltconfig"
+ . $cache_file
+fi
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+if test -z "$srcdir"; then
+ # Assume the source directory is the same one as the path to LTMAIN.
+ srcdir=`$echo "X$ltmain" | $Xsed -e 's%/[^/]*$%%'`
+ test "$srcdir" = "$ltmain" && srcdir=.
+fi
+
+trap "$rm conftest*; exit 1" 1 2 15
+if test "$verify_host" = yes; then
+ # Check for config.guess and config.sub.
+ ac_aux_dir=
+ for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/config.guess; then
+ ac_aux_dir=$ac_dir
+ break
+ fi
+ done
+ if test -z "$ac_aux_dir"; then
+ echo "$progname: cannot find config.guess in $srcdir $srcdir/.. $srcdir/../.." 1>&2
+ echo "$help" 1>&2
+ exit 1
+ fi
+ ac_config_guess=$ac_aux_dir/config.guess
+ ac_config_sub=$ac_aux_dir/config.sub
+
+ # Make sure we can run config.sub.
+ if $SHELL $ac_config_sub sun4 >/dev/null 2>&1; then :
+ else
+ echo "$progname: cannot run $ac_config_sub" 1>&2
+ echo "$help" 1>&2
+ exit 1
+ fi
+
+ echo $ac_n "checking host system type""... $ac_c" 1>&6
+
+ host_alias=$host
+ case "$host_alias" in
+ "")
+ if host_alias=`$SHELL $ac_config_guess`; then :
+ else
+ echo "$progname: cannot guess host type; you must specify one" 1>&2
+ echo "$help" 1>&2
+ exit 1
+ fi ;;
+ esac
+ host=`$SHELL $ac_config_sub $host_alias`
+ echo "$ac_t$host" 1>&6
+
+ # Make sure the host verified.
+ test -z "$host" && exit 1
+
+elif test -z "$host"; then
+ echo "$progname: you must specify a host type if you use \`--no-verify'" 1>&2
+ echo "$help" 1>&2
+ exit 1
+else
+ host_alias=$host
+fi
+
+# Transform linux* to *-*-linux-gnu*, to support old configure scripts.
+case "$host_os" in
+linux-gnu*) ;;
+linux*) host=`echo $host | sed 's/^\(.*-.*-linux\)\(.*\)$/\1-gnu\2/'`
+esac
+
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+case "$host_os" in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "${COLLECT_NAMES+set}" != set; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR cru $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+# Set a sane default for `AR'.
+test -z "$AR" && AR=ar
+
+# Set a sane default for `OBJDUMP'.
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+# If RANLIB is not set, then run the test.
+if test "${RANLIB+set}" != "set"; then
+ result=no
+
+ echo $ac_n "checking for ranlib... $ac_c" 1>&6
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/ranlib || test -f $dir/ranlib$ac_exeext; then
+ RANLIB="ranlib"
+ result="ranlib"
+ break
+ fi
+ done
+ IFS="$save_ifs"
+
+ echo "$ac_t$result" 1>&6
+fi
+
+if test -n "$RANLIB"; then
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+ old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds"
+fi
+
+# Set sane defaults for `DLLTOOL', `OBJDUMP', and `AS', used on cygwin.
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+test -z "$OBJDUMP" && OBJDUMP=objdump
+test -z "$AS" && AS=as
+
+# Check to see if we are using GCC.
+if test "$with_gcc" != yes || test -z "$CC"; then
+ # If CC is not set, then try to find GCC or a usable CC.
+ if test -z "$CC"; then
+ echo $ac_n "checking for gcc... $ac_c" 1>&6
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/gcc || test -f $dir/gcc$ac_exeext; then
+ CC="gcc"
+ break
+ fi
+ done
+ IFS="$save_ifs"
+
+ if test -n "$CC"; then
+ echo "$ac_t$CC" 1>&6
+ else
+ echo "$ac_t"no 1>&6
+ fi
+ fi
+
+ # Not "gcc", so try "cc", rejecting "/usr/ucb/cc".
+ if test -z "$CC"; then
+ echo $ac_n "checking for cc... $ac_c" 1>&6
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+ cc_rejected=no
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/cc || test -f $dir/cc$ac_exeext; then
+ if test "$dir/cc" = "/usr/ucb/cc"; then
+ cc_rejected=yes
+ continue
+ fi
+ CC="cc"
+ break
+ fi
+ done
+ IFS="$save_ifs"
+ if test $cc_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same name, so the bogon will be chosen
+ # first if we set CC to just the name; use the full file name.
+ shift
+ set dummy "$dir/cc" "$@"
+ shift
+ CC="$@"
+ fi
+ fi
+
+ if test -n "$CC"; then
+ echo "$ac_t$CC" 1>&6
+ else
+ echo "$ac_t"no 1>&6
+ fi
+
+ if test -z "$CC"; then
+ echo "$progname: error: no acceptable cc found in \$PATH" 1>&2
+ exit 1
+ fi
+ fi
+
+ # Now see if the compiler is really GCC.
+ with_gcc=no
+ echo $ac_n "checking whether we are using GNU C... $ac_c" 1>&6
+ echo "$progname:579: checking whether we are using GNU C" >&5
+
+ $rm conftest.c
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+ if { ac_try='${CC-cc} -E conftest.c'; { (eval echo $progname:587: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ with_gcc=yes
+ fi
+ $rm conftest.c
+ echo "$ac_t$with_gcc" 1>&6
+fi
+
+# Allow CC to be a program name with arguments.
+set dummy $CC
+compiler="$2"
+
+echo $ac_n "checking for object suffix... $ac_c" 1>&6
+$rm conftest*
+echo 'int i = 1;' > conftest.c
+echo "$progname:601: checking for object suffix" >& 5
+if { (eval echo $progname:602: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; }; then
+ # Append any warnings to the config.log.
+ cat conftest.err 1>&5
+
+ for ac_file in conftest.*; do
+ case $ac_file in
+ *.c) ;;
+ *) objext=`echo $ac_file | sed -e s/conftest.//` ;;
+ esac
+ done
+else
+ cat conftest.err 1>&5
+ echo "$progname: failed program was:" >&5
+ cat conftest.c >&5
+fi
+$rm conftest*
+echo "$ac_t$objext" 1>&6
+
+echo $ac_n "checking for $compiler option to produce PIC... $ac_c" 1>&6
+pic_flag=
+special_shlib_compile_flags=
+wl=
+link_static_flag=
+no_builtin_flag=
+
+if test "$with_gcc" = yes; then
+ wl='-Wl,'
+ link_static_flag='-static'
+
+ case "$host_os" in
+ beos* | irix5* | irix6* | osf3* | osf4*)
+ # PIC is the default for these OSes.
+ ;;
+ aix*)
+ # Below there is a dirty hack to force normal static linking with -ldl
+ # The problem is because libdl dynamically linked with both libc and
+ # libC (AIX C++ library), which obviously doesn't included in libraries
+ # list by gcc. This cause undefined symbols with -static flags.
+ # This hack allows C programs to be linked with "-static -ldl", but
+ # we not sure about C++ programs.
+ link_static_flag="$link_static_flag ${wl}-lC"
+ ;;
+ cygwin* | mingw* | os2*)
+ # We can build DLLs from non-PIC.
+ ;;
+ amigaos*)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ pic_flag='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ *)
+ pic_flag='-fPIC'
+ ;;
+ esac
+else
+ # PORTME Check for PIC flags for the system compiler.
+ case "$host_os" in
+ aix3* | aix4*)
+ # All AIX code is PIC.
+ link_static_flag='-bnso -bI:/lib/syscalls.exp'
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ # Is there a better link_static_flag that works with the bundled CC?
+ wl='-Wl,'
+ link_static_flag="${wl}-a ${wl}archive"
+ pic_flag='+Z'
+ ;;
+
+ irix5* | irix6*)
+ wl='-Wl,'
+ link_static_flag='-non_shared'
+ # PIC (with -KPIC) is the default.
+ ;;
+
+ cygwin* | mingw* | os2*)
+ # We can build DLLs from non-PIC.
+ ;;
+
+ osf3* | osf4*)
+ # All OSF/1 code is PIC.
+ wl='-Wl,'
+ link_static_flag='-non_shared'
+ ;;
+
+ sco3.2v5*)
+ pic_flag='-Kpic'
+ link_static_flag='-dn'
+ special_shlib_compile_flags='-belf'
+ ;;
+
+ solaris*)
+ pic_flag='-KPIC'
+ link_static_flag='-Bstatic'
+ wl='-Wl,'
+ ;;
+
+ sunos4*)
+ pic_flag='-PIC'
+ link_static_flag='-Bstatic'
+ wl='-Qoption ld '
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ pic_flag='-KPIC'
+ link_static_flag='-Bstatic'
+ wl='-Wl,'
+ ;;
+
+ uts4*)
+ pic_flag='-pic'
+ link_static_flag='-Bstatic'
+ ;;
+
+ *)
+ can_build_shared=no
+ ;;
+ esac
+fi
+
+if test -n "$pic_flag"; then
+ echo "$ac_t$pic_flag" 1>&6
+
+ # Check to make sure the pic_flag actually works.
+ echo $ac_n "checking if $compiler PIC flag $pic_flag works... $ac_c" 1>&6
+ $rm conftest*
+ echo "int some_variable = 0;" > conftest.c
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $pic_flag -DPIC"
+ echo "$progname:732: checking if $compiler PIC flag $pic_flag works" >&5
+ if { (eval echo $progname:733: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.$objext; then
+ # Append any warnings to the config.log.
+ cat conftest.err 1>&5
+
+ case "$host_os" in
+ hpux9* | hpux10* | hpux11*)
+ # On HP-UX, both CC and GCC only warn that PIC is supported... then they
+ # create non-PIC objects. So, if there were any warnings, we assume that
+ # PIC is not supported.
+ if test -s conftest.err; then
+ echo "$ac_t"no 1>&6
+ can_build_shared=no
+ pic_flag=
+ else
+ echo "$ac_t"yes 1>&6
+ pic_flag=" $pic_flag"
+ fi
+ ;;
+ *)
+ echo "$ac_t"yes 1>&6
+ pic_flag=" $pic_flag"
+ ;;
+ esac
+ else
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ can_build_shared=no
+ pic_flag=
+ echo "$ac_t"no 1>&6
+ fi
+ CFLAGS="$save_CFLAGS"
+ $rm conftest*
+else
+ echo "$ac_t"none 1>&6
+fi
+
+# Check to see if options -o and -c are simultaneously supported by compiler
+echo $ac_n "checking if $compiler supports -c -o file.o... $ac_c" 1>&6
+$rm -r conftest 2>/dev/null
+mkdir conftest
+cd conftest
+$rm conftest*
+echo "int some_variable = 0;" > conftest.c
+mkdir out
+# According to Tom Tromey, Ian Lance Taylor reported there are C compilers
+# that will create temporary files in the current directory regardless of
+# the output directory. Thus, making CWD read-only will cause this test
+# to fail, enabling locking or at least warning the user not to do parallel
+# builds.
+chmod -w .
+save_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -o out/conftest2.o"
+echo "$progname:785: checking if $compiler supports -c -o file.o" >&5
+if { (eval echo $progname:786: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.o; then
+
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s out/conftest.err; then
+ echo "$ac_t"no 1>&6
+ compiler_c_o=no
+ else
+ echo "$ac_t"yes 1>&6
+ compiler_c_o=yes
+ fi
+else
+ # Append any errors to the config.log.
+ cat out/conftest.err 1>&5
+ compiler_c_o=no
+ echo "$ac_t"no 1>&6
+fi
+CFLAGS="$save_CFLAGS"
+chmod u+w .
+$rm conftest* out/*
+rmdir out
+cd ..
+rmdir conftest
+$rm -r conftest 2>/dev/null
+
+if test x"$compiler_c_o" = x"yes"; then
+ # Check to see if we can write to a .lo
+ echo $ac_n "checking if $compiler supports -c -o file.lo... $ac_c" 1>&6
+ $rm conftest*
+ echo "int some_variable = 0;" > conftest.c
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -c -o conftest.lo"
+ echo "$progname:818: checking if $compiler supports -c -o file.lo" >&5
+if { (eval echo $progname:819: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.lo; then
+
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ echo "$ac_t"no 1>&6
+ compiler_o_lo=no
+ else
+ echo "$ac_t"yes 1>&6
+ compiler_o_lo=yes
+ fi
+ else
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ compiler_o_lo=no
+ echo "$ac_t"no 1>&6
+ fi
+ CFLAGS="$save_CFLAGS"
+ $rm conftest*
+else
+ compiler_o_lo=no
+fi
+
+# Check to see if we can do hard links to lock some files if needed
+hard_links="nottested"
+if test "$compiler_c_o" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ echo $ac_n "checking if we can lock with hard links... $ac_c" 1>&6
+ hard_links=yes
+ $rm conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ echo "$ac_t$hard_links" 1>&6
+ $rm conftest*
+ if test "$hard_links" = no; then
+ echo "*** WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+if test "$with_gcc" = yes; then
+ # Check to see if options -fno-rtti -fno-exceptions are supported by compiler
+ echo $ac_n "checking if $compiler supports -fno-rtti -fno-exceptions ... $ac_c" 1>&6
+ $rm conftest*
+ echo "int some_variable = 0;" > conftest.c
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fno-rtti -fno-exceptions -c conftest.c"
+ echo "$progname:870: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+ if { (eval echo $progname:871: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.o; then
+
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ echo "$ac_t"no 1>&6
+ compiler_rtti_exceptions=no
+ else
+ echo "$ac_t"yes 1>&6
+ compiler_rtti_exceptions=yes
+ fi
+ else
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ compiler_rtti_exceptions=no
+ echo "$ac_t"no 1>&6
+ fi
+ CFLAGS="$save_CFLAGS"
+ $rm conftest*
+
+ if test "$compiler_rtti_exceptions" = "yes"; then
+ no_builtin_flag=' -fno-builtin -fno-rtti -fno-exceptions'
+ else
+ no_builtin_flag=' -fno-builtin'
+ fi
+
+fi
+
+# Check for any special shared library compilation flags.
+if test -n "$special_shlib_compile_flags"; then
+ echo "$progname: warning: \`$CC' requires \`$special_shlib_compile_flags' to build shared libraries" 1>&2
+ if echo "$old_CC $old_CFLAGS " | egrep -e "[ ]$special_shlib_compile_flags[ ]" >/dev/null; then :
+ else
+ echo "$progname: add \`$special_shlib_compile_flags' to the CC or CFLAGS env variable and reconfigure" 1>&2
+ can_build_shared=no
+ fi
+fi
+
+echo $ac_n "checking if $compiler static flag $link_static_flag works... $ac_c" 1>&6
+$rm conftest*
+echo 'main(){return(0);}' > conftest.c
+save_LDFLAGS="$LDFLAGS"
+LDFLAGS="$LDFLAGS $link_static_flag"
+echo "$progname:914: checking if $compiler static flag $link_static_flag works" >&5
+if { (eval echo $progname:915: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ echo "$ac_t$link_static_flag" 1>&6
+else
+ echo "$ac_t"none 1>&6
+ link_static_flag=
+fi
+LDFLAGS="$save_LDFLAGS"
+$rm conftest*
+
+if test -z "$LN_S"; then
+ # Check to see if we can use ln -s, or we need hard links.
+ echo $ac_n "checking whether ln -s works... $ac_c" 1>&6
+ $rm conftest.dat
+ if ln -s X conftest.dat 2>/dev/null; then
+ $rm conftest.dat
+ LN_S="ln -s"
+ else
+ LN_S=ln
+ fi
+ if test "$LN_S" = "ln -s"; then
+ echo "$ac_t"yes 1>&6
+ else
+ echo "$ac_t"no 1>&6
+ fi
+fi
+
+# Make sure LD is an absolute path.
+if test -z "$LD"; then
+ ac_prog=ld
+ if test "$with_gcc" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ echo $ac_n "checking for ld used by GCC... $ac_c" 1>&6
+ echo "$progname:947: checking for ld used by GCC" >&5
+ ac_prog=`($CC -print-prog-name=ld) 2>&5`
+ case "$ac_prog" in
+ # Accept absolute paths.
+ [\\/]* | [A-Za-z]:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we are not using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+ elif test "$with_gnu_ld" = yes; then
+ echo $ac_n "checking for GNU ld... $ac_c" 1>&6
+ echo "$progname:971: checking for GNU ld" >&5
+ else
+ echo $ac_n "checking for non-GNU ld""... $ac_c" 1>&6
+ echo "$progname:974: checking for non-GNU ld" >&5
+ fi
+
+ if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ if "$LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
+ test "$with_gnu_ld" != no && break
+ else
+ test "$with_gnu_ld" != yes && break
+ fi
+ fi
+ done
+ IFS="$ac_save_ifs"
+ fi
+
+ if test -n "$LD"; then
+ echo "$ac_t$LD" 1>&6
+ else
+ echo "$ac_t"no 1>&6
+ fi
+
+ if test -z "$LD"; then
+ echo "$progname: error: no acceptable ld found in \$PATH" 1>&2
+ exit 1
+ fi
+fi
+
+# Check to see if it really is or is not GNU ld.
+echo $ac_n "checking if the linker ($LD) is GNU ld... $ac_c" 1>&6
+# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+ with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+echo "$ac_t$with_gnu_ld" 1>&6
+
+# See if the linker supports building shared libraries.
+echo $ac_n "checking whether the linker ($LD) supports shared libraries... $ac_c" 1>&6
+
+allow_undefined_flag=
+no_undefined_flag=
+need_lib_prefix=unknown
+need_version=unknown
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+archive_cmds=
+archive_expsym_cmds=
+old_archive_from_new_cmds=
+export_dynamic_flag_spec=
+whole_archive_flag_spec=
+thread_safe_flag_spec=
+hardcode_libdir_flag_spec=
+hardcode_libdir_separator=
+hardcode_direct=no
+hardcode_minus_L=no
+hardcode_shlibpath_var=unsupported
+runpath_var=
+always_export_symbols=no
+export_symbols_cmds='$NM $libobjs | $global_symbol_pipe | sed '\''s/.* //'\'' | sort | uniq > $export_symbols'
+# include_expsyms should be a list of space-separated symbols to be *always*
+# included in the symbol list
+include_expsyms=
+# exclude_expsyms can be an egrep regular expression of symbols to exclude
+# it will be wrapped by ` (' and `)$', so one must not match beginning or
+# end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+# as well as any symbol that contains `d'.
+exclude_expsyms="_GLOBAL_OFFSET_TABLE_"
+# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+# platforms (ab)use it in PIC code, but their linkers get confused if
+# the symbol is explicitly referenced. Since portable code cannot
+# rely on this symbol name, it's probably fine to never include it in
+# preloaded symbol tables.
+
+case "$host_os" in
+cygwin* | mingw*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$with_gcc" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+
+esac
+
+ld_shlibs=yes
+if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # See if GNU ld supports shared libraries.
+ case "$host_os" in
+ aix3* | aix4*)
+ # On AIX, the GNU linker is very broken
+ ld_shlibs=no
+ cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+ ;;
+
+ amigaos*)
+ archive_cmds='$rm $objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $objdir/a2ixlibrary.data~$AR cru $lib $libobjs~$RANLIB $lib~(cd $objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+
+ # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+ # that the semantics of dynamic libraries on AmigaOS, at least up
+ # to version 4, is to share data among multiple programs linked
+ # with the same dynamic library. Since this doesn't match the
+ # behavior of shared libraries on other platforms, we can use
+ # them.
+ ld_shlibs=no
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw*)
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+
+ # Extract the symbol export list from an `--export-all' def file,
+ # then regenerate the def file from the symbol export list, so that
+ # the compiled dll only exports the symbol export list.
+ export_symbols_cmds='rm -f $objdir/$soname-ltdll.c~
+ sed -e "/^# \/\* ltdll\.c starts here \*\//,/^# \/\* ltdll.c ends here \*\// { s/^# //; p; }" -e d < $0 > $objdir/$soname-ltdll.c~
+ (cd $objdir && $CC -c $soname-ltdll.c)~
+ $DLLTOOL --export-all --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --output-def $objdir/$soname-def $objdir/$soname-ltdll.$objext $libobjs~
+ sed -e "1,/EXPORTS/d" -e "s/ @ [0-9]* ; *//" < $objdir/$soname-def > $export_symbols'
+
+ archive_expsym_cmds='echo EXPORTS > $objdir/$soname-def~
+ _lt_hint=1;
+ for symbol in `cat $export_symbols`; do
+ echo " \$symbol @ \$_lt_hint ; " >> $objdir/$soname-def;
+ _lt_hint=`expr 1 + \$_lt_hint`;
+ done~
+ $CC -Wl,--base-file,$objdir/$soname-base -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts~
+ $DLLTOOL --as=$AS --dllname $soname --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --def $objdir/$soname-def --base-file $objdir/$soname-base --output-exp $objdir/$soname-exp~
+ $CC -Wl,--base-file,$objdir/$soname-base $objdir/$soname-exp -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts~
+ $DLLTOOL --as=$AS --dllname $soname --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --def $objdir/$soname-def --base-file $objdir/$soname-base --output-exp $objdir/$soname-exp~
+ $CC $objdir/$soname-exp -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts'
+
+ old_archive_from_new_cmds='$DLLTOOL --as=$AS --dllname $soname --def $objdir/$soname-def --output-lib $objdir/$libname.a'
+ ;;
+
+ netbsd*)
+ if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linkopts -o $lib'
+ # can we support soname and/or expsyms with a.out? -oliva
+ fi
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linkopts'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test "$ld_shlibs" = yes; then
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir'
+ export_dynamic_flag_spec='${wl}--export-dynamic'
+ whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ fi
+else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case "$host_os" in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $objdir/$soname $libobjs $deplibs $linkopts -bE:$export_symbols -T512 -H512 -bM:SRE~$AR cru $lib $objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$with_gcc" = yes && test -z "$link_static_flag"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix4*)
+ hardcode_libdir_flag_spec='${wl}-b ${wl}nolibpath ${wl}-b ${wl}libpath:$libdir:/usr/lib:/lib'
+ hardcode_libdir_separator=':'
+ if test "$with_gcc" = yes; then
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" && \
+ strings "$collect2name" | grep resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ hardcode_direct=yes
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ shared_flag='-shared'
+ else
+ shared_flag='${wl}-bM:SRE'
+ hardcode_direct=yes
+ fi
+ allow_undefined_flag=' ${wl}-berok'
+ archive_cmds="\$CC $shared_flag"' -o $objdir/$soname $libobjs $deplibs $linkopts ${wl}-bexpall ${wl}-bnoentry${allow_undefined_flag}'
+ archive_expsym_cmds="\$CC $shared_flag"' -o $objdir/$soname $libobjs $deplibs $linkopts ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}'
+ case "$host_os" in aix4.[01]|aix4.[01].*)
+ # According to Greg Wooledge, -bexpall is only supported from AIX 4.2 on
+ always_export_symbols=yes ;;
+ esac
+ ;;
+
+ amigaos*)
+ archive_cmds='$rm $objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $objdir/a2ixlibrary.data~$AR cru $lib $libobjs~$RANLIB $lib~(cd $objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ # see comment about different semantics on the GNU ld section
+ ld_shlibs=no
+ ;;
+
+ cygwin* | mingw*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $linkopts `echo "$deplibs" | sed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib /OUT:$oldlib$oldobjs'
+ fix_srcfile_path='`cygpath -w $srcfile`'
+ ;;
+
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd*)
+ archive_cmds='$CC -shared -o $lib $libobjs $deplibs $linkopts'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ case "$host_os" in
+ hpux9*) archive_cmds='$rm $objdir/$soname~$LD -b +b $install_libdir -o $objdir/$soname $libobjs $deplibs $linkopts~test $objdir/$soname = $lib || mv $objdir/$soname $lib' ;;
+ *) archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linkopts' ;;
+ esac
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_minus_L=yes # Not in the search PATH, but as the default
+ # location of the library.
+ export_dynamic_flag_spec='${wl}-E'
+ ;;
+
+ irix5* | irix6*)
+ if test "$with_gcc" = yes; then
+ archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+ else
+ archive_cmds='$LD -shared $libobjs $deplibs $linkopts -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linkopts' # ELF
+ fi
+ hardcode_libdir_flag_spec='${wl}-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ openbsd*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $objdir/$libname.def~$echo DATA >> $objdir/$libname.def~$echo " SINGLE NONSHARED" >> $objdir/$libname.def~$echo EXPORTS >> $objdir/$libname.def~emxexp $libobjs >> $objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $linkopts $objdir/$libname.def'
+ old_archive_from_new_cmds='emximp -o $objdir/$libname.a $objdir/$libname.def'
+ ;;
+
+ osf3* | osf4*)
+ if test "$with_gcc" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $linkopts ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linkopts -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ sco3.2v5*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z text'
+ # $CC -shared without GNU ld will not create a library from C++
+ # object files and a static libstdc++, better avoid it by now
+ archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linkopts'
+ archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linkopts~$rm $lib.exp'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case "$host_os" in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+ whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linkopts'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+fi
+echo "$ac_t$ld_shlibs" 1>&6
+test "$ld_shlibs" = no && can_build_shared=no
+
+if test -z "$NM"; then
+ echo $ac_n "checking for BSD-compatible nm... $ac_c" 1>&6
+ case "$NM" in
+ [\\/]* | [A-Za-z]:[\\/]*) ;; # Let the user override the test with a path.
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+ for ac_dir in $PATH /usr/ucb /usr/ccs/bin /bin; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/nm || test -f $ac_dir/nm$ac_exeext; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+ NM="$ac_dir/nm -B"
+ break
+ elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+ NM="$ac_dir/nm -p"
+ break
+ else
+ NM=${NM="$ac_dir/nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ fi
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$NM" && NM=nm
+ ;;
+ esac
+ echo "$ac_t$NM" 1>&6
+fi
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+echo $ac_n "checking command to parse $NM output... $ac_c" 1>&6
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Transform the above into a raw symbol and a C symbol.
+symxfrm='\1 \2\3 \3'
+
+# Transform an extracted symbol line into a proper C declaration
+global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern char \1;/p'"
+
+# Define system-specific variables.
+case "$host_os" in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*) # Its linker distinguishes data from code symbols
+ global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern char \1();/p' -e 's/^. .* \(.*\)$/extern char \1;/p'"
+ ;;
+irix*)
+ symcode='[BCDEGRST]'
+ ;;
+solaris*)
+ symcode='[BDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+if $NM -V 2>&1 | egrep '(GNU|with BFD)' > /dev/null; then
+ symcode='[ABCDGISTW]'
+fi
+
+# Try without a prefix undercore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Write the raw and C identifiers.
+ global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode\)[ ][ ]*\($ac_symprfx\)$sympat$/$symxfrm/p'"
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+ $rm conftest*
+ cat > conftest.c <<EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(){}
+#ifdef __cplusplus
+}
+#endif
+main(){nm_test_var='a';nm_test_func();return(0);}
+EOF
+
+ echo "$progname:1507: checking if global_symbol_pipe works" >&5
+ if { (eval echo $progname:1508: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; } && test -s conftest.$objext; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { echo "$progname:1511: eval \"$NM conftest.$objext | $global_symbol_pipe > $nlist\"" >&5; eval "$NM conftest.$objext | $global_symbol_pipe > $nlist 2>&5"; } && test -s "$nlist"; then
+
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if egrep ' nm_test_var$' "$nlist" >/dev/null; then
+ if egrep ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<EOF > conftest.c
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EOF
+ # Now generate the symbol file.
+ eval "$global_symbol_to_cdecl"' < "$nlist" >> conftest.c'
+
+ cat <<EOF >> conftest.c
+#if defined (__STDC__) && __STDC__
+# define lt_ptr_t void *
+#else
+# define lt_ptr_t char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ lt_ptr_t address;
+}
+lt_preloaded_symbols[] =
+{
+EOF
+ sed 's/^. \(.*\) \(.*\)$/ {"\2", (lt_ptr_t) \&\2},/' < "$nlist" >> conftest.c
+ cat <<\EOF >> conftest.c
+ {0, (lt_ptr_t) 0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+EOF
+ # Now try linking the two files.
+ mv conftest.$objext conftstm.$objext
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$objext"
+ CFLAGS="$CFLAGS$no_builtin_flag"
+ if { (eval echo $progname:1563: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ pipe_works=yes
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.c >&5
+ fi
+ LIBS="$save_LIBS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.c >&5
+ fi
+ $rm conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ global_symbol_pipe=
+ fi
+done
+if test "$pipe_works" = yes; then
+ echo "${ac_t}ok" 1>&6
+else
+ echo "${ac_t}failed" 1>&6
+fi
+
+if test -z "$global_symbol_pipe"; then
+ global_symbol_to_cdecl=
+fi
+
+# Check hardcoding attributes.
+echo $ac_n "checking how to hardcode library paths into programs... $ac_c" 1>&6
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" || \
+ test -n "$runpath_var"; then
+
+ # We can hardcode non-existant directories.
+ if test "$hardcode_direct" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$hardcode_shlibpath_var" != no &&
+ test "$hardcode_minus_L" != no; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+echo "$ac_t$hardcode_action" 1>&6
+
+
+reload_flag=
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+echo $ac_n "checking for $LD option to reload object files... $ac_c" 1>&6
+# PORTME Some linkers may need a different reload flag.
+reload_flag='-r'
+echo "$ac_t$reload_flag" 1>&6
+test -n "$reload_flag" && reload_flag=" $reload_flag"
+
+# PORTME Fill in your ld.so characteristics
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+file_magic_cmd=
+file_magic_test_file=
+deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [regex]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given egrep regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+echo $ac_n "checking dynamic linker characteristics... $ac_c" 1>&6
+case "$host_os" in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}.so$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}.so$major'
+ ;;
+
+aix4*)
+ version_type=linux
+ # AIX has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ # We preserve .a as extension for shared libraries though AIX4.2
+ # and later linker supports .so
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.a'
+ shlibpath_var=LIBPATH
+ deplibs_check_method=pass_all
+ ;;
+
+amigaos*)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "(cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a)"; (cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a) || exit 1; done'
+ ;;
+
+beos*)
+ library_names_spec='${libname}.so'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+bsdi4*)
+ version_type=linux
+ library_names_spec='${libname}.so$major ${libname}.so'
+ soname_spec='${libname}.so'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ deplibs_check_method='file_magic ELF 32-bit LSB shared object'
+ file_magic_cmd=/usr/bin/file
+ file_magic_test_file=/shlib/libc.so
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw*)
+ version_type=windows
+ if test "$with_gcc" = yes; then
+ library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll $libname.a'
+ else
+ library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll $libname.lib'
+ fi
+ dynamic_linker='Win32 ld.exe'
+ deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ file_magic_cmd='${OBJDUMP} -f'
+ need_lib_prefix=no
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd*)
+ objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+ version_type=freebsd-$objformat
+ case "$version_type" in
+ freebsd-elf*)
+ deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB shared object'
+ file_magic_cmd=/usr/bin/file
+ file_magic_test_file=`echo /usr/lib/libc.so*`
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ deplibs_check_method=unknown
+ library_names_spec='${libname}${release}.so$versuffix $libname.so$versuffix'
+ need_version=yes
+ ;;
+ esac
+ finish_cmds='PATH="\$PATH:/sbin" OBJFORMAT="'"$objformat"'" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+gnu*)
+ version_type=linux
+ library_names_spec='${libname}${release}.so$versuffix ${libname}.so'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ dynamic_linker="$host_os dld.sl"
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}.sl$versuffix ${libname}${release}.sl$major $libname.sl'
+ soname_spec='${libname}${release}.sl$major'
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+irix5* | irix6*)
+ version_type=irix
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}.so.$major'
+ library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major ${libname}${release}.so $libname.so'
+ case "$host_os" in
+ irix5*)
+ libsuff= shlibsuff=
+ # this will be overridden with pass_all, but let us keep it just in case
+ deplibs_check_method="file_magic ELF 32-bit MSB dynamic lib MIPS - version 1"
+ ;;
+ *)
+ case "$LD" in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 ") libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 ") libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 ") libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ # this will be overridden with pass_all, but let us keep it just in case
+ deplibs_check_method="file_magic ELF ${libmagic} MSB mips-[1234] dynamic lib MIPS - version 1"
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ file_magic_cmd=/usr/bin/file
+ file_magic_test_file=`echo /lib${libsuff}/libc.so*`
+ deplibs_check_method='pass_all'
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux-gnuoldld* | linux-gnuaout* | linux-gnucoff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux-gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+ soname_spec='${libname}${release}.so$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ file_magic_cmd=/usr/bin/file
+ file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+
+ if test -f /lib/ld.so.1; then
+ dynamic_linker='GNU ld.so'
+ else
+ # Only the GNU ld.so supports shared libraries on MkLinux.
+ case "$host_cpu" in
+ powerpc*) dynamic_linker=no ;;
+ *) dynamic_linker='Linux ld.so' ;;
+ esac
+ fi
+ ;;
+
+netbsd*)
+ version_type=sunos
+ if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major ${libname}${release}.so ${libname}.so'
+ soname_spec='${libname}${release}.so$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+openbsd*)
+ version_type=sunos
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ need_version=no
+ fi
+ library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+os2*)
+ libname_spec='$name'
+ need_lib_prefix=no
+ library_names_spec='$libname.dll $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4*)
+ version_type=osf
+ need_version=no
+ soname_spec='${libname}${release}.so'
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+ shlibpath_var=LD_LIBRARY_PATH
+ # this will be overridden with pass_all, but let us keep it just in case
+ deplibs_check_method='file_magic COFF format alpha shared library'
+ file_magic_cmd=/usr/bin/file
+ file_magic_test_file=/shlib/libc.so
+ deplibs_check_method='pass_all'
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+sco3.2v5*)
+ version_type=osf
+ soname_spec='${libname}${release}.so$major'
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+ soname_spec='${libname}${release}.so$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ deplibs_check_method="file_magic ELF [0-9][0-9]-bit [LM]SB dynamic lib"
+ file_magic_cmd=/usr/bin/file
+ file_magic_test_file=/lib/libc.so
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ version_type=linux
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+ soname_spec='${libname}${release}.so$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case "$host_vendor" in
+ ncr)
+ deplibs_check_method='pass_all'
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ file_magic_cmd=/usr/bin/file
+ file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ esac
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+ soname_spec='${libname}${release}.so$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+ soname_spec='${libname}${release}.so$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+echo "$ac_t$dynamic_linker" 1>&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+# Report the final consequences.
+echo "checking if libtool supports shared libraries... $can_build_shared" 1>&6
+
+# Only try to build win32 dlls if AC_LIBTOOL_WIN32_DLL was used in
+# configure.in, otherwise build static only libraries.
+case "$host_os" in
+cygwin* | mingw* | os2*)
+ if test x$can_build_shared = xyes; then
+ test x$enable_win32_dll = xno && can_build_shared=no
+ echo "checking if package supports dlls... $can_build_shared" 1>&6
+ fi
+;;
+esac
+
+if test -n "$file_magic_test_file" && test -n "$file_magic_cmd"; then
+ case "$deplibs_check_method" in
+ "file_magic "*)
+ file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ egrep "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+ fi ;;
+ esac
+fi
+
+echo $ac_n "checking whether to build shared libraries... $ac_c" 1>&6
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+aix4*)
+ test "$enable_shared" = yes && enable_static=no
+ ;;
+esac
+
+echo "$ac_t$enable_shared" 1>&6
+
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+
+echo "checking whether to build static libraries... $enable_static" 1>&6
+
+if test "$hardcode_action" = relink; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+echo $ac_n "checking for objdir... $ac_c" 1>&6
+rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+echo "$ac_t$objdir" 1>&6
+
+if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+if eval "test \"`echo '$''{'lt_cv_dlopen'+set}'`\" != set"; then
+ lt_cv_dlopen=no lt_cv_dlopen_libs=
+echo $ac_n "checking for dlopen""... $ac_c" 1>&6
+echo "$progname:2063: checking for dlopen" >&5
+if eval "test \"`echo '$''{'ac_cv_func_dlopen'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2068 "ltconfig"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char dlopen(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_dlopen) || defined (__stub___dlopen)
+choke me
+#else
+dlopen();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo $progname:2090: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_dlopen=yes"
+else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_dlopen=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'dlopen`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ lt_cv_dlopen="dlopen"
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6
+echo "$progname:2108: checking for dlopen in -ldl" >&5
+ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2116 "ltconfig"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen();
+
+int main() {
+dlopen()
+; return 0; }
+EOF
+if { (eval echo $progname:2126: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for dld_link in -ldld""... $ac_c" 1>&6
+echo "$progname:2145: checking for dld_link in -ldld" >&5
+ac_lib_var=`echo dld'_'dld_link | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldld $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2153 "ltconfig"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dld_link();
+
+int main() {
+dld_link()
+; return 0; }
+EOF
+if { (eval echo $progname:2163: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for shl_load""... $ac_c" 1>&6
+echo "$progname:2182: checking for shl_load" >&5
+if eval "test \"`echo '$''{'ac_cv_func_shl_load'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2187 "ltconfig"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char shl_load(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shl_load();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_shl_load) || defined (__stub___shl_load)
+choke me
+#else
+shl_load();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo $progname:2209: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_shl_load=yes"
+else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_shl_load=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'shl_load`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ lt_cv_dlopen="shl_load"
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for shl_load in -ldld""... $ac_c" 1>&6
+echo "$progname:2227: checking for shl_load in -ldld" >&5
+ac_lib_var=`echo dld'_'shl_load | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldld $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2235 "ltconfig"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shl_load();
+
+int main() {
+shl_load()
+; return 0; }
+EOF
+if { (eval echo $progname:2246: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+fi
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ fi
+
+ case "$lt_cv_dlopen" in
+ dlopen)
+for ac_hdr in dlfcn.h; do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "$progname:2289: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2294 "ltconfig"
+#include <$ac_hdr>
+int fnord = 0;
+EOF
+ac_try="$ac_compile conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo $progname:2299: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+ if test "x$ac_cv_header_dlfcn_h" = xyes; then
+ CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+ fi
+ eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ echo $ac_n "checking whether a program can dlopen itself""... $ac_c" 1>&6
+echo "$progname:2327: checking whether a program can dlopen itself" >&5
+if test "${lt_cv_dlopen_self+set}" = set; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ lt_cv_dlopen_self=cross
+ else
+ cat > conftest.c <<EOF
+#line 2335 "ltconfig"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LTDL_GLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LTDL_GLOBAL DL_GLOBAL
+# else
+# define LTDL_GLOBAL 0
+# endif
+#endif
+
+/* We may have to define LTDL_LAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LTDL_LAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LTDL_LAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LTDL_LAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LTDL_LAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LTDL_LAZY_OR_NOW DL_NOW
+# else
+# define LTDL_LAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+fnord() { int i=42;}
+main() { void *self, *ptr1, *ptr2; self=dlopen(0,LTDL_GLOBAL|LTDL_LAZY_OR_NOW);
+ if(self) { ptr1=dlsym(self,"fnord"); ptr2=dlsym(self,"_fnord");
+ if(ptr1 || ptr2) exit(0); } exit(1); }
+
+EOF
+if { (eval echo $progname:2381: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+ lt_cv_dlopen_self=yes
+else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ lt_cv_dlopen_self=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$lt_cv_dlopen_self" 1>&6
+
+ if test "$lt_cv_dlopen_self" = yes; then
+ LDFLAGS="$LDFLAGS $link_static_flag"
+ echo $ac_n "checking whether a statically linked program can dlopen itself""... $ac_c" 1>&6
+echo "$progname:2400: checking whether a statically linked program can dlopen itself" >&5
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ lt_cv_dlopen_self_static=cross
+ else
+ cat > conftest.c <<EOF
+#line 2408 "ltconfig"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LTDL_GLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LTDL_GLOBAL DL_GLOBAL
+# else
+# define LTDL_GLOBAL 0
+# endif
+#endif
+
+/* We may have to define LTDL_LAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LTDL_LAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LTDL_LAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LTDL_LAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LTDL_LAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LTDL_LAZY_OR_NOW DL_NOW
+# else
+# define LTDL_LAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+fnord() { int i=42;}
+main() { void *self, *ptr1, *ptr2; self=dlopen(0,LTDL_GLOBAL|LTDL_LAZY_OR_NOW);
+ if(self) { ptr1=dlsym(self,"fnord"); ptr2=dlsym(self,"_fnord");
+ if(ptr1 || ptr2) exit(0); } exit(1); }
+
+EOF
+if { (eval echo $progname:2454: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+ lt_cv_dlopen_self_static=yes
+else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ lt_cv_dlopen_self_static=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$lt_cv_dlopen_self_static" 1>&6
+fi
+ ;;
+ esac
+
+ case "$lt_cv_dlopen_self" in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case "$lt_cv_dlopen_self_static" in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+# Copy echo and quote the copy, instead of the original, because it is
+# used later.
+ltecho="$echo"
+if test "X$ltecho" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+ ltecho="$CONFIG_SHELL \$0 --fallback-echo"
+fi
+LTSHELL="$SHELL"
+
+LTCONFIG_VERSION="$VERSION"
+
+# Only quote variables if we're using ltmain.sh.
+case "$ltmain" in
+*.sh)
+ # Now quote all the things that may contain metacharacters.
+ for var in ltecho old_CC old_CFLAGS old_CPPFLAGS \
+ old_LD old_LDFLAGS old_LIBS \
+ old_NM old_RANLIB old_LN_S old_DLLTOOL old_OBJDUMP old_AS \
+ AR CC LD LN_S NM LTSHELL LTCONFIG_VERSION \
+ reload_flag reload_cmds wl \
+ pic_flag link_static_flag no_builtin_flag export_dynamic_flag_spec \
+ thread_safe_flag_spec whole_archive_flag_spec libname_spec \
+ library_names_spec soname_spec \
+ RANLIB old_archive_cmds old_archive_from_new_cmds old_postinstall_cmds \
+ old_postuninstall_cmds archive_cmds archive_expsym_cmds postinstall_cmds postuninstall_cmds \
+ file_magic_cmd export_symbols_cmds deplibs_check_method allow_undefined_flag no_undefined_flag \
+ finish_cmds finish_eval global_symbol_pipe global_symbol_to_cdecl \
+ hardcode_libdir_flag_spec hardcode_libdir_separator \
+ sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+ compiler_c_o compiler_o_lo need_locks exclude_expsyms include_expsyms; do
+
+ case "$var" in
+ reload_cmds | old_archive_cmds | old_archive_from_new_cmds | \
+ old_postinstall_cmds | old_postuninstall_cmds | \
+ export_symbols_cmds | archive_cmds | archive_expsym_cmds | \
+ postinstall_cmds | postuninstall_cmds | \
+ finish_cmds | sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+ # Double-quote double-evaled strings.
+ eval "$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+ ;;
+ *)
+ eval "$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+ ;;
+ esac
+ done
+
+ case "$ltecho" in
+ *'\$0 --fallback-echo"')
+ ltecho=`$echo "X$ltecho" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'`
+ ;;
+ esac
+
+ trap "$rm \"$ofile\"; exit 1" 1 2 15
+ echo "creating $ofile"
+ $rm "$ofile"
+ cat <<EOF > "$ofile"
+#! $SHELL
+
+# `$echo "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+# NOTE: Changes made to this file will be lost: look at ltconfig or ltmain.sh.
+#
+# Copyright (C) 1996-1999 Free Software Foundation, Inc.
+# Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="sed -e s/^X//"
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "\${CDPATH+set}" = set; then CDPATH=; export CDPATH; fi
+
+### BEGIN LIBTOOL CONFIG
+EOF
+ cfgfile="$ofile"
+ ;;
+
+*)
+ # Double-quote the variables that need it (for aesthetics).
+ for var in old_CC old_CFLAGS old_CPPFLAGS \
+ old_LD old_LDFLAGS old_LIBS \
+ old_NM old_RANLIB old_LN_S old_DLLTOOL old_OBJDUMP old_AS; do
+ eval "$var=\\\"\$var\\\""
+ done
+
+ # Just create a config file.
+ cfgfile="$ofile.cfg"
+ trap "$rm \"$cfgfile\"; exit 1" 1 2 15
+ echo "creating $cfgfile"
+ $rm "$cfgfile"
+ cat <<EOF > "$cfgfile"
+# `$echo "$cfgfile" | sed 's%^.*/%%'` - Libtool configuration file.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+EOF
+ ;;
+esac
+
+cat <<EOF >> "$cfgfile"
+# Libtool was configured as follows, on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# CC=$old_CC CFLAGS=$old_CFLAGS CPPFLAGS=$old_CPPFLAGS \\
+# LD=$old_LD LDFLAGS=$old_LDFLAGS LIBS=$old_LIBS \\
+# NM=$old_NM RANLIB=$old_RANLIB LN_S=$old_LN_S \\
+# DLLTOOL=$old_DLLTOOL OBJDUMP=$old_OBJDUMP AS=$old_AS \\
+# $0$ltconfig_args
+#
+# Compiler and other test output produced by $progname, useful for
+# debugging $progname, is in ./config.log if it exists.
+
+# The version of $progname that generated this script.
+LTCONFIG_VERSION=$LTCONFIG_VERSION
+
+# Shell to use when invoking shell scripts.
+SHELL=$LTSHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$ltecho
+
+# The archiver.
+AR=$AR
+
+# The default C compiler.
+CC=$CC
+
+# The linker used to build libraries.
+LD=$LD
+
+# Whether we need hard or soft links.
+LN_S=$LN_S
+
+# A BSD-compatible nm program.
+NM=$NM
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$reload_flag
+reload_cmds=$reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$wl
+
+# Object file suffix (normally "o").
+objext="$objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$pic_flag
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$compiler_c_o
+
+# Can we write directly to a .lo ?
+compiler_o_lo=$compiler_o_lo
+
+# Must we lock files when doing compilation ?
+need_locks=$need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$link_static_flag
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$no_builtin_flag
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$whole_archive_flag_spec
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$thread_safe_flag_spec
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$RANLIB
+old_archive_cmds=$old_archive_cmds
+old_postinstall_cmds=$old_postinstall_cmds
+old_postuninstall_cmds=$old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$old_archive_from_new_cmds
+
+# Commands used to build and install a shared archive.
+archive_cmds=$archive_cmds
+archive_expsym_cmds=$archive_expsym_cmds
+postinstall_cmds=$postinstall_cmds
+postuninstall_cmds=$postuninstall_cmds
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$allow_undefined_flag
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$no_undefined_flag
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$global_symbol_to_cdecl
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$hardcode_libdir_flag_spec
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$hardcode_libdir_separator
+
+# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$include_expsyms
+
+EOF
+
+case "$ltmain" in
+*.sh)
+ echo '### END LIBTOOL CONFIG' >> "$ofile"
+ echo >> "$ofile"
+ case "$host_os" in
+ aix3*)
+ cat <<\EOF >> "$ofile"
+
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "${COLLECT_NAMES+set}" != set; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+EOF
+ ;;
+ esac
+
+ # Append the ltmain.sh script.
+ cat "$ltmain" >> "$ofile" || (rm -f "$ofile"; exit 1)
+
+ chmod +x "$ofile"
+ ;;
+
+*)
+ # Compile the libtool program.
+ echo "FIXME: would compile $ltmain"
+ ;;
+esac
+
+test -n "$cache_file" || exit 0
+
+# AC_CACHE_SAVE
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+exit 0
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/neon/ltmain.sh b/neon/ltmain.sh
new file mode 100644
index 000000000..f1b998611
--- /dev/null
+++ b/neon/ltmain.sh
@@ -0,0 +1,3892 @@
+# ltmain.sh - Provide generalized library-building support services.
+# NOTE: Changing this file will not affect anything until you rerun ltconfig.
+#
+# Copyright (C) 1996-1999 Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Check that we have a working $echo.
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+ # Yippee, $echo works!
+ :
+else
+ # Restart under the correct shell, and then maybe $echo will work.
+ exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<EOF
+$*
+EOF
+ exit 0
+fi
+
+# The name of this program.
+progname=`$echo "$0" | sed 's%^.*/%%'`
+modename="$progname"
+
+# Constants.
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=1.3
+TIMESTAMP=" (1.385.2.117 1999/04/29 13:07:13)"
+
+default_mode=
+help="Try \`$progname --help' for more information."
+magic="%%%MAGIC variable%%%"
+mkdir="mkdir"
+mv="mv -f"
+rm="rm -f"
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e 1s/^X//'
+sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g'
+SP2NL='tr \040 \012'
+NL2SP='tr \012 \040'
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+# We save the old values to restore during execute mode.
+if test "${LC_ALL+set}" = set; then
+ save_LC_ALL="$LC_ALL"; LC_ALL=C; export LC_ALL
+fi
+if test "${LANG+set}" = set; then
+ save_LANG="$LANG"; LANG=C; export LANG
+fi
+
+if test "$LTCONFIG_VERSION" != "$VERSION"; then
+ echo "$modename: ltconfig version \`$LTCONFIG_VERSION' does not match $PROGRAM version \`$VERSION'" 1>&2
+ echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2
+ exit 1
+fi
+
+if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+ echo "$modename: not configured to build any kind of library" 1>&2
+ echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2
+ exit 1
+fi
+
+# Global variables.
+mode=$default_mode
+nonopt=
+prev=
+prevopt=
+run=
+show="$echo"
+show_help=
+execute_dlfiles=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+
+# Parse our command line options once, thoroughly.
+while test $# -gt 0
+do
+ arg="$1"
+ shift
+
+ case "$arg" in
+ -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case "$prev" in
+ execute_dlfiles)
+ eval "$prev=\"\$$prev \$arg\""
+ ;;
+ *)
+ eval "$prev=\$arg"
+ ;;
+ esac
+
+ prev=
+ prevopt=
+ continue
+ fi
+
+ # Have we seen a non-optional argument yet?
+ case "$arg" in
+ --help)
+ show_help=yes
+ ;;
+
+ --version)
+ echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP"
+ exit 0
+ ;;
+
+ --config)
+ sed -e '1,/^### BEGIN LIBTOOL CONFIG/d' -e '/^### END LIBTOOL CONFIG/,$d' $0
+ exit 0
+ ;;
+
+ --debug)
+ echo "$progname: enabling shell trace mode"
+ set -x
+ ;;
+
+ --dry-run | -n)
+ run=:
+ ;;
+
+ --features)
+ echo "host: $host"
+ if test "$build_libtool_libs" = yes; then
+ echo "enable shared libraries"
+ else
+ echo "disable shared libraries"
+ fi
+ if test "$build_old_libs" = yes; then
+ echo "enable static libraries"
+ else
+ echo "disable static libraries"
+ fi
+ exit 0
+ ;;
+
+ --finish) mode="finish" ;;
+
+ --mode) prevopt="--mode" prev=mode ;;
+ --mode=*) mode="$optarg" ;;
+
+ --quiet | --silent)
+ show=:
+ ;;
+
+ -dlopen)
+ prevopt="-dlopen"
+ prev=execute_dlfiles
+ ;;
+
+ -*)
+ $echo "$modename: unrecognized option \`$arg'" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ ;;
+
+ *)
+ nonopt="$arg"
+ break
+ ;;
+ esac
+done
+
+if test -n "$prevopt"; then
+ $echo "$modename: option \`$prevopt' requires an argument" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+fi
+
+if test -z "$show_help"; then
+
+ # Infer the operation mode.
+ if test -z "$mode"; then
+ case "$nonopt" in
+ *cc | *++ | gcc* | *-gcc*)
+ mode=link
+ for arg
+ do
+ case "$arg" in
+ -c)
+ mode=compile
+ break
+ ;;
+ esac
+ done
+ ;;
+ *db | *dbx | *strace | *truss)
+ mode=execute
+ ;;
+ *install*|cp|mv)
+ mode=install
+ ;;
+ *rm)
+ mode=uninstall
+ ;;
+ *)
+ # If we have no mode, but dlfiles were specified, then do execute mode.
+ test -n "$execute_dlfiles" && mode=execute
+
+ # Just use the default operation mode.
+ if test -z "$mode"; then
+ if test -n "$nonopt"; then
+ $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2
+ else
+ $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2
+ fi
+ fi
+ ;;
+ esac
+ fi
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$execute_dlfiles" && test "$mode" != execute; then
+ $echo "$modename: unrecognized option \`-dlopen'" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help="$help"
+ help="Try \`$modename --help --mode=$mode' for more information."
+
+ # These modes are in order of execution frequency so that they run quickly.
+ case "$mode" in
+ # libtool compile mode
+ compile)
+ modename="$modename: compile"
+ # Get the compilation command and the source file.
+ base_compile=
+ lastarg=
+ srcfile="$nonopt"
+ suppress_output=
+
+ user_target=no
+ for arg
+ do
+ # Accept any command-line options.
+ case "$arg" in
+ -o)
+ if test "$user_target" != "no"; then
+ $echo "$modename: you cannot specify \`-o' more than once" 1>&2
+ exit 1
+ fi
+ user_target=next
+ ;;
+
+ -static)
+ build_old_libs=yes
+ continue
+ ;;
+ esac
+
+ case "$user_target" in
+ next)
+ # The next one is the -o target name
+ user_target=yes
+ continue
+ ;;
+ yes)
+ # We got the output file
+ user_target=set
+ libobj="$arg"
+ continue
+ ;;
+ esac
+
+ # Accept the current argument as the source file.
+ lastarg="$srcfile"
+ srcfile="$arg"
+
+ # Aesthetically quote the previous argument.
+
+ # Backslashify any backslashes, double quotes, and dollar signs.
+ # These are the only characters that are still specially
+ # interpreted inside of double-quoted scrings.
+ lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"`
+
+ # Double-quote args containing other shell metacharacters.
+ # Many Bourne shells cannot handle close brackets correctly in scan
+ # sets, so we specify it separately.
+ case "$lastarg" in
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*)
+ lastarg="\"$lastarg\""
+ ;;
+ esac
+
+ # Add the previous argument to base_compile.
+ if test -z "$base_compile"; then
+ base_compile="$lastarg"
+ else
+ base_compile="$base_compile $lastarg"
+ fi
+ done
+
+ case "$user_target" in
+ set)
+ ;;
+ no)
+ # Get the name of the library object.
+ libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'`
+ ;;
+ *)
+ $echo "$modename: you must specify a target with \`-o'" 1>&2
+ exit 1
+ ;;
+ esac
+
+ # Recognize several different file suffixes.
+ # If the user specifies -o file.o, it is replaced with file.lo
+ xform='[cCFSfmso]'
+ case "$libobj" in
+ *.ada) xform=ada ;;
+ *.adb) xform=adb ;;
+ *.ads) xform=ads ;;
+ *.asm) xform=asm ;;
+ *.c++) xform=c++ ;;
+ *.cc) xform=cc ;;
+ *.cpp) xform=cpp ;;
+ *.cxx) xform=cxx ;;
+ *.f90) xform=f90 ;;
+ *.for) xform=for ;;
+ esac
+
+ libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"`
+
+ case "$libobj" in
+ *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;;
+ *)
+ $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2
+ exit 1
+ ;;
+ esac
+
+ if test -z "$base_compile"; then
+ $echo "$modename: you must specify a compilation command" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ # Delete any leftover library objects.
+ if test "$build_old_libs" = yes; then
+ removelist="$obj $libobj"
+ else
+ removelist="$libobj"
+ fi
+
+ $run $rm $removelist
+ trap "$run $rm $removelist; exit 1" 1 2 15
+
+ # Calculate the filename of the output object if compiler does
+ # not support -o with -c
+ if test "$compiler_c_o" = no; then
+ output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\..*$%%'`.${objext}
+ lockfile="$output_obj.lock"
+ removelist="$removelist $output_obj $lockfile"
+ trap "$run $rm $removelist; exit 1" 1 2 15
+ else
+ need_locks=no
+ lockfile=
+ fi
+
+ # Lock this critical section if it is needed
+ # We use this script file to make the link, it avoids creating a new file
+ if test "$need_locks" = yes; then
+ until ln "$0" "$lockfile" 2>/dev/null; do
+ $show "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ elif test "$need_locks" = warn; then
+ if test -f "$lockfile"; then
+ echo "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $run $rm $removelist
+ exit 1
+ fi
+ echo $srcfile > "$lockfile"
+ fi
+
+ if test -n "$fix_srcfile_path"; then
+ eval srcfile=\"$fix_srcfile_path\"
+ fi
+
+ # Only build a PIC object if we are building libtool libraries.
+ if test "$build_libtool_libs" = yes; then
+ # Without this assignment, base_compile gets emptied.
+ fbsd_hideous_sh_bug=$base_compile
+
+ # All platforms use -DPIC, to notify preprocessed assembler code.
+ command="$base_compile $pic_flag -DPIC $srcfile"
+ if test "$build_old_libs" = yes; then
+ lo_libobj="$libobj"
+ dir=`$echo "X$libobj" | $Xsed -e 's%/[^/]*$%%'`
+ if test "X$dir" = "X$libobj"; then
+ dir="$objdir"
+ else
+ dir="$dir/$objdir"
+ fi
+ libobj="$dir/"`$echo "X$libobj" | $Xsed -e 's%^.*/%%'`
+
+ if test -d "$dir"; then
+ $show "$rm $libobj"
+ $run $rm $libobj
+ else
+ $show "$mkdir $dir"
+ $run $mkdir $dir
+ status=$?
+ if test $status -ne 0 && test ! -d $dir; then
+ exit $status
+ fi
+ fi
+ fi
+ if test "$compiler_o_lo" = yes; then
+ output_obj="$libobj"
+ command="$command -o $output_obj"
+ elif test "$compiler_c_o" = yes; then
+ output_obj="$obj"
+ command="$command -o $output_obj"
+ fi
+
+ $show "$command"
+ if $run eval "$command"; then :
+ else
+ test -n "$output_obj" && $run $rm $removelist
+ exit 1
+ fi
+
+ if test "$need_locks" = warn &&
+ test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then
+ echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $run $rm $removelist
+ exit 1
+ fi
+
+ # Just move the object if needed, then go on to compile the next one
+ if test x"$output_obj" != x"$libobj"; then
+ $show "$mv $output_obj $libobj"
+ if $run $mv $output_obj $libobj; then :
+ else
+ error=$?
+ $run $rm $removelist
+ exit $error
+ fi
+ fi
+
+ # If we have no pic_flag, then copy the object into place and finish.
+ if test -z "$pic_flag" && test "$build_old_libs" = yes; then
+ # Rename the .lo from within objdir to obj
+ if test -f $obj; then
+ $show $rm $obj
+ $run $rm $obj
+ fi
+
+ $show "$mv $libobj $obj"
+ if $run $mv $libobj $obj; then :
+ else
+ error=$?
+ $run $rm $removelist
+ exit $error
+ fi
+
+ # Now arrange that obj and lo_libobj become the same file
+ $show "$LN_S $obj $lo_libobj"
+ if $run $LN_S $obj $lo_libobj; then
+ exit 0
+ else
+ error=$?
+ $run $rm $removelist
+ exit $error
+ fi
+ fi
+
+ # Allow error messages only from the first compilation.
+ suppress_output=' >/dev/null 2>&1'
+ fi
+
+ # Only build a position-dependent object if we build old libraries.
+ if test "$build_old_libs" = yes; then
+ command="$base_compile $srcfile"
+ if test "$compiler_c_o" = yes; then
+ command="$command -o $obj"
+ output_obj="$obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ command="$command$suppress_output"
+ $show "$command"
+ if $run eval "$command"; then :
+ else
+ $run $rm $removelist
+ exit 1
+ fi
+
+ if test "$need_locks" = warn &&
+ test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then
+ echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $run $rm $removelist
+ exit 1
+ fi
+
+ # Just move the object if needed
+ if test x"$output_obj" != x"$obj"; then
+ $show "$mv $output_obj $obj"
+ if $run $mv $output_obj $obj; then :
+ else
+ error=$?
+ $run $rm $removelist
+ exit $error
+ fi
+ fi
+
+ # Create an invalid libtool object if no PIC, so that we do not
+ # accidentally link it into a program.
+ if test "$build_libtool_libs" != yes; then
+ $show "echo timestamp > $libobj"
+ $run eval "echo timestamp > \$libobj" || exit $?
+ else
+ # Move the .lo from within objdir
+ $show "$mv $libobj $lo_libobj"
+ if $run $mv $libobj $lo_libobj; then :
+ else
+ error=$?
+ $run $rm $removelist
+ exit $error
+ fi
+ fi
+ fi
+
+ # Unlock the critical section if it was locked
+ if test "$need_locks" != no; then
+ $rm "$lockfile"
+ fi
+
+ exit 0
+ ;;
+
+ # libtool link mode
+ link)
+ modename="$modename: link"
+ C_compiler="$CC" # save it, to compile generated C sources
+ CC="$nonopt"
+ case "$host" in
+ *-*-cygwin* | *-*-mingw* | *-*-os2*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # which system we are compiling for in order to pass an extra
+ # flag for every libtool invokation.
+ # allow_undefined=no
+
+ # FIXME: Unfortunately, there are problems with the above when trying
+ # to make a dll which has undefined symbols, in which case not
+ # even a static library is built. For now, we need to specify
+ # -no-undefined on the libtool link line when we can be certain
+ # that all symbols are satisfied, otherwise we get a static library.
+ allow_undefined=yes
+
+ # This is a source program that is used to create dlls on Windows
+ # Don't remove nor modify the starting and closing comments
+# /* ltdll.c starts here */
+# #define WIN32_LEAN_AND_MEAN
+# #include <windows.h>
+# #undef WIN32_LEAN_AND_MEAN
+# #include <stdio.h>
+#
+# #ifdef __cplusplus
+# extern "C" {
+# #endif
+# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved);
+# #ifdef __cplusplus
+# }
+# #endif
+#
+# #include <cygwin/cygwin_dll.h>
+# DECLARE_CYGWIN_DLL( DllMain );
+# HINSTANCE __hDllInstance_base;
+#
+# BOOL APIENTRY
+# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
+# {
+# __hDllInstance_base = hInst;
+# return TRUE;
+# }
+# /* ltdll.c ends here */
+ # This is a source program that is used to create import libraries
+ # on Windows for dlls which lack them. Don't remove nor modify the
+ # starting and closing comments
+# /* impgen.c starts here */
+# /* Copyright (C) 1999 Free Software Foundation, Inc.
+#
+# This file is part of GNU libtool.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# */
+#
+# #include <stdio.h> /* for printf() */
+# #include <unistd.h> /* for open(), lseek(), read() */
+# #include <fcntl.h> /* for O_RDONLY, O_BINARY */
+# #include <string.h> /* for strdup() */
+#
+# static unsigned int
+# pe_get16 (fd, offset)
+# int fd;
+# int offset;
+# {
+# unsigned char b[2];
+# lseek (fd, offset, SEEK_SET);
+# read (fd, b, 2);
+# return b[0] + (b[1]<<8);
+# }
+#
+# static unsigned int
+# pe_get32 (fd, offset)
+# int fd;
+# int offset;
+# {
+# unsigned char b[4];
+# lseek (fd, offset, SEEK_SET);
+# read (fd, b, 4);
+# return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+# }
+#
+# static unsigned int
+# pe_as32 (ptr)
+# void *ptr;
+# {
+# unsigned char *b = ptr;
+# return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+# }
+#
+# int
+# main (argc, argv)
+# int argc;
+# char *argv[];
+# {
+# int dll;
+# unsigned long pe_header_offset, opthdr_ofs, num_entries, i;
+# unsigned long export_rva, export_size, nsections, secptr, expptr;
+# unsigned long name_rvas, nexp;
+# unsigned char *expdata, *erva;
+# char *filename, *dll_name;
+#
+# filename = argv[1];
+#
+# dll = open(filename, O_RDONLY|O_BINARY);
+# if (!dll)
+# return 1;
+#
+# dll_name = filename;
+#
+# for (i=0; filename[i]; i++)
+# if (filename[i] == '/' || filename[i] == '\\' || filename[i] == ':')
+# dll_name = filename + i +1;
+#
+# pe_header_offset = pe_get32 (dll, 0x3c);
+# opthdr_ofs = pe_header_offset + 4 + 20;
+# num_entries = pe_get32 (dll, opthdr_ofs + 92);
+#
+# if (num_entries < 1) /* no exports */
+# return 1;
+#
+# export_rva = pe_get32 (dll, opthdr_ofs + 96);
+# export_size = pe_get32 (dll, opthdr_ofs + 100);
+# nsections = pe_get16 (dll, pe_header_offset + 4 +2);
+# secptr = (pe_header_offset + 4 + 20 +
+# pe_get16 (dll, pe_header_offset + 4 + 16));
+#
+# expptr = 0;
+# for (i = 0; i < nsections; i++)
+# {
+# char sname[8];
+# unsigned long secptr1 = secptr + 40 * i;
+# unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
+# unsigned long vsize = pe_get32 (dll, secptr1 + 16);
+# unsigned long fptr = pe_get32 (dll, secptr1 + 20);
+# lseek(dll, secptr1, SEEK_SET);
+# read(dll, sname, 8);
+# if (vaddr <= export_rva && vaddr+vsize > export_rva)
+# {
+# expptr = fptr + (export_rva - vaddr);
+# if (export_rva + export_size > vaddr + vsize)
+# export_size = vsize - (export_rva - vaddr);
+# break;
+# }
+# }
+#
+# expdata = (unsigned char*)malloc(export_size);
+# lseek (dll, expptr, SEEK_SET);
+# read (dll, expdata, export_size);
+# erva = expdata - export_rva;
+#
+# nexp = pe_as32 (expdata+24);
+# name_rvas = pe_as32 (expdata+32);
+#
+# printf ("EXPORTS\n");
+# for (i = 0; i<nexp; i++)
+# {
+# unsigned long name_rva = pe_as32 (erva+name_rvas+i*4);
+# printf ("\t%s @ %ld ;\n", erva+name_rva, 1+ i);
+# }
+#
+# return 0;
+# }
+# /* impgen.c ends here */
+ ;;
+ *)
+ allow_undefined=yes
+ ;;
+ esac
+ compile_command="$CC"
+ finalize_command="$CC"
+
+ compile_rpath=
+ finalize_rpath=
+ compile_shlibpath=
+ finalize_shlibpath=
+ convenience=
+ old_convenience=
+ deplibs=
+ linkopts=
+
+ if test -n "$shlibpath_var"; then
+ # get the directories listed in $shlibpath_var
+ eval lib_search_path=\`\$echo \"X \${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+ else
+ lib_search_path=
+ fi
+ # now prepend the system-specific ones
+ eval lib_search_path=\"$sys_lib_search_path_spec\$lib_search_path\"
+ eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+ avoid_version=no
+ dlfiles=
+ dlprefiles=
+ dlself=no
+ export_dynamic=no
+ export_symbols=
+ export_symbols_regex=
+ generated=
+ libobjs=
+ link_against_libtool_libs=
+ ltlibs=
+ module=no
+ objs=
+ prefer_static_libs=no
+ preload=no
+ prev=
+ prevarg=
+ release=
+ rpath=
+ xrpath=
+ perm_rpath=
+ temp_rpath=
+ thread_safe=no
+ vinfo=
+
+ # We need to know -static, to get the right output filenames.
+ for arg
+ do
+ case "$arg" in
+ -all-static | -static)
+ if test "X$arg" = "X-all-static"; then
+ if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+ $echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2
+ fi
+ if test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ else
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ fi
+ build_libtool_libs=no
+ build_old_libs=yes
+ prefer_static_libs=yes
+ break
+ ;;
+ esac
+ done
+
+ # See if our shared archives depend on static archives.
+ test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+ # Go through the arguments, transforming them on the way.
+ while test $# -gt 0; do
+ arg="$1"
+ shift
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case "$prev" in
+ output)
+ compile_command="$compile_command @OUTPUT@"
+ finalize_command="$finalize_command @OUTPUT@"
+ ;;
+ esac
+
+ case "$prev" in
+ dlfiles|dlprefiles)
+ if test "$preload" = no; then
+ # Add the symbol object into the linking commands.
+ compile_command="$compile_command @SYMFILE@"
+ finalize_command="$finalize_command @SYMFILE@"
+ preload=yes
+ fi
+ case "$arg" in
+ *.la | *.lo) ;; # We handle these cases below.
+ self)
+ if test "$prev" = dlprefiles; then
+ dlself=yes
+ elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+ dlself=yes
+ else
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ if test "$prev" = dlfiles; then
+ dlfiles="$dlfiles $arg"
+ else
+ dlprefiles="$dlprefiles $arg"
+ fi
+ prev=
+ ;;
+ esac
+ ;;
+ expsyms)
+ export_symbols="$arg"
+ if test ! -f "$arg"; then
+ $echo "$modename: symbol file \`$arg' does not exist"
+ exit 1
+ fi
+ prev=
+ continue
+ ;;
+ expsyms_regex)
+ export_symbols_regex="$arg"
+ prev=
+ continue
+ ;;
+ release)
+ release="-$arg"
+ prev=
+ continue
+ ;;
+ rpath | xrpath)
+ # We need an absolute path.
+ case "$arg" in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ $echo "$modename: only absolute run-paths are allowed" 1>&2
+ exit 1
+ ;;
+ esac
+ if test "$prev" = rpath; then
+ case "$rpath " in
+ *" $arg "*) ;;
+ *) rpath="$rpath $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) xrpath="$xrpath $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ eval "$prev=\"\$arg\""
+ prev=
+ continue
+ ;;
+ esac
+ fi
+
+ prevarg="$arg"
+
+ case "$arg" in
+ -all-static)
+ if test -n "$link_static_flag"; then
+ compile_command="$compile_command $link_static_flag"
+ finalize_command="$finalize_command $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -allow-undefined)
+ # FIXME: remove this flag sometime in the future.
+ $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2
+ continue
+ ;;
+
+ -avoid-version)
+ avoid_version=yes
+ continue
+ ;;
+
+ -dlopen)
+ prev=dlfiles
+ continue
+ ;;
+
+ -dlpreopen)
+ prev=dlprefiles
+ continue
+ ;;
+
+ -export-dynamic)
+ export_dynamic=yes
+ continue
+ ;;
+
+ -export-symbols | -export-symbols-regex)
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ $echo "$modename: not more than one -exported-symbols argument allowed"
+ exit 1
+ fi
+ if test "X$arg" = "X-export-symbols"; then
+ prev=expsyms
+ else
+ prev=expsyms_regex
+ fi
+ continue
+ ;;
+
+ -L*)
+ dir=`$echo "X$arg" | $Xsed -e 's/^-L//'`
+ # We need an absolute path.
+ case "$dir" in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2
+ exit 1
+ fi
+ dir="$absdir"
+ ;;
+ esac
+ case " $deplibs " in
+ *" $arg "*) ;;
+ *) deplibs="$deplibs $arg";;
+ esac
+ case " $lib_search_path " in
+ *" $dir "*) ;;
+ *) lib_search_path="$lib_search_path $dir";;
+ esac
+ case "$host" in
+ *-*-cygwin* | *-*-mingw* | *-*-os2*)
+ dllsearchdir=`cd "$dir" && pwd || echo "$dir"`
+ case ":$dllsearchpath:" in
+ ::) dllsearchpath="$dllsearchdir";;
+ *":$dllsearchdir:"*) ;;
+ *) dllsearchpath="$dllsearchpath:$dllsearchdir";;
+ esac
+ ;;
+ esac
+ ;;
+
+ -l*)
+ if test "$arg" = "-lc"; then
+ case "$host" in
+ *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*)
+ # These systems don't actually have c library (as such)
+ continue
+ ;;
+ esac
+ elif test "$arg" = "-lm"; then
+ case "$host" in
+ *-*-cygwin* | *-*-beos*)
+ # These systems don't actually have math library (as such)
+ continue
+ ;;
+ esac
+ fi
+ deplibs="$deplibs $arg"
+ ;;
+
+ -module)
+ module=yes
+ continue
+ ;;
+
+ -no-undefined)
+ allow_undefined=no
+ continue
+ ;;
+
+ -o) prev=output ;;
+
+ -release)
+ prev=release
+ continue
+ ;;
+
+ -rpath)
+ prev=rpath
+ continue
+ ;;
+
+ -R)
+ prev=xrpath
+ continue
+ ;;
+
+ -R*)
+ dir=`$echo "X$arg" | $Xsed -e 's/^-R//'`
+ # We need an absolute path.
+ case "$dir" in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ $echo "$modename: only absolute run-paths are allowed" 1>&2
+ exit 1
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) xrpath="$xrpath $dir" ;;
+ esac
+ continue
+ ;;
+
+ -static)
+ # If we have no pic_flag, then this is the same as -all-static.
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ compile_command="$compile_command $link_static_flag"
+ finalize_command="$finalize_command $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -thread-safe)
+ thread_safe=yes
+ continue
+ ;;
+
+ -version-info)
+ prev=vinfo
+ continue
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+ case "$arg" in
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*)
+ arg="\"$arg\""
+ ;;
+ esac
+ ;;
+
+ *.o | *.obj | *.a | *.lib)
+ # A standard object.
+ objs="$objs $arg"
+ ;;
+
+ *.lo)
+ # A library object.
+ if test "$prev" = dlfiles; then
+ dlfiles="$dlfiles $arg"
+ if test "$build_libtool_libs" = yes && test "$dlopen" = yes; then
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ if test "$prev" = dlprefiles; then
+ # Preload the old-style object.
+ dlprefiles="$dlprefiles "`$echo "X$arg" | $Xsed -e "$lo2o"`
+ prev=
+ fi
+ libobjs="$libobjs $arg"
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ dlname=
+ libdir=
+ library_names=
+ old_library=
+
+ # Check to see that this really is a libtool archive.
+ if (sed -e '2q' $arg | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+ else
+ $echo "$modename: \`$arg' is not a valid libtool archive" 1>&2
+ exit 1
+ fi
+
+ # If the library was installed with an old release of libtool,
+ # it will not redefine variable installed.
+ installed=yes
+
+ # Read the .la file
+ # If there is no directory component, then add one.
+ case "$arg" in
+ */* | *\\*) . $arg ;;
+ *) . ./$arg ;;
+ esac
+
+ # Get the name of the library we link against.
+ linklib=
+ for l in $old_library $library_names; do
+ linklib="$l"
+ done
+
+ if test -z "$linklib"; then
+ $echo "$modename: cannot find name of link library for \`$arg'" 1>&2
+ exit 1
+ fi
+
+ # Find the relevant object directory and library name.
+ name=`$echo "X$arg" | $Xsed -e 's%^.*/%%' -e 's/\.la$//' -e 's/^lib//'`
+
+ if test "X$installed" = Xyes; then
+ dir="$libdir"
+ else
+ dir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'`
+ if test "X$dir" = "X$arg"; then
+ dir="$objdir"
+ else
+ dir="$dir/$objdir"
+ fi
+ fi
+
+ if test -n "$dependency_libs"; then
+ # Extract -R from dependency_libs
+ temp_deplibs=
+ for deplib in $dependency_libs; do
+ case "$deplib" in
+ -R*) temp_xrpath=`$echo "X$deplib" | $Xsed -e 's/^-R//'`
+ case " $rpath $xrpath " in
+ *" $temp_xrpath "*) ;;
+ *) xrpath="$xrpath $temp_xrpath";;
+ esac;;
+ -L*) case "$compile_command $temp_deplibs " in
+ *" $deplib "*) ;;
+ *) temp_deplibs="$temp_deplibs $deplib";;
+ esac;;
+ *) temp_deplibs="$temp_deplibs $deplib";;
+ esac
+ done
+ dependency_libs="$temp_deplibs"
+ fi
+
+ if test -z "$libdir"; then
+ # It is a libtool convenience library, so add in its objects.
+ convenience="$convenience $dir/$old_library"
+ old_convenience="$old_convenience $dir/$old_library"
+ deplibs="$deplibs$dependency_libs"
+ compile_command="$compile_command $dir/$old_library$dependency_libs"
+ finalize_command="$finalize_command $dir/$old_library$dependency_libs"
+ continue
+ fi
+
+ # This library was specified with -dlopen.
+ if test "$prev" = dlfiles; then
+ dlfiles="$dlfiles $arg"
+ if test -z "$dlname" || test "$dlopen" != yes || test "$build_libtool_libs" = no; then
+ # If there is no dlname, no dlopen support or we're linking statically,
+ # we need to preload.
+ prev=dlprefiles
+ else
+ # We should not create a dependency on this library, but we
+ # may need any libraries it requires.
+ compile_command="$compile_command$dependency_libs"
+ finalize_command="$finalize_command$dependency_libs"
+ prev=
+ continue
+ fi
+ fi
+
+ # The library was specified with -dlpreopen.
+ if test "$prev" = dlprefiles; then
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ dlprefiles="$dlprefiles $dir/$old_library"
+ else
+ dlprefiles="$dlprefiles $dir/$linklib"
+ fi
+ prev=
+ fi
+
+ if test -n "$library_names" &&
+ { test "$prefer_static_libs" = no || test -z "$old_library"; }; then
+ link_against_libtool_libs="$link_against_libtool_libs $arg"
+ if test -n "$shlibpath_var"; then
+ # Make sure the rpath contains only unique directories.
+ case "$temp_rpath " in
+ *" $dir "*) ;;
+ *) temp_rpath="$temp_rpath $dir" ;;
+ esac
+ fi
+
+ # We need an absolute path.
+ case "$dir" in
+ [\\/] | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2
+ exit 1
+ fi
+ ;;
+ esac
+
+ # This is the magic to use -rpath.
+ # Skip directories that are in the system default run-time
+ # search path, unless they have been requested with -R.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) compile_rpath="$compile_rpath $absdir"
+ esac
+ ;;
+ esac
+
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir"
+ esac
+ ;;
+ esac
+
+ lib_linked=yes
+ case "$hardcode_action" in
+ immediate | unsupported)
+ if test "$hardcode_direct" = no; then
+ compile_command="$compile_command $dir/$linklib"
+ deplibs="$deplibs $dir/$linklib"
+ case "$host" in
+ *-*-cygwin* | *-*-mingw* | *-*-os2*)
+ dllsearchdir=`cd "$dir" && pwd || echo "$dir"`
+ if test -n "$dllsearchpath"; then
+ dllsearchpath="$dllsearchpath:$dllsearchdir"
+ else
+ dllsearchpath="$dllsearchdir"
+ fi
+ ;;
+ esac
+ elif test "$hardcode_minus_L" = no; then
+ case "$host" in
+ *-*-sunos*)
+ compile_shlibpath="$compile_shlibpath$dir:"
+ ;;
+ esac
+ case "$compile_command " in
+ *" -L$dir "*) ;;
+ *) compile_command="$compile_command -L$dir";;
+ esac
+ compile_command="$compile_command -l$name"
+ deplibs="$deplibs -L$dir -l$name"
+ elif test "$hardcode_shlibpath_var" = no; then
+ case ":$compile_shlibpath:" in
+ *":$dir:"*) ;;
+ *) compile_shlibpath="$compile_shlibpath$dir:";;
+ esac
+ compile_command="$compile_command -l$name"
+ deplibs="$deplibs -l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+
+ relink)
+ if test "$hardcode_direct" = yes; then
+ compile_command="$compile_command $absdir/$linklib"
+ deplibs="$deplibs $absdir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ case "$compile_command " in
+ *" -L$absdir "*) ;;
+ *) compile_command="$compile_command -L$absdir";;
+ esac
+ compile_command="$compile_command -l$name"
+ deplibs="$deplibs -L$absdir -l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ case ":$compile_shlibpath:" in
+ *":$absdir:"*) ;;
+ *) compile_shlibpath="$compile_shlibpath$absdir:";;
+ esac
+ compile_command="$compile_command -l$name"
+ deplibs="$deplibs -l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+
+ *)
+ lib_linked=no
+ ;;
+ esac
+
+ if test "$lib_linked" != yes; then
+ $echo "$modename: configuration error: unsupported hardcode properties"
+ exit 1
+ fi
+
+ # Finalize command for both is simple: just hardcode it.
+ if test "$hardcode_direct" = yes; then
+ finalize_command="$finalize_command $libdir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ case "$finalize_command " in
+ *" -L$libdir "*) ;;
+ *) finalize_command="$finalize_command -L$libdir";;
+ esac
+ finalize_command="$finalize_command -l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ case ":$finalize_shlibpath:" in
+ *":$libdir:"*) ;;
+ *) finalize_shlibpath="$finalize_shlibpath$libdir:";;
+ esac
+ finalize_command="$finalize_command -l$name"
+ else
+ # We cannot seem to hardcode it, guess we'll fake it.
+ case "$finalize_command " in
+ *" -L$dir "*) ;;
+ *) finalize_command="$finalize_command -L$libdir";;
+ esac
+ finalize_command="$finalize_command -l$name"
+ fi
+ else
+ # Transform directly to old archives if we don't build new libraries.
+ if test -n "$pic_flag" && test -z "$old_library"; then
+ $echo "$modename: cannot find static library for \`$arg'" 1>&2
+ exit 1
+ fi
+
+ # Here we assume that one of hardcode_direct or hardcode_minus_L
+ # is not unsupported. This is valid on all known static and
+ # shared platforms.
+ if test "$hardcode_direct" != unsupported; then
+ test -n "$old_library" && linklib="$old_library"
+ compile_command="$compile_command $dir/$linklib"
+ finalize_command="$finalize_command $dir/$linklib"
+ else
+ case "$compile_command " in
+ *" -L$dir "*) ;;
+ *) compile_command="$compile_command -L$dir";;
+ esac
+ compile_command="$compile_command -l$name"
+ case "$finalize_command " in
+ *" -L$dir "*) ;;
+ *) finalize_command="$finalize_command -L$dir";;
+ esac
+ finalize_command="$finalize_command -l$name"
+ fi
+ fi
+
+ # Add in any libraries that this one depends upon.
+ compile_command="$compile_command$dependency_libs"
+ finalize_command="$finalize_command$dependency_libs"
+ continue
+ ;;
+
+ # Some other compiler argument.
+ *)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+ case "$arg" in
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*)
+ arg="\"$arg\""
+ ;;
+ esac
+ ;;
+ esac
+
+ # Now actually substitute the argument into the commands.
+ if test -n "$arg"; then
+ compile_command="$compile_command $arg"
+ finalize_command="$finalize_command $arg"
+ fi
+ done
+
+ if test -n "$prev"; then
+ $echo "$modename: the \`$prevarg' option requires an argument" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+ eval arg=\"$export_dynamic_flag_spec\"
+ compile_command="$compile_command $arg"
+ finalize_command="$finalize_command $arg"
+ fi
+
+ oldlibs=
+ # calculate the name of the file, without its directory
+ outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'`
+ libobjs_save="$libobjs"
+
+ case "$output" in
+ "")
+ $echo "$modename: you must specify an output file" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ ;;
+
+ *.a | *.lib)
+ if test -n "$link_against_libtool_libs"; then
+ $echo "$modename: error: cannot link libtool libraries into archives" 1>&2
+ exit 1
+ fi
+
+ if test -n "$deplibs"; then
+ $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2
+ fi
+
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2
+ fi
+
+ if test -n "$rpath"; then
+ $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2
+ fi
+
+ if test -n "$xrpath"; then
+ $echo "$modename: warning: \`-R' is ignored for archives" 1>&2
+ fi
+
+ if test -n "$vinfo"; then
+ $echo "$modename: warning: \`-version-info' is ignored for archives" 1>&2
+ fi
+
+ if test -n "$release"; then
+ $echo "$modename: warning: \`-release' is ignored for archives" 1>&2
+ fi
+
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2
+ fi
+
+ # Now set the variables for building old libraries.
+ build_libtool_libs=no
+ oldlibs="$output"
+ ;;
+
+ *.la)
+ # Make sure we only generate libraries of the form `libNAME.la'.
+ case "$outputname" in
+ lib*)
+ name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'`
+ eval libname=\"$libname_spec\"
+ ;;
+ *)
+ if test "$module" = no; then
+ $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+ if test "$need_lib_prefix" != no; then
+ # Add the "lib" prefix for modules if required
+ name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+ eval libname=\"$libname_spec\"
+ else
+ libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+ fi
+ ;;
+ esac
+
+ output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'`
+ if test "X$output_objdir" = "X$output"; then
+ output_objdir="$objdir"
+ else
+ output_objdir="$output_objdir/$objdir"
+ fi
+
+ if test -n "$objs"; then
+ $echo "$modename: cannot build libtool library \`$output' from non-libtool objects:$objs" 2>&1
+ exit 1
+ fi
+
+ # How the heck are we supposed to write a wrapper for a shared library?
+ if test -n "$link_against_libtool_libs"; then
+ $echo "$modename: error: cannot link shared libraries into libtool libraries" 1>&2
+ exit 1
+ fi
+
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ $echo "$modename: warning: \`-dlopen' is ignored for libtool libraries" 1>&2
+ fi
+
+ set dummy $rpath
+ if test $# -gt 2; then
+ $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2
+ fi
+ install_libdir="$2"
+
+ oldlibs=
+ if test -z "$rpath"; then
+ if test "$build_libtool_libs" = yes; then
+ # Building a libtool convenience library.
+ libext=al
+ oldlibs="$output_objdir/$libname.$libext $oldlibs"
+ build_libtool_libs=convenience
+ build_old_libs=yes
+ fi
+ dependency_libs="$deplibs"
+
+ if test -n "$vinfo"; then
+ $echo "$modename: warning: \`-version-info' is ignored for convenience libraries" 1>&2
+ fi
+
+ if test -n "$release"; then
+ $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2
+ fi
+ else
+
+ # Parse the version information argument.
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS=':'
+ set dummy $vinfo 0 0 0
+ IFS="$save_ifs"
+
+ if test -n "$8"; then
+ $echo "$modename: too many parameters to \`-version-info'" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ current="$2"
+ revision="$3"
+ age="$4"
+
+ # Check that each of the things are valid numbers.
+ case "$current" in
+ 0 | [1-9] | [1-9][0-9]*) ;;
+ *)
+ $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2
+ $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+ exit 1
+ ;;
+ esac
+
+ case "$revision" in
+ 0 | [1-9] | [1-9][0-9]*) ;;
+ *)
+ $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2
+ $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+ exit 1
+ ;;
+ esac
+
+ case "$age" in
+ 0 | [1-9] | [1-9][0-9]*) ;;
+ *)
+ $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2
+ $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+ exit 1
+ ;;
+ esac
+
+ if test $age -gt $current; then
+ $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2
+ $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+ exit 1
+ fi
+
+ # Calculate the version variables.
+ major=
+ versuffix=
+ verstring=
+ case "$version_type" in
+ none) ;;
+
+ irix)
+ major=`expr $current - $age + 1`
+ versuffix="$major.$revision"
+ verstring="sgi$major.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$revision
+ while test $loop != 0; do
+ iface=`expr $revision - $loop`
+ loop=`expr $loop - 1`
+ verstring="sgi$major.$iface:$verstring"
+ done
+ ;;
+
+ linux)
+ major=.`expr $current - $age`
+ versuffix="$major.$age.$revision"
+ ;;
+
+ osf)
+ major=`expr $current - $age`
+ versuffix=".$current.$age.$revision"
+ verstring="$current.$age.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$age
+ while test $loop != 0; do
+ iface=`expr $current - $loop`
+ loop=`expr $loop - 1`
+ verstring="$verstring:${iface}.0"
+ done
+
+ # Make executables depend on our current version.
+ verstring="$verstring:${current}.0"
+ ;;
+
+ sunos)
+ major=".$current"
+ versuffix=".$current.$revision"
+ ;;
+
+ freebsd-aout)
+ major=".$current"
+ versuffix=".$current.$revision";
+ ;;
+
+ freebsd-elf)
+ major=".$current"
+ versuffix=".$current";
+ ;;
+
+ windows)
+ # Like Linux, but with '-' rather than '.', since we only
+ # want one extension on Windows 95.
+ major=`expr $current - $age`
+ versuffix="-$major-$age-$revision"
+ ;;
+
+ *)
+ $echo "$modename: unknown library version type \`$version_type'" 1>&2
+ echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2
+ exit 1
+ ;;
+ esac
+
+ # Clear the version info if we defaulted, and they specified a release.
+ if test -z "$vinfo" && test -n "$release"; then
+ major=
+ verstring="0.0"
+ if test "$need_version" = no; then
+ versuffix=
+ else
+ versuffix=".0.0"
+ fi
+ fi
+
+ # Remove version info from name if versioning should be avoided
+ if test "$avoid_version" = yes && test "$need_version" = no; then
+ major=
+ versuffix=
+ verstring=""
+ fi
+
+ # Check to see if the archive will have undefined symbols.
+ if test "$allow_undefined" = yes; then
+ if test "$allow_undefined_flag" = unsupported; then
+ $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2
+ build_libtool_libs=no
+ build_old_libs=yes
+ fi
+ else
+ # Don't allow undefined symbols.
+ allow_undefined_flag="$no_undefined_flag"
+ fi
+
+ dependency_libs="$deplibs"
+ case "$host" in
+ *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *)
+ # Add libc to deplibs on all other systems.
+ deplibs="$deplibs -lc"
+ ;;
+ esac
+ fi
+
+ # Create the output directory, or remove our outputs if we need to.
+ if test -d $output_objdir; then
+ $show "${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.*"
+ $run ${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.*
+ else
+ $show "$mkdir $output_objdir"
+ $run $mkdir $output_objdir
+ status=$?
+ if test $status -ne 0 && test ! -d $output_objdir; then
+ exit $status
+ fi
+ fi
+
+ # Now set the variables for building old libraries.
+ if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+ oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+ fi
+
+ if test "$build_libtool_libs" = yes; then
+ # Transform deplibs into only deplibs that can be linked in shared.
+ name_save=$name
+ libname_save=$libname
+ release_save=$release
+ versuffix_save=$versuffix
+ major_save=$major
+ # I'm not sure if I'm treating the release correctly. I think
+ # release should show up in the -l (ie -lgmp5) so we don't want to
+ # add it in twice. Is that correct?
+ release=""
+ versuffix=""
+ major=""
+ newdeplibs=
+ droppeddeps=no
+ case "$deplibs_check_method" in
+ pass_all)
+ # Don't check for shared/static. Everything works.
+ # This might be a little naive. We might want to check
+ # whether the library exists or not. But this is on
+ # osf3 & osf4 and I'm not really sure... Just
+ # implementing what was already the behaviour.
+ newdeplibs=$deplibs
+ ;;
+ test_compile)
+ # This code stresses the "libraries are programs" paradigm to its
+ # limits. Maybe even breaks it. We compile a program, linking it
+ # against the deplibs as a proxy for the library. Then we can check
+ # whether they linked in statically or dynamically with ldd.
+ $rm conftest.c
+ cat > conftest.c <<EOF
+ int main() { return 0; }
+EOF
+ $rm conftest
+ $C_compiler -o conftest conftest.c $deplibs
+ if test $? -eq 0 ; then
+ ldd_output=`ldd conftest`
+ for i in $deplibs; do
+ name="`expr $i : '-l\(.*\)'`"
+ # If $name is empty we are operating on a -L argument.
+ if test "$name" != "" ; then
+ libname=`eval \\$echo \"$libname_spec\"`
+ deplib_matches=`eval \\$echo \"$library_names_spec\"`
+ set dummy $deplib_matches
+ deplib_match=$2
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ echo
+ echo "*** Warning: This library needs some functionality provided by $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have."
+ fi
+ else
+ newdeplibs="$newdeplibs $i"
+ fi
+ done
+ else
+ # Error occured in the first compile. Let's try to salvage the situation:
+ # Compile a seperate program for each library.
+ for i in $deplibs; do
+ name="`expr $i : '-l\(.*\)'`"
+ # If $name is empty we are operating on a -L argument.
+ if test "$name" != "" ; then
+ $rm conftest
+ $C_compiler -o conftest conftest.c $i
+ # Did it work?
+ if test $? -eq 0 ; then
+ ldd_output=`ldd conftest`
+ libname=`eval \\$echo \"$libname_spec\"`
+ deplib_matches=`eval \\$echo \"$library_names_spec\"`
+ set dummy $deplib_matches
+ deplib_match=$2
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ echo
+ echo "*** Warning: This library needs some functionality provided by $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have."
+ fi
+ else
+ droppeddeps=yes
+ echo
+ echo "*** Warning! Library $i is needed by this library but I was not able to"
+ echo "*** make it link in! You will probably need to install it or some"
+ echo "*** library that it depends on before this library will be fully"
+ echo "*** functional. Installing it before continuing would be even better."
+ fi
+ else
+ newdeplibs="$newdeplibs $i"
+ fi
+ done
+ fi
+ ;;
+ file_magic*)
+ set dummy $deplibs_check_method
+ file_magic_regex="`expr \"$deplibs_check_method\" : \"$2 \(.*\)\"`"
+ for a_deplib in $deplibs; do
+ name="`expr $a_deplib : '-l\(.*\)'`"
+ # If $name is empty we are operating on a -L argument.
+ if test "$name" != "" ; then
+ libname=`eval \\$echo \"$libname_spec\"`
+ for i in $lib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ # Follow soft links.
+ if ls -lLd "$potlib" 2>/dev/null \
+ | grep " -> " >/dev/null; then
+ continue
+ fi
+ # The statement above tries to avoid entering an
+ # endless loop below, in case of cyclic links.
+ # We might still enter an endless loop, since a link
+ # loop can be closed while we follow links,
+ # but so what?
+ potlib="$potent_lib"
+ while test -h "$potlib" 2>/dev/null; do
+ potliblink=`ls -ld $potlib | sed 's/.* -> //'`
+ case "$potliblink" in
+ [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+ *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \
+ | sed 10q \
+ | egrep "$file_magic_regex" > /dev/null; then
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ break 2
+ fi
+ done
+ done
+ if test -n "$a_deplib" ; then
+ droppeddeps=yes
+ echo
+ echo "*** Warning: This library needs some functionality provided by $a_deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have."
+ fi
+ else
+ # Add a -L argument.
+ newdeplibs="$newdeplibs $a_deplib"
+ fi
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=""
+ if $echo "X $deplibs" | $Xsed -e 's/ -lc$//' \
+ -e 's/ -[LR][^ ]*//g' -e 's/[ ]//g' |
+ grep . >/dev/null; then
+ echo
+ if test "X$deplibs_check_method" = "Xnone"; then
+ echo "*** Warning: inter-library dependencies are not supported in this platform."
+ else
+ echo "*** Warning: inter-library dependencies are not known to be supported."
+ fi
+ echo "*** All declared inter-library dependencies are being dropped."
+ droppeddeps=yes
+ fi
+ ;;
+ esac
+ versuffix=$versuffix_save
+ major=$major_save
+ release=$release_save
+ libname=$libname_save
+ name=$name_save
+
+ if test "$droppeddeps" = yes; then
+ if test "$module" = yes; then
+ echo
+ echo "*** Warning: libtool could not satisfy all declared inter-library"
+ echo "*** dependencies of module $libname. Therefore, libtool will create"
+ echo "*** a static module, that should work as long as the dlopening"
+ echo "*** application is linked with the -dlopen flag."
+ if test -z "$global_symbol_pipe"; then
+ echo
+ echo "*** However, this would only work if libtool was able to extract symbol"
+ echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ echo "*** not find such a program. So, this module is probably useless."
+ echo "*** \`nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test "$build_old_libs" = no; then
+ oldlibs="$output_objdir/$libname.$libext"
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ else
+ echo "*** The inter-library dependencies that have been dropped here will be"
+ echo "*** automatically added whenever a program is linked with this library"
+ echo "*** or is declared to -dlopen it."
+ fi
+ fi
+ # Done checking deplibs!
+ deplibs=$newdeplibs
+ fi
+
+ # All the library-specific variables (install_libdir is set above).
+ library_names=
+ old_library=
+ dlname=
+
+ # Test again, we may have decided not to build it any more
+ if test "$build_libtool_libs" = yes; then
+ # Get the real and link names of the library.
+ eval library_names=\"$library_names_spec\"
+ set dummy $library_names
+ realname="$2"
+ shift; shift
+
+ if test -n "$soname_spec"; then
+ eval soname=\"$soname_spec\"
+ else
+ soname="$realname"
+ fi
+
+ lib="$output_objdir/$realname"
+ for link
+ do
+ linknames="$linknames $link"
+ done
+
+ # Ensure that we have .o objects for linkers which dislike .lo
+ # (e.g. aix) incase we are running --disable-static
+ for obj in $libobjs; do
+ oldobj=`$echo "X$obj" | $Xsed -e "$lo2o"`
+ if test ! -f $oldobj; then
+ $show "${LN_S} $obj $oldobj"
+ $run ${LN_S} $obj $oldobj || exit $?
+ fi
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+
+ if test -n "$whole_archive_flag_spec"; then
+ if test -n "$convenience"; then
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ fi
+ else
+ gentop="$output_objdir/${outputname}x"
+ $show "${rm}r $gentop"
+ $run ${rm}r "$gentop"
+ $show "mkdir $gentop"
+ $run mkdir "$gentop"
+ status=$?
+ if test $status -ne 0 && test ! -d "$gentop"; then
+ exit $status
+ fi
+ generated="$generated $gentop"
+
+ for xlib in $convenience; do
+ # Extract the objects.
+ case "$xlib" in
+ [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+ *) xabs=`pwd`"/$xlib" ;;
+ esac
+ xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+ xdir="$gentop/$xlib"
+
+ $show "${rm}r $xdir"
+ $run ${rm}r "$xdir"
+ $show "mkdir $xdir"
+ $run mkdir "$xdir"
+ status=$?
+ if test $status -ne 0 && test ! -d "$xdir"; then
+ exit $status
+ fi
+ $show "(cd $xdir && $AR x $xabs)"
+ $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+
+ libobjs="$libobjs "`find $xdir -name \*.o -print -o -name \*.lo -print | $NL2SP`
+ done
+ fi
+
+ if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+ eval flag=\"$thread_safe_flag_spec\"
+ linkopts="$linkopts $flag"
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+ $show "generating symbol list for \`$libname.la'"
+ export_symbols="$output_objdir/$libname.exp"
+ $run $rm $export_symbols
+ eval cmds=\"$export_symbols_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || exit $?
+ done
+ IFS="$save_ifs"
+ if test -n "$export_symbols_regex"; then
+ $show "egrep -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\""
+ $run eval 'egrep -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ $show "$mv \"${export_symbols}T\" \"$export_symbols\""
+ $run eval '$mv "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+ fi
+
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"'
+ fi
+
+ # Do each of the archive commands.
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ eval cmds=\"$archive_expsym_cmds\"
+ else
+ eval cmds=\"$archive_cmds\"
+ fi
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || exit $?
+ done
+ IFS="$save_ifs"
+
+ # Create links to the real library.
+ for linkname in $linknames; do
+ if test "$realname" != "$linkname"; then
+ $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)"
+ $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $?
+ fi
+ done
+
+ # If -module or -export-dynamic was specified, set the dlname.
+ if test "$module" = yes || test "$export_dynamic" = yes; then
+ # On all known operating systems, these are identical.
+ dlname="$soname"
+ fi
+ fi
+ ;;
+
+ *.lo | *.o | *.obj)
+ if test -n "$link_against_libtool_libs"; then
+ $echo "$modename: error: cannot link libtool libraries into objects" 1>&2
+ exit 1
+ fi
+
+ if test -n "$deplibs"; then
+ $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2
+ fi
+
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2
+ fi
+
+ if test -n "$rpath"; then
+ $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2
+ fi
+
+ if test -n "$xrpath"; then
+ $echo "$modename: warning: \`-R' is ignored for objects" 1>&2
+ fi
+
+ if test -n "$vinfo"; then
+ $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2
+ fi
+
+ if test -n "$release"; then
+ $echo "$modename: warning: \`-release' is ignored for objects" 1>&2
+ fi
+
+ case "$output" in
+ *.lo)
+ if test -n "$objs"; then
+ $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2
+ exit 1
+ fi
+ libobj="$output"
+ obj=`$echo "X$output" | $Xsed -e "$lo2o"`
+ ;;
+ *)
+ libobj=
+ obj="$output"
+ ;;
+ esac
+
+ # Delete the old objects.
+ $run $rm $obj $libobj
+
+ # Create the old-style object.
+ reload_objs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`
+
+ output="$obj"
+ eval cmds=\"$reload_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || exit $?
+ done
+ IFS="$save_ifs"
+
+ # Exit if we aren't doing a library object file.
+ test -z "$libobj" && exit 0
+
+ if test "$build_libtool_libs" != yes; then
+ # Create an invalid libtool object if no PIC, so that we don't
+ # accidentally link it into a program.
+ $show "echo timestamp > $libobj"
+ $run eval "echo timestamp > $libobj" || exit $?
+ exit 0
+ fi
+
+ if test -n "$pic_flag"; then
+ # Only do commands if we really have different PIC objects.
+ reload_objs="$libobjs"
+ output="$libobj"
+ eval cmds=\"$reload_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || exit $?
+ done
+ IFS="$save_ifs"
+ else
+ # Just create a symlink.
+ $show $rm $libobj
+ $run $rm $libobj
+ $show "$LN_S $obj $libobj"
+ $run $LN_S $obj $libobj || exit $?
+ fi
+
+ exit 0
+ ;;
+
+ # Anything else should be a program.
+ *)
+ if test -n "$vinfo"; then
+ $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2
+ fi
+
+ if test -n "$release"; then
+ $echo "$modename: warning: \`-release' is ignored for programs" 1>&2
+ fi
+
+ if test "$preload" = yes; then
+ if test "$dlopen" = unknown && test "$dlopen_self" = unknown &&
+ test "$dlopen_self_static" = unknown; then
+ $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support."
+ fi
+ fi
+
+ if test -n "$rpath$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ for libdir in $rpath $xrpath; do
+ # This is the magic to use -rpath.
+ case "$compile_rpath " in
+ *" $libdir "*) ;;
+ *) compile_rpath="$compile_rpath $libdir" ;;
+ esac
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir" ;;
+ esac
+ done
+ fi
+
+ # Now hardcode the library paths
+ rpath=
+ hardcode_libdirs=
+ for libdir in $compile_rpath $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) perm_rpath="$perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ compile_rpath="$rpath"
+
+ rpath=
+ hardcode_libdirs=
+ for libdir in $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ finalize_rpath="$rpath"
+
+ output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'`
+ if test "X$output_objdir" = "X$output"; then
+ output_objdir="$objdir"
+ else
+ output_objdir="$output_objdir/$objdir"
+ fi
+
+ # Create the binary in the object directory, then wrap it.
+ if test ! -d $output_objdir; then
+ $show "$mkdir $output_objdir"
+ $run $mkdir $output_objdir
+ status=$?
+ if test $status -ne 0 && test ! -d $output_objdir; then
+ exit $status
+ fi
+ fi
+
+ if test -n "$libobjs" && test "$build_old_libs" = yes; then
+ # Transform all the library objects into standard objects.
+ compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ fi
+
+ dlsyms=
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" = yes; then
+ if test -n "$NM" && test -n "$global_symbol_pipe"; then
+ dlsyms="${outputname}S.c"
+ else
+ $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2
+ fi
+ fi
+
+ if test -n "$dlsyms"; then
+ case "$dlsyms" in
+ "") ;;
+ *.c)
+ # Discover the nlist of each of the dlfiles.
+ nlist="$output_objdir/${outputname}.nm"
+
+ $show "$rm $nlist ${nlist}S ${nlist}T"
+ $run $rm "$nlist" "${nlist}S" "${nlist}T"
+
+ # Parse the name list into a source file.
+ $show "creating $output_objdir/$dlsyms"
+
+ test -z "$run" && $echo > "$output_objdir/$dlsyms" "\
+/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */
+/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* Prevent the only kind of declaration conflicts we can make. */
+#define lt_preloaded_symbols some_other_symbol
+
+/* External symbol declarations for the compiler. */\
+"
+
+ if test "$dlself" = yes; then
+ $show "generating symbol list for \`$output'"
+
+ test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist"
+
+ # Add our own program objects to the symbol list.
+ progfiles=`$echo "X$objs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ for arg in $progfiles; do
+ $show "extracting global C symbols from \`$arg'"
+ $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -n "$exclude_expsyms"; then
+ $run eval 'egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+ $run eval '$mv "$nlist"T "$nlist"'
+ fi
+
+ if test -n "$export_symbols_regex"; then
+ $run eval 'egrep -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+ $run eval '$mv "$nlist"T "$nlist"'
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ export_symbols="$output_objdir/$output.exp"
+ $run $rm $export_symbols
+ $run eval "sed -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ else
+ $run eval "sed -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"'
+ $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T'
+ $run eval 'mv "$nlist"T "$nlist"'
+ fi
+ fi
+
+ for arg in $dlprefiles; do
+ $show "extracting global C symbols from \`$arg'"
+ name=`echo "$arg" | sed -e 's%^.*/%%'`
+ $run eval 'echo ": $name " >> "$nlist"'
+ $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -z "$run"; then
+ # Make sure we have at least an empty file.
+ test -f "$nlist" || : > "$nlist"
+
+ if test -n "$exclude_expsyms"; then
+ egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+ $mv "$nlist"T "$nlist"
+ fi
+
+ # Try sorting and uniquifying the output.
+ if grep -v "^: " < "$nlist" | sort +2 | uniq > "$nlist"S; then
+ :
+ else
+ grep -v "^: " < "$nlist" > "$nlist"S
+ fi
+
+ if test -f "$nlist"S; then
+ eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"'
+ else
+ echo '/* NONE */' >> "$output_objdir/$dlsyms"
+ fi
+
+ $echo >> "$output_objdir/$dlsyms" "\
+
+#undef lt_preloaded_symbols
+
+#if defined (__STDC__) && __STDC__
+# define lt_ptr_t void *
+#else
+# define lt_ptr_t char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ lt_ptr_t address;
+}
+lt_preloaded_symbols[] =
+{\
+"
+
+ sed -n -e 's/^: \([^ ]*\) $/ {\"\1\", (lt_ptr_t) 0},/p' \
+ -e 's/^. \([^ ]*\) \([^ ]*\)$/ {"\2", (lt_ptr_t) \&\2},/p' \
+ < "$nlist" >> "$output_objdir/$dlsyms"
+
+ $echo >> "$output_objdir/$dlsyms" "\
+ {0, (lt_ptr_t) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+ fi
+
+ pic_flag_for_symtable=
+ case "$host" in
+ # compiling the symbol table file with pic_flag works around
+ # a FreeBSD bug that causes programs to crash when -lm is
+ # linked before any other PIC object. But we must not use
+ # pic_flag when linking with -static. The problem exists in
+ # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+ *-*-freebsd2*|*-*-freebsd3.0*)
+ case "$compile_command " in
+ *" -static "*) ;;
+ *) pic_flag_for_symtable=" $pic_flag -DPIC -DFREEBSD_WORKAROUND";;
+ esac
+ esac
+
+ # Now compile the dynamic symbol file.
+ $show "(cd $output_objdir && $C_compiler -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")"
+ $run eval '(cd $output_objdir && $C_compiler -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $?
+
+ # Clean up the generated files.
+ $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T"
+ $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T"
+
+ # Transform the symbol file into the correct name.
+ compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+ finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+ ;;
+ *)
+ $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2
+ exit 1
+ ;;
+ esac
+ else
+ # We keep going just in case the user didn't refer to
+ # lt_preloaded_symbols. The linker will fail if global_symbol_pipe
+ # really was required.
+
+ # Nullify the symbol file.
+ compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+ finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+ fi
+
+ if test -z "$link_against_libtool_libs" || test "$build_libtool_libs" != yes; then
+ # Replace the output file specification.
+ compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+ link_command="$compile_command$compile_rpath"
+
+ # We have no uninstalled library dependencies, so finalize right now.
+ $show "$link_command"
+ $run eval "$link_command"
+ status=$?
+
+ # Delete the generated files.
+ if test -n "$dlsyms"; then
+ $show "$rm $output_objdir/${outputname}S.${objext}"
+ $run $rm "$output_objdir/${outputname}S.${objext}"
+ fi
+
+ exit $status
+ fi
+
+ if test -n "$shlibpath_var"; then
+ # We should set the shlibpath_var
+ rpath=
+ for dir in $temp_rpath; do
+ case "$dir" in
+ [\\/]* | [A-Za-z]:[\\/]*)
+ # Absolute path.
+ rpath="$rpath$dir:"
+ ;;
+ *)
+ # Relative path: add a thisdir entry.
+ rpath="$rpath\$thisdir/$dir:"
+ ;;
+ esac
+ done
+ temp_rpath="$rpath"
+ fi
+
+ if test -n "$compile_shlibpath$finalize_shlibpath"; then
+ compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+ fi
+ if test -n "$finalize_shlibpath"; then
+ finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+ fi
+
+ compile_var=
+ finalize_var=
+ if test -n "$runpath_var"; then
+ if test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ if test -n "$finalize_perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $finalize_perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ fi
+
+ if test "$hardcode_action" = relink; then
+ # Fast installation is not supported
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+ $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2
+ $echo "$modename: \`$output' will be relinked during installation" 1>&2
+ else
+ if test "$fast_install" != no; then
+ link_command="$finalize_var$compile_command$finalize_rpath"
+ if test "$fast_install" = yes; then
+ relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+ else
+ # fast_install is set to needless
+ relink_command=
+ fi
+ else
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+ fi
+ fi
+
+ # Replace the output file specification.
+ link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+ # Delete the old output files.
+ $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+ $show "$link_command"
+ $run eval "$link_command" || exit $?
+
+ # Now create the wrapper script.
+ $show "creating $output"
+
+ # Quote the relink command for shipping.
+ if test -n "$relink_command"; then
+ relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Quote $echo for shipping.
+ if test "X$echo" = "X$SHELL $0 --fallback-echo"; then
+ case "$0" in
+ [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $0 --fallback-echo";;
+ *) qecho="$SHELL `pwd`/$0 --fallback-echo";;
+ esac
+ qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"`
+ else
+ qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Only actually do things if our run command is non-null.
+ if test -z "$run"; then
+ # win32 will think the script is a binary if it has
+ # a .exe suffix, so we strip it off here.
+ case $output in
+ *.exe) output=`echo $output|sed 's,.exe$,,'` ;;
+ esac
+ $rm $output
+ trap "$rm $output; exit 1" 1 2 15
+
+ $echo > $output "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test \"\${CDPATH+set}\" = set; then CDPATH=; export CDPATH; fi
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+ # install mode needs the following variable:
+ link_against_libtool_libs='$link_against_libtool_libs'
+else
+ # When we are sourced in execute mode, \$file and \$echo are already set.
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ echo=\"$qecho\"
+ file=\"\$0\"
+ # Make sure echo works.
+ if test \"X\$1\" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+ elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then
+ # Yippee, \$echo works!
+ :
+ else
+ # Restart under the correct shell, and then maybe \$echo will work.
+ exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+ fi
+ fi\
+"
+ $echo >> $output "\
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+ test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=\`ls -ld \"\$file\" | sed -n 's/.*-> //p'\`
+ while test -n \"\$file\"; do
+ destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+ # If there was a directory component, then change thisdir.
+ if test \"x\$destdir\" != \"x\$file\"; then
+ case \"\$destdir\" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir=\"\$destdir\" ;;
+ *) thisdir=\"\$thisdir/\$destdir\" ;;
+ esac
+ fi
+
+ file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+ file=\`ls -ld \"\$thisdir/\$file\" | sed -n 's/.*-> //p'\`
+ done
+
+ # Try to get the absolute directory name.
+ absdir=\`cd \"\$thisdir\" && pwd\`
+ test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+ if test "$fast_install" = yes; then
+ echo >> $output "\
+ program=lt-'$outputname'
+ progdir=\"\$thisdir/$objdir\"
+
+ if test ! -f \"\$progdir/\$program\" || \\
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | sed 1q\`; \\
+ test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+ file=\"\$\$-\$program\"
+
+ if test ! -d \"\$progdir\"; then
+ $mkdir \"\$progdir\"
+ else
+ $rm \"\$progdir/\$file\"
+ fi"
+
+ echo >> $output "\
+
+ # relink executable if necessary
+ if test -n \"\$relink_command\"; then
+ if (cd \"\$thisdir\" && eval \$relink_command); then :
+ else
+ $rm \"\$progdir/\$file\"
+ exit 1
+ fi
+ fi
+
+ $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+ { $rm \"\$progdir/\$program\";
+ $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+ $rm \"\$progdir/\$file\"
+ fi"
+ else
+ echo >> $output "\
+ program='$outputname'
+ progdir=\"\$thisdir/$objdir\"
+"
+ fi
+
+ echo >> $output "\
+
+ if test -f \"\$progdir/\$program\"; then"
+
+ # Export our shlibpath_var if we have one.
+ if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ $echo >> $output "\
+ # Add our own library path to $shlibpath_var
+ $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+ # Some systems cannot cope with colon-terminated $shlibpath_var
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ # fixup the dll searchpath if we need to.
+ if test -n "$dllsearchpath"; then
+ $echo >> $output "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ $echo >> $output "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+"
+ case $host in
+ *-*-cygwin* | *-*-mingw | *-*-os2*)
+ # win32 systems need to use the prog path for dll
+ # lookup to work
+ $echo >> $output "\
+ exec \$progdir\\\\\$program \${1+\"\$@\"}
+"
+ ;;
+ *)
+ $echo >> $output "\
+ # Export the path to the program.
+ PATH=\"\$progdir:\$PATH\"
+ export PATH
+
+ exec \$program \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $echo >> $output "\
+ \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\"
+ exit 1
+ fi
+ else
+ # The program doesn't exist.
+ \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2
+ \$echo \"This script is just a wrapper for \$program.\" 1>&2
+ echo \"See the $PACKAGE documentation for more information.\" 1>&2
+ exit 1
+ fi
+fi\
+"
+ chmod +x $output
+ fi
+ exit 0
+ ;;
+ esac
+
+ # See if we need to build an old-fashioned archive.
+ for oldlib in $oldlibs; do
+
+ if test "$build_libtool_libs" = convenience; then
+ oldobjs="$libobjs_save"
+ addlibs="$convenience"
+ build_libtool_libs=no
+ else
+ if test "$build_libtool_libs" = module; then
+ oldobjs="$libobjs_save"
+ build_libtool_libs=no
+ else
+ oldobjs="$objs "`$echo "X$libobjs_save" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`
+ fi
+ addlibs="$old_convenience"
+ fi
+
+ if test -n "$addlibs"; then
+ gentop="$output_objdir/${outputname}x"
+ $show "${rm}r $gentop"
+ $run ${rm}r "$gentop"
+ $show "mkdir $gentop"
+ $run mkdir "$gentop"
+ status=$?
+ if test $status -ne 0 && test ! -d "$gentop"; then
+ exit $status
+ fi
+ generated="$generated $gentop"
+
+ # Add in members from convenience archives.
+ for xlib in $addlibs; do
+ # Extract the objects.
+ case "$xlib" in
+ [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+ *) xabs=`pwd`"/$xlib" ;;
+ esac
+ xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+ xdir="$gentop/$xlib"
+
+ $show "${rm}r $xdir"
+ $run ${rm}r "$xdir"
+ $show "mkdir $xdir"
+ $run mkdir "$xdir"
+ status=$?
+ if test $status -ne 0 && test ! -d "$xdir"; then
+ exit $status
+ fi
+ $show "(cd $xdir && $AR x $xabs)"
+ $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+
+ oldobjs="$oldobjs "`find $xdir -name \*.${objext} -print -o -name \*.lo -print | $NL2SP`
+ done
+ fi
+
+ # Do each command in the archive commands.
+ if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+ eval cmds=\"$old_archive_from_new_cmds\"
+ else
+ # Ensure that we have .o objects in place incase we decided
+ # not to build a shared library, and have fallen back to building
+ # static libs even though --disable-static was passed!
+ for oldobj in $oldobjs; do
+ if test ! -f $oldobj; then
+ obj=`$echo "X$oldobj" | $Xsed -e "$o2lo"`
+ $show "${LN_S} $obj $oldobj"
+ $run ${LN_S} $obj $oldobj || exit $?
+ fi
+ done
+
+ eval cmds=\"$old_archive_cmds\"
+ fi
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || exit $?
+ done
+ IFS="$save_ifs"
+ done
+
+ if test -n "$generated"; then
+ $show "${rm}r$generated"
+ $run ${rm}r$generated
+ fi
+
+ # Now create the libtool archive.
+ case "$output" in
+ *.la)
+ old_library=
+ test "$build_old_libs" = yes && old_library="$libname.$libext"
+ $show "creating $output"
+
+ if test -n "$xrpath"; then
+ temp_xrpath=
+ for libdir in $xrpath; do
+ temp_xrpath="$temp_xrpath -R$libdir"
+ done
+ dependency_libs="$temp_xrpath $dependency_libs"
+ fi
+
+ # Only create the output if not a dry run.
+ if test -z "$run"; then
+ for installed in no yes; do
+ if test "$installed" = yes; then
+ if test -z "$install_libdir"; then
+ break
+ fi
+ output="$output_objdir/$outputname"i
+ fi
+ $rm $output
+ $echo > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$dlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'\
+"
+ done
+ fi
+
+ # Do a symbolic link so that the libtool archive can be found in
+ # LD_LIBRARY_PATH before the program is installed.
+ $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)"
+ $run eval "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" || exit $?
+ ;;
+ esac
+ exit 0
+ ;;
+
+ # libtool install mode
+ install)
+ modename="$modename: install"
+
+ # There may be an optional sh(1) argument at the beginning of
+ # install_prog (especially on Windows NT).
+ if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh; then
+ # Aesthetically quote it.
+ arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"`
+ case "$arg" in
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*)
+ arg="\"$arg\""
+ ;;
+ esac
+ install_prog="$arg "
+ arg="$1"
+ shift
+ else
+ install_prog=
+ arg="$nonopt"
+ fi
+
+ # The real first argument should be the name of the installation program.
+ # Aesthetically quote it.
+ arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+ case "$arg" in
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*)
+ arg="\"$arg\""
+ ;;
+ esac
+ install_prog="$install_prog$arg"
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=no
+ stripme=
+ for arg
+ do
+ if test -n "$dest"; then
+ files="$files $dest"
+ dest="$arg"
+ continue
+ fi
+
+ case "$arg" in
+ -d) isdir=yes ;;
+ -f) prev="-f" ;;
+ -g) prev="-g" ;;
+ -m) prev="-m" ;;
+ -o) prev="-o" ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*) ;;
+
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ prev=
+ else
+ dest="$arg"
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+ case "$arg" in
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*)
+ arg="\"$arg\""
+ ;;
+ esac
+ install_prog="$install_prog $arg"
+ done
+
+ if test -z "$install_prog"; then
+ $echo "$modename: you must specify an install program" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ if test -n "$prev"; then
+ $echo "$modename: the \`$prev' option requires an argument" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ if test -z "$files"; then
+ if test -z "$dest"; then
+ $echo "$modename: no file or destination specified" 1>&2
+ else
+ $echo "$modename: you must specify a destination" 1>&2
+ fi
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ # Strip any trailing slash from the destination.
+ dest=`$echo "X$dest" | $Xsed -e 's%/$%%'`
+
+ # Check to see that the destination is a directory.
+ test -d "$dest" && isdir=yes
+ if test "$isdir" = yes; then
+ destdir="$dest"
+ destname=
+ else
+ destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'`
+ test "X$destdir" = "X$dest" && destdir=.
+ destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'`
+
+ # Not a directory, so check to see that there is only one file specified.
+ set dummy $files
+ if test $# -gt 2; then
+ $echo "$modename: \`$dest' is not a directory" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+ fi
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ for file in $files; do
+ case "$file" in
+ *.lo) ;;
+ *)
+ $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic="$magic"
+
+ staticlibs=
+ future_libdirs=
+ current_libdirs=
+ for file in $files; do
+
+ # Do each installation.
+ case "$file" in
+ *.a | *.lib)
+ # Do the static libraries later.
+ staticlibs="$staticlibs $file"
+ ;;
+
+ *.la)
+ # Check to see that this really is a libtool archive.
+ if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+ else
+ $echo "$modename: \`$file' is not a valid libtool archive" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ library_names=
+ old_library=
+ # If there is no directory component, then add one.
+ case "$file" in
+ */* | *\\*) . $file ;;
+ *) . ./$file ;;
+ esac
+
+ # Add the libdir to current_libdirs if it is the destination.
+ if test "X$destdir" = "X$libdir"; then
+ case "$current_libdirs " in
+ *" $libdir "*) ;;
+ *) current_libdirs="$current_libdirs $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) future_libdirs="$future_libdirs $libdir" ;;
+ esac
+ fi
+
+ dir="`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/"
+ test "X$dir" = "X$file/" && dir=
+ dir="$dir$objdir"
+
+ # See the names of the shared library.
+ set dummy $library_names
+ if test -n "$2"; then
+ realname="$2"
+ shift
+ shift
+
+ # Install the shared library and build the symlinks.
+ $show "$install_prog $dir/$realname $destdir/$realname"
+ $run eval "$install_prog $dir/$realname $destdir/$realname" || exit $?
+ test "X$dlname" = "X$realname" && dlname=
+
+ if test $# -gt 0; then
+ # Delete the old symlinks, and create new ones.
+ for linkname
+ do
+ test "X$dlname" = "X$linkname" && dlname=
+ if test "$linkname" != "$realname"; then
+ $show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+ $run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+ fi
+ done
+ fi
+
+ if test -n "$dlname"; then
+ # Install the dynamically-loadable library.
+ $show "$install_prog $dir/$dlname $destdir/$dlname"
+ $run eval "$install_prog $dir/$dlname $destdir/$dlname" || exit $?
+ fi
+
+ # Do each command in the postinstall commands.
+ lib="$destdir/$realname"
+ eval cmds=\"$postinstall_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || exit $?
+ done
+ IFS="$save_ifs"
+ fi
+
+ # Install the pseudo-library for information purposes.
+ name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+ instname="$dir/$name"i
+ $show "$install_prog $instname $destdir/$name"
+ $run eval "$install_prog $instname $destdir/$name" || exit $?
+
+ # Maybe install the static library, too.
+ test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+ ;;
+
+ *.lo)
+ # Install (i.e. copy) a libtool object.
+
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+ destfile="$destdir/$destfile"
+ fi
+
+ # Deduce the name of the destination old-style object file.
+ case "$destfile" in
+ *.lo)
+ staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"`
+ ;;
+ *.o | *.obj)
+ staticdest="$destfile"
+ destfile=
+ ;;
+ *)
+ $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ ;;
+ esac
+
+ # Install the libtool object if requested.
+ if test -n "$destfile"; then
+ $show "$install_prog $file $destfile"
+ $run eval "$install_prog $file $destfile" || exit $?
+ fi
+
+ # Install the old object if enabled.
+ if test "$build_old_libs" = yes; then
+ # Deduce the name of the old-style object file.
+ staticobj=`$echo "X$file" | $Xsed -e "$lo2o"`
+
+ $show "$install_prog $staticobj $staticdest"
+ $run eval "$install_prog \$staticobj \$staticdest" || exit $?
+ fi
+ exit 0
+ ;;
+
+ *)
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+ destfile="$destdir/$destfile"
+ fi
+
+ # Do a test to see if this is really a libtool program.
+ if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+ link_against_libtool_libs=
+ relink_command=
+
+ # If there is no directory component, then add one.
+ case "$file" in
+ */* | *\\*) . $file ;;
+ *) . ./$file ;;
+ esac
+
+ # Check the variables that should have been set.
+ if test -z "$link_against_libtool_libs"; then
+ $echo "$modename: invalid libtool wrapper script \`$file'" 1>&2
+ exit 1
+ fi
+
+ finalize=yes
+ for lib in $link_against_libtool_libs; do
+ # Check to see that each library is installed.
+ libdir=
+ if test -f "$lib"; then
+ # If there is no directory component, then add one.
+ case "$lib" in
+ */* | *\\*) . $lib ;;
+ *) . ./$lib ;;
+ esac
+ fi
+ libfile="$libdir/`$echo "X$lib" | $Xsed -e 's%^.*/%%g'`"
+ if test -n "$libdir" && test ! -f "$libfile"; then
+ $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2
+ finalize=no
+ fi
+ done
+
+ outputname=
+ if test "$fast_install" = no && test -n "$relink_command"; then
+ if test "$finalize" = yes && test -z "$run"; then
+ tmpdir="/tmp"
+ test -n "$TMPDIR" && tmpdir="$TMPDIR"
+ tmpdir="$tmpdir/libtool-$$"
+ if $mkdir -p "$tmpdir" && chmod 700 "$tmpdir"; then :
+ else
+ $echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2
+ continue
+ fi
+ outputname="$tmpdir/$file"
+ # Replace the output file specification.
+ relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+ $show "$relink_command"
+ if $run eval "$relink_command"; then :
+ else
+ $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2
+ ${rm}r "$tmpdir"
+ continue
+ fi
+ file="$outputname"
+ else
+ $echo "$modename: warning: cannot relink \`$file'" 1>&2
+ fi
+ else
+ # Install the binary that we compiled earlier.
+ file=`$echo "X$file" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+ fi
+ fi
+
+ $show "$install_prog$stripme $file $destfile"
+ $run eval "$install_prog\$stripme \$file \$destfile" || exit $?
+ test -n "$outputname" && ${rm}r "$tmpdir"
+ ;;
+ esac
+ done
+
+ for file in $staticlibs; do
+ name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+
+ # Set up the ranlib parameters.
+ oldlib="$destdir/$name"
+
+ $show "$install_prog $file $oldlib"
+ $run eval "$install_prog \$file \$oldlib" || exit $?
+
+ # Do each command in the postinstall commands.
+ eval cmds=\"$old_postinstall_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || exit $?
+ done
+ IFS="$save_ifs"
+ done
+
+ if test -n "$future_libdirs"; then
+ $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2
+ fi
+
+ if test -n "$current_libdirs"; then
+ # Maybe just do a dry run.
+ test -n "$run" && current_libdirs=" -n$current_libdirs"
+ exec $SHELL $0 --finish$current_libdirs
+ exit 1
+ fi
+
+ exit 0
+ ;;
+
+ # libtool finish mode
+ finish)
+ modename="$modename: finish"
+ libdirs="$nonopt"
+ admincmds=
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ for dir
+ do
+ libdirs="$libdirs $dir"
+ done
+
+ for libdir in $libdirs; do
+ if test -n "$finish_cmds"; then
+ # Do each command in the finish commands.
+ eval cmds=\"$finish_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd" || admincmds="$admincmds
+ $cmd"
+ done
+ IFS="$save_ifs"
+ fi
+ if test -n "$finish_eval"; then
+ # Do the single finish_eval.
+ eval cmds=\"$finish_eval\"
+ $run eval "$cmds" || admincmds="$admincmds
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ test "$show" = : && exit 0
+
+ echo "----------------------------------------------------------------------"
+ echo "Libraries have been installed in:"
+ for libdir in $libdirs; do
+ echo " $libdir"
+ done
+ echo
+ echo "If you ever happen to want to link against installed libraries"
+ echo "in a given directory, LIBDIR, you must either use libtool, and"
+ echo "specify the full pathname of the library, or use \`-LLIBDIR'"
+ echo "flag during linking and do at least one of the following:"
+ if test -n "$shlibpath_var"; then
+ echo " - add LIBDIR to the \`$shlibpath_var' environment variable"
+ echo " during execution"
+ fi
+ if test -n "$runpath_var"; then
+ echo " - add LIBDIR to the \`$runpath_var' environment variable"
+ echo " during linking"
+ fi
+ if test -n "$hardcode_libdir_flag_spec"; then
+ libdir=LIBDIR
+ eval flag=\"$hardcode_libdir_flag_spec\"
+
+ echo " - use the \`$flag' linker flag"
+ fi
+ if test -n "$admincmds"; then
+ echo " - have your system administrator run these commands:$admincmds"
+ fi
+ if test -f /etc/ld.so.conf; then
+ echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+ fi
+ echo
+ echo "See any operating system documentation about shared libraries for"
+ echo "more information, such as the ld(1) and ld.so(8) manual pages."
+ echo "----------------------------------------------------------------------"
+ exit 0
+ ;;
+
+ # libtool execute mode
+ execute)
+ modename="$modename: execute"
+
+ # The first argument is the command name.
+ cmd="$nonopt"
+ if test -z "$cmd"; then
+ $echo "$modename: you must specify a COMMAND" 1>&2
+ $echo "$help"
+ exit 1
+ fi
+
+ # Handle -dlopen flags immediately.
+ for file in $execute_dlfiles; do
+ if test ! -f "$file"; then
+ $echo "$modename: \`$file' is not a file" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ dir=
+ case "$file" in
+ *.la)
+ # Check to see that this really is a libtool archive.
+ if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+ else
+ $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ # Read the libtool library.
+ dlname=
+ library_names=
+
+ # If there is no directory component, then add one.
+ case "$file" in
+ */* | *\\*) . $file ;;
+ *) . ./$file ;;
+ esac
+
+ # Skip this library if it cannot be dlopened.
+ if test -z "$dlname"; then
+ # Warn if it was a shared library.
+ test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'"
+ continue
+ fi
+
+ dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+ test "X$dir" = "X$file" && dir=.
+
+ if test -f "$dir/$objdir/$dlname"; then
+ dir="$dir/$objdir"
+ else
+ $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2
+ exit 1
+ fi
+ ;;
+
+ *.lo)
+ # Just add the directory containing the .lo file.
+ dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+ test "X$dir" = "X$file" && dir=.
+ ;;
+
+ *)
+ $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2
+ continue
+ ;;
+ esac
+
+ # Get the absolute pathname.
+ absdir=`cd "$dir" && pwd`
+ test -n "$absdir" && dir="$absdir"
+
+ # Now add the directory to shlibpath_var.
+ if eval "test -z \"\$$shlibpath_var\""; then
+ eval "$shlibpath_var=\"\$dir\""
+ else
+ eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+ fi
+ done
+
+ # This variable tells wrapper scripts just to set shlibpath_var
+ # rather than running their programs.
+ libtool_execute_magic="$magic"
+
+ # Check if any of the arguments is a wrapper script.
+ args=
+ for file
+ do
+ case "$file" in
+ -*) ;;
+ *)
+ # Do a test to see if this is really a libtool program.
+ if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+ # If there is no directory component, then add one.
+ case "$file" in
+ */* | *\\*) . $file ;;
+ *) . ./$file ;;
+ esac
+
+ # Transform arg to wrapped name.
+ file="$progdir/$program"
+ fi
+ ;;
+ esac
+ # Quote arguments (to preserve shell metacharacters).
+ file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"`
+ args="$args \"$file\""
+ done
+
+ if test -z "$run"; then
+ # Export the shlibpath_var.
+ eval "export $shlibpath_var"
+
+ # Restore saved enviroment variables
+ if test "${save_LC_ALL+set}" = set; then
+ LC_ALL="$save_LC_ALL"; export LC_ALL
+ fi
+ if test "${save_LANG+set}" = set; then
+ LANG="$save_LANG"; export LANG
+ fi
+
+ # Now actually exec the command.
+ eval "exec \$cmd$args"
+
+ $echo "$modename: cannot exec \$cmd$args"
+ exit 1
+ else
+ # Display what would be done.
+ eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\""
+ $echo "export $shlibpath_var"
+ $echo "$cmd$args"
+ exit 0
+ fi
+ ;;
+
+ # libtool uninstall mode
+ uninstall)
+ modename="$modename: uninstall"
+ rm="$nonopt"
+ files=
+
+ for arg
+ do
+ case "$arg" in
+ -*) rm="$rm $arg" ;;
+ *) files="$files $arg" ;;
+ esac
+ done
+
+ if test -z "$rm"; then
+ $echo "$modename: you must specify an RM program" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ fi
+
+ for file in $files; do
+ dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+ test "X$dir" = "X$file" && dir=.
+ name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+
+ rmfiles="$file"
+
+ case "$name" in
+ *.la)
+ # Possibly a libtool archive, so verify it.
+ if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+ . $dir/$name
+
+ # Delete the libtool libraries and symlinks.
+ for n in $library_names; do
+ rmfiles="$rmfiles $dir/$n"
+ test "X$n" = "X$dlname" && dlname=
+ done
+ test -n "$dlname" && rmfiles="$rmfiles $dir/$dlname"
+ test -n "$old_library" && rmfiles="$rmfiles $dir/$old_library"
+
+ $show "$rm $rmfiles"
+ $run $rm $rmfiles
+
+ if test -n "$library_names"; then
+ # Do each command in the postuninstall commands.
+ eval cmds=\"$postuninstall_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd"
+ done
+ IFS="$save_ifs"
+ fi
+
+ if test -n "$old_library"; then
+ # Do each command in the old_postuninstall commands.
+ eval cmds=\"$old_postuninstall_cmds\"
+ IFS="${IFS= }"; save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ $show "$cmd"
+ $run eval "$cmd"
+ done
+ IFS="$save_ifs"
+ fi
+
+ # FIXME: should reinstall the best remaining shared library.
+ fi
+ ;;
+
+ *.lo)
+ if test "$build_old_libs" = yes; then
+ oldobj=`$echo "X$name" | $Xsed -e "$lo2o"`
+ rmfiles="$rmfiles $dir/$oldobj"
+ fi
+ $show "$rm $rmfiles"
+ $run $rm $rmfiles
+ ;;
+
+ *)
+ $show "$rm $rmfiles"
+ $run $rm $rmfiles
+ ;;
+ esac
+ done
+ exit 0
+ ;;
+
+ "")
+ $echo "$modename: you must specify a MODE" 1>&2
+ $echo "$generic_help" 1>&2
+ exit 1
+ ;;
+ esac
+
+ $echo "$modename: invalid operation mode \`$mode'" 1>&2
+ $echo "$generic_help" 1>&2
+ exit 1
+fi # test -z "$show_help"
+
+# We need to display help for each of the modes.
+case "$mode" in
+"") $echo \
+"Usage: $modename [OPTION]... [MODE-ARG]...
+
+Provide generalized library-building support services.
+
+ --config show all configuration variables
+ --debug enable verbose shell tracing
+-n, --dry-run display commands without modifying any files
+ --features display basic configuration information and exit
+ --finish same as \`--mode=finish'
+ --help display this help message and exit
+ --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS]
+ --quiet same as \`--silent'
+ --silent don't print informational messages
+ --version print version information
+
+MODE must be one of the following:
+
+ compile compile a source file into a libtool object
+ execute automatically set library path, then run a program
+ finish complete the installation of libtool libraries
+ install install libraries or executables
+ link create a library or an executable
+ uninstall remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for
+a more detailed description of MODE."
+ exit 0
+ ;;
+
+compile)
+ $echo \
+"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+ -o OUTPUT-FILE set the output file name to OUTPUT-FILE
+ -static always build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+ ;;
+
+execute)
+ $echo \
+"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+ -dlopen FILE add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+ ;;
+
+finish)
+ $echo \
+"Usage: $modename [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges. Use
+the \`--dry-run' option if you just want to see what would be executed."
+ ;;
+
+install)
+ $echo \
+"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command. The first component should be
+either the \`install' or \`cp' program.
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+ ;;
+
+link)
+ $echo \
+"Usage: $modename [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+ -all-static do not do any dynamic linking at all
+ -avoid-version do not add a version suffix if possible
+ -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
+ -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+ -export-symbols SYMFILE
+ try to export only the symbols listed in SYMFILE
+ -export-symbols-regex REGEX
+ try to export only the symbols matching REGEX
+ -LLIBDIR search LIBDIR for required installed libraries
+ -lNAME OUTPUT-FILE requires the installed library libNAME
+ -module build a library that can dlopened
+ -no-undefined declare that a library does not refer to external symbols
+ -o OUTPUT-FILE create OUTPUT-FILE from the specified objects
+ -release RELEASE specify package release information
+ -rpath LIBDIR the created library will eventually be installed in LIBDIR
+ -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries
+ -static do not do any dynamic linking of libtool libraries
+ -version-info CURRENT[:REVISION[:AGE]]
+ specify library version info [each variable defaults to 0]
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename. Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+ ;;
+
+uninstall)
+ $echo \
+"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+*)
+ $echo "$modename: invalid operation mode \`$mode'" 1>&2
+ $echo "$help" 1>&2
+ exit 1
+ ;;
+esac
+
+echo
+$echo "Try \`$modename --help' for more information about other modes."
+
+exit 0
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/neon/macros/ChangeLog b/neon/macros/ChangeLog
new file mode 100644
index 000000000..9a59cc69a
--- /dev/null
+++ b/neon/macros/ChangeLog
@@ -0,0 +1,26 @@
+Wed May 10 19:18:14 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon-xml-parser.m4: Error if no XML parser is found.
+
+Wed May 10 14:33:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon-checks.m4: New file.
+
+Wed May 10 14:26:57 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon-xml-parser.m4 (NEON_XML_PARSER): Use "neon_" prefix for
+ variables.
+
+Wed May 10 13:47:04 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * acconfig.h: New file.
+
+Wed May 10 13:42:16 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon-xml-parser.m4: New file.
+
+Sun May 7 21:57:32 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * gnome-x-checks.m4 (GNOME_X_CHECKS): Check for Gtk 1.2.7 or
+ later, passing "gthread" module argument.
+
diff --git a/neon/macros/ac_c_bigendian_cross.m4 b/neon/macros/ac_c_bigendian_cross.m4
new file mode 100644
index 000000000..f52c41be9
--- /dev/null
+++ b/neon/macros/ac_c_bigendian_cross.m4
@@ -0,0 +1,81 @@
+dnl @synopsis AC_C_BIGENDIAN_CROSS
+dnl
+dnl Check endianess even when crosscompiling
+dnl (partially based on the original AC_C_BIGENDIAN).
+dnl
+dnl The implementation will create a binary, and instead of running
+dnl the binary it will be grep'ed for some symbols that will look
+dnl different for different endianess of the binary.
+dnl
+dnl @version Id: ac_c_bigendian_cross.m4,v 1.1 2001/09/24 16:27:57 joe Exp
+dnl @author Guido Draheim <guidod@gmx.de>
+dnl
+AC_DEFUN([AC_C_BIGENDIAN_CROSS],
+[AC_CACHE_CHECK(whether byte ordering is bigendian, ac_cv_c_bigendian,
+[ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/param.h>], [
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif], [# It does; now see whether it defined to BIG_ENDIAN or not.
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/param.h>], [
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif], ac_cv_c_bigendian=yes, ac_cv_c_bigendian=no)])
+if test $ac_cv_c_bigendian = unknown; then
+AC_TRY_RUN([main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}], ac_cv_c_bigendian=no, ac_cv_c_bigendian=yes,
+[ echo $ac_n "cross-compiling... " 2>&AC_FD_MSG ])
+fi])
+if test $ac_cv_c_bigendian = unknown; then
+AC_MSG_CHECKING(to probe for byte ordering)
+[
+cat >conftest.c <<EOF
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii() { char* s = (char*) ascii_mm; s = (char*) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic() { char* s = (char*) ebcdic_mm; s = (char*) ebcdic_ii; }
+int main() { _ascii (); _ebcdic (); return 0; }
+EOF
+] if test -f conftest.c ; then
+ if ${CC-cc} conftest.c -o conftest.o && test -f conftest.o ; then
+ if test `grep -l BIGenDianSyS conftest.o` ; then
+ echo $ac_n ' big endian probe OK, ' 1>&AC_FD_MSG
+ ac_cv_c_bigendian=yes
+ fi
+ if test `grep -l LiTTleEnDian conftest.o` ; then
+ echo $ac_n ' little endian probe OK, ' 1>&AC_FD_MSG
+ if test $ac_cv_c_bigendian = yes ; then
+ ac_cv_c_bigendian=unknown;
+ else
+ ac_cv_c_bigendian=no
+ fi
+ fi
+ echo $ac_n 'guessing bigendian ... ' >&AC_FD_MSG
+ fi
+ fi
+AC_MSG_RESULT($ac_cv_c_bigendian)
+fi
+if test $ac_cv_c_bigendian = yes; then
+ AC_DEFINE(WORDS_BIGENDIAN, 1, [whether byteorder is bigendian])
+ BYTEORDER=4321
+else
+ BYTEORDER=1234
+fi
+AC_DEFINE_UNQUOTED(BYTEORDER, $BYTEORDER, [1234 = LIL_ENDIAN, 4321 = BIGENDIAN])
+if test $ac_cv_c_bigendian = unknown; then
+ AC_MSG_ERROR(unknown endianess - sorry, please pre-set ac_cv_c_bigendian)
+fi
+])
diff --git a/neon/macros/acconfig.h b/neon/macros/acconfig.h
new file mode 100644
index 000000000..07ee5ad3d
--- /dev/null
+++ b/neon/macros/acconfig.h
@@ -0,0 +1,4 @@
+/* Unconditionally define _GNU_SOURCE */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
diff --git a/neon/macros/aclocal-include.m4 b/neon/macros/aclocal-include.m4
new file mode 100644
index 000000000..abf6533fe
--- /dev/null
+++ b/neon/macros/aclocal-include.m4
@@ -0,0 +1,16 @@
+# aclocal-include.m4
+#
+# This macro adds the name macrodir to the set of directories
+# that `aclocal' searches for macros.
+
+# serial 1
+
+dnl AM_ACLOCAL_INCLUDE(macrodir)
+AC_DEFUN([AM_ACLOCAL_INCLUDE],
+[
+ AM_CONDITIONAL(INSIDE_GNOME_COMMON, test x = y)
+
+ test -n "$ACLOCAL_FLAGS" && ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+ for k in $1 ; do ACLOCAL="$ACLOCAL -I $k" ; done
+])
diff --git a/neon/macros/compiler-flags.m4 b/neon/macros/compiler-flags.m4
new file mode 100644
index 000000000..63f8e2e6a
--- /dev/null
+++ b/neon/macros/compiler-flags.m4
@@ -0,0 +1,109 @@
+dnl GNOME_COMPILE_WARNINGS
+dnl Turn on many useful compiler warnings
+dnl For now, only works on GCC
+AC_DEFUN([GNOME_COMPILE_WARNINGS],[
+ AC_ARG_ENABLE(compile-warnings,
+ [ --enable-compile-warnings=[no/minimum/yes] Turn on compiler warnings.],,enable_compile_warnings=minimum)
+
+ AC_MSG_CHECKING(what warning flags to pass to the C compiler)
+ warnCFLAGS=
+ if test "x$GCC" != xyes; then
+ enable_compile_warnings=no
+ fi
+
+ if test "x$enable_compile_warnings" != "xno"; then
+ if test "x$GCC" = "xyes"; then
+ case " $CFLAGS " in
+ *[\ \ ]-Wall[\ \ ]*) ;;
+ *) warnCFLAGS="-Wall -Wunused" ;;
+ esac
+
+ ## -W is not all that useful. And it cannot be controlled
+ ## with individual -Wno-xxx flags, unlike -Wall
+ if test "x$enable_compile_warnings" = "xyes"; then
+ warnCFLAGS="$warnCFLAGS -Wmissing-prototypes -Wmissing-declarations"
+ fi
+ fi
+ fi
+ AC_MSG_RESULT($warnCFLAGS)
+
+ AC_ARG_ENABLE(iso-c,
+ [ --enable-iso-c Try to warn if code is not ISO C ],,
+ enable_iso_c=no)
+
+ AC_MSG_CHECKING(what language compliance flags to pass to the C compiler)
+ complCFLAGS=
+ if test "x$enable_iso_c" != "xno"; then
+ if test "x$GCC" = "xyes"; then
+ case " $CFLAGS " in
+ *[\ \ ]-ansi[\ \ ]*) ;;
+ *) complCFLAGS="$complCFLAGS -ansi" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[\ \ ]-pedantic[\ \ ]*) ;;
+ *) complCFLAGS="$complCFLAGS -pedantic" ;;
+ esac
+ fi
+ fi
+ AC_MSG_RESULT($complCFLAGS)
+ if test "x$cflags_set" != "xyes"; then
+ CFLAGS="$CFLAGS $warnCFLAGS $complCFLAGS"
+ cflags_set=yes
+ AC_SUBST(cflags_set)
+ fi
+])
+
+dnl For C++, do basically the same thing.
+
+AC_DEFUN([GNOME_CXX_WARNINGS],[
+ AC_ARG_ENABLE(cxx-warnings,
+ [ --enable-cxx-warnings=[no/minimum/yes] Turn on compiler warnings.],,enable_cxx_warnings=minimum)
+
+ AC_MSG_CHECKING(what warning flags to pass to the C++ compiler)
+ warnCXXFLAGS=
+ if test "x$GCC" != xyes; then
+ enable_compile_warnings=no
+ fi
+ if test "x$enable_cxx_warnings" != "xno"; then
+ if test "x$GCC" = "xyes"; then
+ case " $CXXFLAGS " in
+ *[\ \ ]-Wall[\ \ ]*) ;;
+ *) warnCXXFLAGS="-Wall -Wno-unused" ;;
+ esac
+
+ ## -W is not all that useful. And it cannot be controlled
+ ## with individual -Wno-xxx flags, unlike -Wall
+ if test "x$enable_cxx_warnings" = "xyes"; then
+ warnCXXFLAGS="$warnCXXFLAGS -Wmissing-prototypes -Wmissing-declarations -Wshadow -Woverloaded-virtual"
+ fi
+ fi
+ fi
+ AC_MSG_RESULT($warnCXXFLAGS)
+
+ AC_ARG_ENABLE(iso-cxx,
+ [ --enable-iso-cxx Try to warn if code is not ISO C++ ],,
+ enable_iso_cxx=no)
+
+ AC_MSG_CHECKING(what language compliance flags to pass to the C++ compiler)
+ complCXXFLAGS=
+ if test "x$enable_iso_cxx" != "xno"; then
+ if test "x$GCC" = "xyes"; then
+ case " $CXXFLAGS " in
+ *[\ \ ]-ansi[\ \ ]*) ;;
+ *) complCXXFLAGS="$complCXXFLAGS -ansi" ;;
+ esac
+
+ case " $CXXFLAGS " in
+ *[\ \ ]-pedantic[\ \ ]*) ;;
+ *) complCXXFLAGS="$complCXXFLAGS -pedantic" ;;
+ esac
+ fi
+ fi
+ AC_MSG_RESULT($complCXXFLAGS)
+ if test "x$cxxflags_set" != "xyes"; then
+ CXXFLAGS="$CXXFLAGS $warnCXXFLAGS $complCXXFLAGS"
+ cxxflags_set=yes
+ AC_SUBST(cxxflags_set)
+ fi
+])
diff --git a/neon/macros/gnome-common.m4 b/neon/macros/gnome-common.m4
new file mode 100644
index 000000000..b72382970
--- /dev/null
+++ b/neon/macros/gnome-common.m4
@@ -0,0 +1,14 @@
+# gnome-common.m4
+#
+# This only for packages that are not in the GNOME CVS tree.
+
+dnl GNOME_COMMON_INIT
+
+AC_DEFUN([GNOME_COMMON_INIT],
+[
+ GNOME_ACLOCAL_DIR=`$ACLOCAL --print-ac-dir`/gnome
+ AC_SUBST(GNOME_ACLOCAL_DIR)
+
+ ACLOCAL="$ACLOCAL -I $GNOME_ACLOCAL_DIR"
+])
+
diff --git a/neon/macros/gnome-gnorba-check.m4 b/neon/macros/gnome-gnorba-check.m4
new file mode 100644
index 000000000..dbac0a6cf
--- /dev/null
+++ b/neon/macros/gnome-gnorba-check.m4
@@ -0,0 +1,35 @@
+dnl
+dnl GNOME_GNORBA_HOOK (script-if-gnorba-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if gnorba is not found.
+dnl
+
+AC_DEFUN([GNOME_GNORBA_HOOK],[
+ GNOME_ORBIT_HOOK([],$2)
+ AC_CACHE_CHECK([for gnorba libraries],gnome_cv_gnorba_found,[
+ gnome_cv_gnorba_found=no
+ if test x$gnome_cv_orbit_found = xyes; then
+ GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+ GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+ if test -n "$GNORBA_LIBS"; then
+ gnome_cv_gnorba_found=yes
+ fi
+ fi
+ ])
+ AM_CONDITIONAL(HAVE_GNORBA, test x$gnome_cv_gnorba_found = xyes)
+ if test x$gnome_cv_orbit_found = xyes; then
+ $1
+ GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+ GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+ AC_SUBST(GNORBA_CFLAGS)
+ AC_SUBST(GNORBA_LIBS)
+ else
+ if test x$2 = xfailure; then
+ AC_MSG_ERROR(gnorba library not installed or installation problem)
+ fi
+ fi
+])
+
+AC_DEFUN([GNOME_GNORBA_CHECK], [
+ GNOME_GNORBA_HOOK([],failure)
+])
diff --git a/neon/macros/gnome-orbit-check.m4 b/neon/macros/gnome-orbit-check.m4
new file mode 100644
index 000000000..54bf33aa4
--- /dev/null
+++ b/neon/macros/gnome-orbit-check.m4
@@ -0,0 +1,33 @@
+dnl
+dnl GNOME_ORBIT_HOOK (script-if-orbit-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if orbit is not found.
+dnl
+
+AC_DEFUN([GNOME_ORBIT_HOOK],[
+ AC_PATH_PROG(ORBIT_CONFIG,orbit-config,no)
+ AC_PATH_PROG(ORBIT_IDL,orbit-idl,no)
+ AC_CACHE_CHECK([for working ORBit environment],gnome_cv_orbit_found,[
+ if test x$ORBIT_CONFIG = xno -o x$ORBIT_IDL = xno; then
+ gnome_cv_orbit_found=no
+ else
+ gnome_cv_orbit_found=yes
+ fi
+ ])
+ AM_CONDITIONAL(HAVE_ORBIT, test x$gnome_cv_orbit_found = xyes)
+ if test x$gnome_cv_orbit_found = xyes; then
+ $1
+ ORBIT_CFLAGS=`orbit-config --cflags client server`
+ ORBIT_LIBS=`orbit-config --use-service=name --libs client server`
+ AC_SUBST(ORBIT_CFLAGS)
+ AC_SUBST(ORBIT_LIBS)
+ else
+ if test x$2 = xfailure; then
+ AC_MSG_ERROR(ORBit not installed or installation problem)
+ fi
+ fi
+])
+
+AC_DEFUN([GNOME_ORBIT_CHECK], [
+ GNOME_ORBIT_HOOK([],failure)
+])
diff --git a/neon/macros/gnome-print-check.m4 b/neon/macros/gnome-print-check.m4
new file mode 100644
index 000000000..7d98281d9
--- /dev/null
+++ b/neon/macros/gnome-print-check.m4
@@ -0,0 +1,171 @@
+# Configure paths for GNOME-PRINT
+# Chris Lahey 99-2-5
+# stolen from Manish Singh again
+# stolen back from Frank Belew
+# stolen from Manish Singh
+# Shamelessly stolen from Owen Taylor
+
+dnl AM_PATH_GNOME_PRINT([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for GNOME-PRINT, and define GNOME_PRINT_CFLAGS and GNOME_PRINT_LIBS
+dnl
+AC_DEFUN(AM_PATH_GNOME_PRINT,
+[dnl
+dnl Get the cflags and libraries from the gnome-config script
+dnl
+AC_ARG_WITH(gnome-print-prefix,[ --with-gnome-print-prefix=PFX Prefix where GNOME-PRINT is installed (optional)],
+ gnome_print_prefix="$withval", gnome_print_prefix="")
+AC_ARG_WITH(gnome-print-exec-prefix,[ --with-gnome-print-exec-prefix=PFX Exec prefix where GNOME-PRINT is installed (optional)],
+ gnome_print_exec_prefix="$withval", gnome_print_exec_prefix="")
+AC_ARG_ENABLE(gnome-printtest, [ --disable-gnome-printtest Do not try to compile and run a test GNOME-PRINT program],
+ , enable_gnome_printtest=yes)
+
+ if test x$gnome_print_exec_prefix != x ; then
+ gnome_print_args="$gnome_print_args --exec-prefix=$gnome_print_exec_prefix"
+ if test x${GNOME_CONFIG+set} != xset ; then
+ GNOME_CONFIG=$gnome_print_exec_prefix/bin/gnome-config
+ fi
+ fi
+ if test x$gnome_print_prefix != x ; then
+ gnome_print_args="$gnome_print_args --prefix=$gnome_print_prefix"
+ if test x${GNOME_CONFIG+set} != xset ; then
+ GNOME_CONFIG=$gnome_print_prefix/bin/gnome-config
+ fi
+ fi
+
+ AC_PATH_PROG(GNOME_CONFIG, gnome-config, no)
+ min_gnome_print_version=ifelse([$1], ,0.1.0,$1)
+ AC_MSG_CHECKING(for GNOME-PRINT - version >= $min_gnome_print_version)
+ no_gnome_print=""
+ if test "$GNOME_CONFIG" = "no" ; then
+ no_gnome_print=yes
+ else
+ GNOME_PRINT_CFLAGS=`$GNOME_CONFIG $gnome_printconf_args --cflags print`
+ GNOME_PRINT_LIBS=`$GNOME_CONFIG $gnome_printconf_args --libs print`
+
+ gnome_print_major_version=`$GNOME_CONFIG $gnome_print_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ gnome_print_minor_version=`$GNOME_CONFIG $gnome_print_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ gnome_print_micro_version=`$GNOME_CONFIG $gnome_print_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_gnome_printtest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $GNOME_PRINT_CFLAGS"
+ LIBS="$LIBS $GNOME_PRINT_LIBS"
+dnl
+dnl Now check if the installed GNOME-PRINT is sufficiently new. (Also sanity
+dnl checks the results of gnome-config to some extent
+dnl
+ rm -f conf.gnome_printtest
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgnomeprint/gnome-print.h>
+
+static char*
+my_strdup (char *str)
+{
+ char *new_str;
+
+ if (str)
+ {
+ new_str = malloc ((strlen (str) + 1) * sizeof(char));
+ strcpy (new_str, str);
+ }
+ else
+ new_str = NULL;
+
+ return new_str;
+}
+
+int main ()
+{
+ int major, minor, micro;
+ char *tmp_version;
+
+ system ("touch conf.gnome_printtest");
+
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = my_strdup("$min_gnome_print_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+ printf("%s, bad version string\n", "$min_gnome_print_version");
+ exit(1);
+ }
+ return 0;
+#if 0
+ if (($gnome_print_major_version > major) ||
+ (($gnome_print_major_version == major) && ($gnome_print_minor_version > minor)) ||
+ (($gnome_print_major_version == major) && ($gnome_print_minor_version == minor) && ($gnome_print_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** 'gnome-config print --version' returned %d.%d.%d, but the minimum version\n", $gnome_print_major_version, $gnome_print_minor_version, $gnome_print_micro_version);
+ printf("*** of GNOME-PRINT required is %d.%d.%d. If gnome-config is correct, then it is\n", major, minor, micro);
+ printf("*** best to upgrade to the required version.\n");
+ printf("*** If gnome-config was wrong, set the environment variable GNOME_CONFIG\n");
+ printf("*** to point to the correct copy of gnome-config, and remove the file\n");
+ printf("*** config.cache before re-running configure\n");
+ return 1;
+ }
+#endif
+}
+
+],, no_gnome_print=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_gnome_print" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$GNOME_CONFIG" = "no" ; then
+ echo "*** The gnome-config script installed by GNOME-LIBS could not be found"
+ echo "*** If GNOME-PRINT was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the GNOME_CONFIG environment variable to the"
+ echo "*** full path to gnome-config."
+ else
+ if test -f conf.gnome_printtest ; then
+ :
+ else
+ echo "*** Could not run GNOME-PRINT test program, checking why..."
+ CFLAGS="$CFLAGS $GNOME_PRINT_CFLAGS"
+ LIBS="$LIBS $GNOME_PRINT_LIBS"
+ AC_TRY_LINK([
+#include <stdio.h>
+#include <libgnomeprint/gnome-print.h>
+], [ return 0; ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding GNOME-PRINT or finding the wrong"
+ echo "*** version of GNOME-PRINT. If it is not finding GNOME-PRINT, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means GNOME-PRINT was incorrectly installed"
+ echo "*** or that you have moved GNOME-PRINT since it was installed. In the latter case, you"
+ echo "*** may want to edit the gnome-config script: $GNOME_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ GNOME_PRINT_CFLAGS=""
+ GNOME_PRINT_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(GNOME_PRINT_CFLAGS)
+ AC_SUBST(GNOME_PRINT_LIBS)
+ rm -f conf.gnome_printtest
+])
+
+AC_DEFUN([GNOME_PRINT_CHECK], [
+ AM_PATH_GNOME_PRINT(0.1.0,,[AC_MSG_ERROR(GNOME-PRINT not found)])
+])
diff --git a/neon/macros/gnome-pthread-check.m4 b/neon/macros/gnome-pthread-check.m4
new file mode 100644
index 000000000..a4eb3b489
--- /dev/null
+++ b/neon/macros/gnome-pthread-check.m4
@@ -0,0 +1,16 @@
+dnl
+dnl And better, use gthreads instead...
+dnl
+
+AC_DEFUN([GNOME_PTHREAD_CHECK],[
+ PTHREAD_LIB=""
+ AC_CHECK_LIB(pthread, pthread_create, PTHREAD_LIB="-lpthread",
+ [AC_CHECK_LIB(pthreads, pthread_create, PTHREAD_LIB="-lpthreads",
+ [AC_CHECK_LIB(c_r, pthread_create, PTHREAD_LIB="-lc_r",
+ [AC_CHECK_FUNC(pthread_create)]
+ )]
+ )]
+ )
+ AC_SUBST(PTHREAD_LIB)
+ AC_PROVIDE([GNOME_PTHREAD_CHECK])
+])
diff --git a/neon/macros/gnome-support.m4 b/neon/macros/gnome-support.m4
new file mode 100644
index 000000000..2c1d04984
--- /dev/null
+++ b/neon/macros/gnome-support.m4
@@ -0,0 +1,68 @@
+dnl GNOME_SUPPORT_CHECKS
+dnl Check for various support functions needed by the standard
+dnl Gnome libraries. Sets LIBOBJS, might define some macros.
+dnl This should only be used when building the Gnome libs;
+dnl Gnome clients should not need this macro.
+AC_DEFUN([GNOME_SUPPORT_CHECKS],[
+ # we need an `awk' to build `gnomesupport.h'
+ AC_REQUIRE([AC_PROG_AWK])
+
+ # this should go away soon
+ need_gnome_support=yes
+
+ save_LIBOBJS="$LIBOBJS"
+ LIBOBJS=
+
+ AC_CHECK_FUNCS(getopt_long,,LIBOBJS="$LIBOBJS getopt.o getopt1.o")
+
+ # for `scandir'
+ AC_HEADER_DIRENT
+
+ # copied from `configure.in' of `libiberty'
+ vars="program_invocation_short_name program_invocation_name sys_errlist"
+ for v in $vars; do
+ AC_MSG_CHECKING([for $v])
+ AC_CACHE_VAL(gnome_cv_var_$v,
+ [AC_TRY_LINK([int *p;], [extern int $v; p = &$v;],
+ [eval "gnome_cv_var_$v=yes"],
+ [eval "gnome_cv_var_$v=no"])])
+ if eval "test \"`echo '$gnome_cv_var_'$v`\" = yes"; then
+ AC_MSG_RESULT(yes)
+ n=HAVE_`echo $v | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ AC_DEFINE_UNQUOTED($n)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ done
+
+ AC_REPLACE_FUNCS(memmove mkstemp scandir strcasecmp strerror strndup strnlen)
+ AC_REPLACE_FUNCS(strtok_r strtod strtol strtoul vasprintf vsnprintf)
+
+ AC_CHECK_FUNCS(realpath,,LIBOBJS="$LIBOBJS canonicalize.o")
+
+ # to include `error.c' error.c has some HAVE_* checks
+ AC_CHECK_FUNCS(vprintf doprnt strerror_r)
+ AM_FUNC_ERROR_AT_LINE
+
+ # This is required if we declare setreuid () and setregid ().
+ AC_TYPE_UID_T
+
+ # see if we need to declare some functions. Solaris is notorious for
+ # putting functions into the `libc' but not listing them in the headers
+ AC_CHECK_HEADERS(string.h strings.h stdlib.h unistd.h dirent.h)
+ GCC_NEED_DECLARATIONS(gethostname setreuid setregid getpagesize)
+ GCC_NEED_DECLARATION(scandir,[
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+])
+
+ # Turn our LIBOBJS into libtool objects. This is gross, but it
+ # requires changes to autoconf before it goes away.
+ LTLIBOBJS=`echo "$LIBOBJS" | sed 's/\.o/.lo/g'`
+ AC_SUBST(need_gnome_support)
+ AC_SUBST(LTLIBOBJS)
+
+ LIBOBJS="$save_LIBOBJS"
+ AM_CONDITIONAL(BUILD_GNOME_SUPPORT, test "$need_gnome_support" = yes)
+])
diff --git a/neon/macros/gnome-x-checks.m4 b/neon/macros/gnome-x-checks.m4
new file mode 100644
index 000000000..e1125cc55
--- /dev/null
+++ b/neon/macros/gnome-x-checks.m4
@@ -0,0 +1,80 @@
+dnl GNOME_X_CHECKS
+dnl
+dnl Basic X11 related checks for X11. At the end, the following will be
+dnl defined/changed:
+dnl GTK_{CFLAGS,LIBS} From AM_PATH_GTK
+dnl CPPFLAGS Will include $X_CFLAGS
+dnl GNOME_HAVE_SM `true' or `false' depending on whether session
+dnl management is available. It is available if
+dnl both -lSM and X11/SM/SMlib.h exist. (Some
+dnl Solaris boxes have the library but not the header)
+dnl XPM_LIBS -lXpm if Xpm library is present, otherwise ""
+dnl
+dnl The following configure cache variables are defined (but not used):
+dnl gnome_cv_passdown_{x_libs,X_LIBS,X_CFLAGS}
+dnl
+AC_DEFUN([GNOME_X_CHECKS],
+[
+ AM_PATH_GTK(1.2.7,,AC_MSG_ERROR(GTK not installed, or gtk-config not in path),gthread)
+ dnl Hope that GTK_CFLAGS have only -I and -D. Otherwise, we could
+ dnl test -z "$x_includes" || CPPFLAGS="$CPPFLAGS -I$x_includes"
+ dnl
+ dnl Use CPPFLAGS instead of CFLAGS because AC_CHECK_HEADERS uses
+ dnl CPPFLAGS, not CFLAGS
+ CPPFLAGS="$CPPFLAGS $GTK_CFLAGS"
+
+ saved_ldflags="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $GTK_LIBS"
+
+ gnome_cv_passdown_x_libs="$GTK_LIBS"
+ gnome_cv_passdown_X_LIBS="$GTK_LIBS"
+ gnome_cv_passdown_X_CFLAGS="$GTK_CFLAGS"
+ gnome_cv_passdown_GTK_LIBS="$GTK_LIBS"
+
+ LDFLAGS="$saved_ldflags $GTK_LIBS"
+
+dnl We are requiring GTK >= 1.1.1, which means this will be fine anyhow.
+ USE_DEVGTK=true
+
+dnl AC_MSG_CHECKING([whether to use features from (unstable) GTK+ 1.1.x])
+dnl AC_EGREP_CPP(answer_affirmatively,
+dnl [#include <gtk/gtkfeatures.h>
+dnl #ifdef GTK_HAVE_FEATURES_1_1_0
+dnl answer_affirmatively
+dnl #endif
+dnl ], dev_gtk=yes, dev_gtk=no)
+dnl if test "$dev_gtk" = "yes"; then
+dnl USE_DEVGTK=true
+dnl fi
+dnl AC_MSG_RESULT("$dev_gtk")
+
+ GNOME_HAVE_SM=true
+ case "$GTK_LIBS" in
+ *-lSM*)
+ dnl Already found it.
+ ;;
+ *)
+ dnl Assume that if we have -lSM then we also have -lICE.
+ AC_CHECK_LIB(SM, SmcSaveYourselfDone,
+ [GTK_LIBS="-lSM -lICE $GTK_LIBS"],GNOME_HAVE_SM=false,
+ $x_libs -lICE)
+ ;;
+ esac
+
+ if test "$GNOME_HAVE_SM" = true; then
+ AC_CHECK_HEADERS(X11/SM/SMlib.h,,GNOME_HAVE_SM=false)
+ fi
+
+ if test "$GNOME_HAVE_SM" = true; then
+ AC_DEFINE(HAVE_LIBSM)
+ fi
+
+ XPM_LIBS=""
+ AC_CHECK_LIB(Xpm, XpmFreeXpmImage, [XPM_LIBS="-lXpm"], , $x_libs)
+ AC_SUBST(XPM_LIBS)
+
+ AC_REQUIRE([GNOME_PTHREAD_CHECK])
+ LDFLAGS="$saved_ldflags"
+
+ AC_PROVIDE([GNOME_X_CHECKS])
+])
diff --git a/neon/macros/gnome.m4 b/neon/macros/gnome.m4
new file mode 100644
index 000000000..a3a9ca740
--- /dev/null
+++ b/neon/macros/gnome.m4
@@ -0,0 +1,124 @@
+dnl
+dnl GNOME_INIT_HOOK (script-if-gnome-enabled, [failflag], [additional-inits])
+dnl
+dnl if failflag is "fail" then GNOME_INIT_HOOK will abort if gnomeConf.sh
+dnl is not found.
+dnl
+
+AC_DEFUN([GNOME_INIT_HOOK],[
+ AC_SUBST(GNOME_LIBS)
+ AC_SUBST(GNOMEUI_LIBS)
+ AC_SUBST(GNOMEGNORBA_LIBS)
+ AC_SUBST(GTKXMHTML_LIBS)
+ AC_SUBST(ZVT_LIBS)
+ AC_SUBST(GNOME_LIBDIR)
+ AC_SUBST(GNOME_INCLUDEDIR)
+
+ AC_ARG_WITH(gnome-includes,
+ [ --with-gnome-includes Specify location of GNOME headers],[
+ CFLAGS="$CFLAGS -I$withval"
+ ])
+
+ AC_ARG_WITH(gnome-libs,
+ [ --with-gnome-libs Specify location of GNOME libs],[
+ LDFLAGS="$LDFLAGS -L$withval"
+ gnome_prefix=$withval
+ ])
+
+ AC_ARG_WITH(gnome,
+ [ --with-gnome Specify prefix for GNOME files],
+ if test x$withval = xyes; then
+ want_gnome=yes
+ dnl Note that an empty true branch is not
+ dnl valid sh syntax.
+ ifelse([$1], [], :, [$1])
+ else
+ if test "x$withval" = xno; then
+ want_gnome=no
+ else
+ want_gnome=yes
+ LDFLAGS="$LDFLAGS -L$withval/lib"
+ CFLAGS="$CFLAGS -I$withval/include"
+ gnome_prefix=$withval/lib
+ fi
+ fi,
+ want_gnome=yes)
+
+ if test "x$want_gnome" = xyes; then
+
+ AC_PATH_PROG(GNOME_CONFIG,gnome-config,no)
+ if test "$GNOME_CONFIG" = "no"; then
+ no_gnome_config="yes"
+ else
+ AC_MSG_CHECKING(if $GNOME_CONFIG works)
+ if $GNOME_CONFIG --libs-only-l gnome >/dev/null 2>&1; then
+ AC_MSG_RESULT(yes)
+ GNOME_GNORBA_HOOK([],$2)
+ GNOME_LIBS="`$GNOME_CONFIG --libs-only-l gnome`"
+ GNOMEUI_LIBS="`$GNOME_CONFIG --libs-only-l gnomeui`"
+ GNOMEGNORBA_LIBS="`$GNOME_CONFIG --libs-only-l gnorba gnomeui`"
+ GTKXMHTML_LIBS="`$GNOME_CONFIG --libs-only-l gtkxmhtml`"
+ ZVT_LIBS="`$GNOME_CONFIG --libs-only-l zvt`"
+ GNOME_LIBDIR="`$GNOME_CONFIG --libs-only-L gnorba gnomeui`"
+ GNOME_INCLUDEDIR="`$GNOME_CONFIG --cflags gnorba gnomeui`"
+ $1
+ else
+ AC_MSG_RESULT(no)
+ no_gnome_config="yes"
+ fi
+ fi
+
+ if test x$exec_prefix = xNONE; then
+ if test x$prefix = xNONE; then
+ gnome_prefix=$ac_default_prefix/lib
+ else
+ gnome_prefix=$prefix/lib
+ fi
+ else
+ gnome_prefix=`eval echo \`echo $libdir\``
+ fi
+
+ if test "$no_gnome_config" = "yes"; then
+ AC_MSG_CHECKING(for gnomeConf.sh file in $gnome_prefix)
+ if test -f $gnome_prefix/gnomeConf.sh; then
+ AC_MSG_RESULT(found)
+ echo "loading gnome configuration from" \
+ "$gnome_prefix/gnomeConf.sh"
+ . $gnome_prefix/gnomeConf.sh
+ $1
+ else
+ AC_MSG_RESULT(not found)
+ if test x$2 = xfail; then
+ AC_MSG_ERROR(Could not find the gnomeConf.sh file that is generated by gnome-libs install)
+ fi
+ fi
+ fi
+ fi
+
+ if test -n "$3"; then
+ n="$3"
+ for i in $n; do
+ AC_MSG_CHECKING(extra library \"$i\")
+ case $i in
+ applets)
+ AC_SUBST(GNOME_APPLETS_LIBS)
+ GNOME_APPLETS_LIBS=`$GNOME_CONFIG --libs-only-l applets`
+ AC_MSG_RESULT($GNOME_APPLETS_LIBS);;
+ capplet)
+ AC_SUBST(GNOME_CAPPLET_LIBS)
+ GNOME_CAPPLET_LIBS=`$GNOME_CONFIG --libs-only-l capplet`
+ AC_MSG_RESULT($GNOME_CAPPLET_LIBS);;
+ *)
+ AC_MSG_RESULT(unknown library)
+ esac
+ done
+ fi
+])
+
+dnl
+dnl GNOME_INIT ([additional-inits])
+dnl
+
+AC_DEFUN([GNOME_INIT],[
+ GNOME_INIT_HOOK([],fail,$1)
+])
diff --git a/neon/macros/neon-checks.m4 b/neon/macros/neon-checks.m4
new file mode 100644
index 000000000..da5fffe46
--- /dev/null
+++ b/neon/macros/neon-checks.m4
@@ -0,0 +1,21 @@
+
+dnl Call these checks when compiling the libneon source package.
+
+AC_DEFUN([LIBNEON_SOURCE_CHECKS],[
+
+AC_CHECK_HEADERS(stdarg.h string.h strings.h sys/time.h regex.h \
+ stdlib.h unistd.h limits.h sys/select.h)
+
+AC_REPLACE_FUNCS(strcasecmp)
+
+dnl Check for snprintf
+AC_CHECK_FUNC(snprintf,
+ AC_DEFINE(HAVE_SNPRINTF, 1, [Define if you have snprintf]),
+ LIBOBJS="$LIBOBJS lib/snprintf.o" )
+
+AC_CHECK_FUNC(gethostbyname,
+ AC_MSG_RESULT(using libc's gethostbyname),
+ AC_CHECK_LIB(nsl,gethostbyname)
+ )
+
+])
diff --git a/neon/macros/neon-debug.m4 b/neon/macros/neon-debug.m4
new file mode 100644
index 000000000..b4d5b870e
--- /dev/null
+++ b/neon/macros/neon-debug.m4
@@ -0,0 +1,41 @@
+# Copyright (C) 1998-2001 Joe Orton <joe@manyfish.co.uk>
+#
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the neon library code
+# itself may be copied and distributed under the terms of the GNU
+# LGPL, see COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version.
+# Please send any feedback to <neon@webdav.org>
+
+# Adds an --disable-debug argument to configure to allow disabling
+# debugging messages.
+#
+# Usage:
+# NEON_WARNINGS([actions-if-debug-enabled], [actions-if-debug-disable])
+#
+
+AC_DEFUN([NEON_DEBUG], [
+
+AC_ARG_ENABLE(debug,
+ [ --disable-debug disable runtime debugging messages ],
+ [with_debug=$enableval],
+ [with_debug=yes]) dnl Defaults to ENABLED
+
+if test "$with_debug" = "yes"; then
+ AC_DEFINE(NE_DEBUGGING, 1, [Define to enable debugging])
+ :
+ $1
+else
+ :
+ $2
+fi
+
+])
diff --git a/neon/macros/neon-socks.m4 b/neon/macros/neon-socks.m4
new file mode 100644
index 000000000..499e3646d
--- /dev/null
+++ b/neon/macros/neon-socks.m4
@@ -0,0 +1,45 @@
+# Copyright (C) 1998-2001 Joe Orton <joe@manyfish.co.uk>
+#
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the neon library code
+# itself may be copied and distributed under the terms of the GNU
+# LGPL, see COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version.
+# Please send any feedback to <neon@webdav.org>
+
+# Following instructions at:
+# http://www.socks.nec.com/how2socksify.html
+
+AC_DEFUN([NEON_SOCKS], [
+
+AC_ARG_WITH([--with-socks],
+[ --with-socks[=DIR] Use SOCKS library ],
+[
+if test "$withval" != "no"; then
+
+ if test "$withval" != "yes"; then
+ LDFLAGS="$LDFLAGS -L$withval/lib"
+ CFLAGS="$CFLAGS -I$withval/include"
+ fi
+
+ AC_CHECK_HEADERS(sock.h)
+
+ CFLAGS="$CFLAGS -DSOCKS"
+
+ AC_CHECK_LIB(socks5, connect, [NEONLIBS="$NEONLIBS -lsocks5"],
+ AC_MSG_ERROR([could not find libsocks5 for SOCKS support]))
+
+fi
+
+])
+
+])
+
diff --git a/neon/macros/neon-ssl.m4 b/neon/macros/neon-ssl.m4
new file mode 100644
index 000000000..6603a83e7
--- /dev/null
+++ b/neon/macros/neon-ssl.m4
@@ -0,0 +1,68 @@
+# SSL macro from fetchmail
+# (C) Eric S. Raymond <esr@thyrsus.com>
+
+AC_DEFUN([NEON_SSL],[
+
+### use option --with-ssl to compile in the SSL support
+AC_ARG_WITH(ssl,
+ [ --with-ssl=[DIR] enable SSL support using libraries in DIR],
+ [with_ssl=$withval],
+ [with_ssl=no])
+test "$with_ssl" = "yes" && AC_DEFINE(SSL_ENABLE)
+
+if test "$with_ssl" = "yes"
+then
+# He didn't specify an SSL location. Let's look at some common
+# directories where SSL has been found in the past and try and auto
+# configure for SSL. OpenSSL determination will be made later.
+# This will screw up if an OpenSSL install is located in a later
+# directory than an older SSLeay install, but the user should fix that
+# anyways and he can override on the configure line.
+
+ for ac_dir in \
+ /usr/local/ssl \
+ /usr/ssl \
+ /local/ssl \
+ /opt/ssl \
+ ; \
+ do
+ if test -d "$ac_dir" ; then
+ with_ssl=$ac_dir
+ break;
+ fi
+ done
+fi
+
+if test -n "$with_ssl" -a "$with_ssl" != "no"
+then
+ # With the autoconfigure above, the only time this is going to be
+ # true is going to be when we could not find the headers. If they
+ # are not in system standard locations, we are going to be broken.
+ if test "$with_ssl" = "yes"
+ then
+# Let's just define the standard location for the SSLeay root
+ with_ssl="/usr/local/ssl"
+ fi
+ if test -r $with_ssl/include/openssl/ssl.h
+ then
+### ssl.h found under openssl. Use openssl configuration preferentially
+ echo "Enabling OpenSSL support in $with_ssl"
+ CFLAGS="$CFLAGS -I$with_ssl/include -I$with_ssl/include/openssl"
+### OpenBSD comes with ssl headers
+ elif test -r /usr/include/ssl/ssl.h
+ then
+ echo "Enabling SSLeay support in $with_ssl"
+ CFLAGS="$CFLAGS -I/usr/include/ssl"
+ else
+ echo "Enabling SSLeay support in $with_ssl"
+ CFLAGS="$CFLAGS -I$with_ssl/include"
+ fi
+ LDFLAGS="$LDFLAGS -L$with_ssl/lib"
+ LIBS="$LIBS -lssl -lcrypto"
+ AC_DEFINE(SSL_ENABLE)
+else
+ echo 'Disabling SSL support...'
+fi
+
+])
+
diff --git a/neon/macros/neon-test.m4 b/neon/macros/neon-test.m4
new file mode 100644
index 000000000..e748ee0c6
--- /dev/null
+++ b/neon/macros/neon-test.m4
@@ -0,0 +1,24 @@
+# Copyright (C) 2001 Joe Orton <joe@manyfish.co.uk>
+#
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the neon library code
+# itself may be copied and distributed under the terms of the GNU
+# LGPL, see COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version.
+# Please send any feedback to <neon@webdav.org>
+
+# Tests needed for the neon-test common test code.
+
+AC_DEFUN([NEON_TEST], [
+
+AC_CHECK_FUNCS(pipe)
+
+])
diff --git a/neon/macros/neon-warnings.m4 b/neon/macros/neon-warnings.m4
new file mode 100644
index 000000000..3c6a6454b
--- /dev/null
+++ b/neon/macros/neon-warnings.m4
@@ -0,0 +1,38 @@
+# Copyright (C) 1998-2000 Joe Orton.
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the library code itself
+# may be copied and distributed under the terms of the GNU LGPL, see
+# COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version.
+# Please send any feedback to <neon@webdav.org>
+# Id: neon-warnings.m4,v 1.4 2000/07/27 15:23:24 joe Exp
+
+# Usage:
+# NEON_WARINGS(default)
+
+AC_DEFUN([NEON_WARNINGS],[
+
+AC_ARG_ENABLE(warnings,
+ [ --enable-warnings enable GCC warnings during build ],
+ [with_warnings=$enableval],
+ [with_warnings=no]) dnl Defaults to DISABLED
+
+if test "$with_warnings" = "yes"; then
+ CFLAGS="$CFLAGS -Wall -ansi-pedantic -Wmissing-declarations -Winline"
+ if test -z "$with_ssl" -o "$with_ssl" = "no"; then
+ # joe: My OpenSSL ssl.h fails strict prototypes checks
+ # left right and center
+ CFLAGS="$CFLAGS -Wstrict-prototypes"
+ fi
+fi
+
+])
+
diff --git a/neon/macros/neon-xml-parser.m4 b/neon/macros/neon-xml-parser.m4
new file mode 100644
index 000000000..aca9d07e2
--- /dev/null
+++ b/neon/macros/neon-xml-parser.m4
@@ -0,0 +1,84 @@
+dnl Check for XML parser.
+dnl Supports:
+dnl * libxml (requires version 1.8.3 or later)
+dnl * expat in -lexpat
+dnl * expat in -lxmlparse and -lxmltok (as packaged by Debian)
+dnl * Bundled expat if bundled is passed as arg
+
+dnl Usage: NEON_XML_PARSER([bundled])
+
+AC_DEFUN([NEON_XML_PARSER],
+[
+
+AC_ARG_ENABLE( libxml,
+ [ --enable-libxml force use of libxml ],
+ [neon_force_libxml=$enableval],
+ [neon_force_libxml=no])
+
+if test "$neon_force_libxml" = "no"; then
+dnl Look for expat
+AC_CHECK_LIB(expat, XML_Parse,
+ neon_expat_libs="-lexpat" neon_found_parser="expat",
+ AC_CHECK_LIB(xmlparse, XML_Parse,
+ neon_expat_libs="-lxmltok -lxmlparse" neon_found_parser="expat",
+ neon_found_parser="no",
+ -lxmltok )
+ )
+else
+ neon_found_parser="no"
+fi
+
+if test "$neon_found_parser" = "no"; then
+ # Have we got libxml 1.8.3 or later?
+ AC_CHECK_PROG(XML_CONFIG, xml-config, xml-config)
+ if test "$XML_CONFIG" != ""; then
+ # Check for recent library
+ oLIBS="$LIBS"
+ oCFLAGS="$CFLAGS"
+ LIBS="$LIBS `$XML_CONFIG --libs`"
+ CFLAGS="$CFLAGS `$XML_CONFIG --cflags`"
+ AC_CHECK_LIB(xml, xmlCreatePushParserCtxt,
+ neon_found_parser="libxml" neon_xml_parser_message="libxml"
+ AC_DEFINE(HAVE_LIBXML, 1, [Define if you have libxml]),
+ CFLAGS="$oCFLAGS"
+ LIBS="$oLIBS"
+ AC_WARN([cannot use old libxml (1.8.3 or newer required)])
+ )
+ fi
+fi
+
+if test "$neon_found_parser" = "expat"; then
+ # This is crap. Maybe just use AC_CHECK_HEADERS and use the
+ # right file by ifdef'ing is best
+ AC_CHECK_HEADER(xmlparse.h,
+ neon_expat_incs="" neon_found_expatincs="yes",
+ AC_CHECK_HEADER(xmltok/xmlparse.h,
+ neon_expat_incs="-I/usr/include/xmltok" neon_found_expatincs="yes",
+ ))
+ if test "$neon_found_expatincs" = "yes"; then
+ AC_DEFINE(HAVE_EXPAT, 1, [Define if you have expat])
+ if test "$neon_expat_incs"; then
+ CFLAGS="$CFLAGS $neon_expat_incs"
+ fi
+ LIBS="$LIBS $neon_expat_libs"
+ else
+ AC_MSG_ERROR(["found expat library but could not find xmlparse.h"])
+ fi
+ neon_xml_parser_message="expat in $neon_expat_libs"
+fi
+
+if test "$neon_found_parser" = "no" ; then
+ if test "$1" = "bundled"; then
+ # Use the bundled expat sources
+ LIBOBJS="$LIBOBJS expat/xmltok/xmltok.o expat/xmltok/xmlrole.o expat/xmlparse/xmlparse.o expat/xmlparse/hashtable.o"
+ CFLAGS="$CFLAGS -DXML_DTD -Iexpat/xmlparse -Iexpat/xmltok"
+ AC_MSG_RESULT(using supplied expat XML parser)
+ AC_DEFINE(HAVE_EXPAT, 1, [Define if you have expat] )
+ neon_xml_parser_message="supplied expat"
+ else
+ AC_MSG_ERROR([no XML parser was found])
+ fi
+
+fi
+
+]) \ No newline at end of file
diff --git a/neon/macros/neon.m4 b/neon/macros/neon.m4
new file mode 100644
index 000000000..5b4d83920
--- /dev/null
+++ b/neon/macros/neon.m4
@@ -0,0 +1,75 @@
+
+AC_DEFUN([NEON_LIBRARY],[
+
+AC_ARG_WITH( neon,
+ [ --with-neon Specify location of neon library ],
+ [neon_loc="$withval"])
+
+if test "$1" = "bundle"; then
+ AC_ARG_WITH( included-neon,
+ [ --with-included-neon Force use of included neon library ],
+ [neon_included="$withval"],
+ [neon_included="no"])
+fi
+
+AC_MSG_CHECKING(for neon location)
+
+if test "$neon_included" != "yes"; then
+ # Look for neon
+
+ if test -z "$neon_loc"; then
+ # Look in standard places
+ for d in /usr /usr/local; do
+ if test -x $d/bin/neon-config; then
+ neon_loc=$d
+ fi
+ done
+ fi
+
+ if test -x $neon_loc/bin/neon-config; then
+ # Couldn't find it!
+ AC_MSG_RESULT(found in $neon_loc)
+ NEON_CONFIG=$neon_loc/bin/neon-config
+ CFLAGS="$CFLAGS `$NEON_CONFIG --cflags`"
+ LIBS="$LIBS `$NEON_CONFIG --libs`"
+ else
+ if test "$1" = "bundle"; then
+ neon_included="yes"
+ else
+ AC_MSG_ERROR(could not find neon)
+ fi
+ fi
+fi
+
+if test "$neon_included" = "yes"; then
+ AC_MSG_RESULT(using supplied)
+ NEON_XML_PARSER
+ CFLAGS="$CFLAGS -Ilibneon"
+ LIBNEON_SOURCE_CHECKS
+ LIBOBJS="$LIBOBJS \$(NEONOBJS)"
+fi
+
+])
+
+dnl Call these checks when compiling the libneon source package.
+
+AC_DEFUN([LIBNEON_SOURCE_CHECKS],[
+
+AC_CHECK_HEADERS(stdarg.h string.h strings.h sys/time.h regex.h \
+ stdlib.h unistd.h limits.h sys/select.h)
+
+AC_REPLACE_FUNCS(strcasecmp)
+
+dnl Check for snprintf
+AC_CHECK_FUNC(snprintf,
+ AC_DEFINE(HAVE_SNPRINTF, 1, [Define if you have snprintf]),
+ LIBOBJS="$LIBOBJS lib/snprintf.o" )
+
+AC_CHECK_FUNC(gethostbyname,
+ AC_MSG_RESULT(using libc's gethostbyname),
+ AC_CHECK_LIB(nsl,gethostbyname)
+ )
+
+NEON_SSL
+
+])
diff --git a/neon/macros/readline.m4 b/neon/macros/readline.m4
new file mode 100644
index 000000000..2769ffd9c
--- /dev/null
+++ b/neon/macros/readline.m4
@@ -0,0 +1,47 @@
+
+dnl CHECK_READLINE checks for presence of readline on the
+dnl system.
+
+AC_DEFUN([CHECK_READLINE], [
+
+AC_ARG_ENABLE(readline,
+ [ --disable-readline disable readline support ],
+ [use_readline=$enableval],
+ [use_readline=yes]) dnl Defaults to ON (if found)
+
+if test "$use_readline" = "yes"; then
+ AC_CHECK_LIB(curses, tputs, LIBS="$LIBS -lcurses",
+ AC_CHECK_LIB(ncurses, tputs))
+ AC_CHECK_LIB(readline, readline)
+
+ AC_SEARCH_LIBS(add_history, history,
+ AC_DEFINE(HAVE_ADD_HISTORY, 1, [Define if you have the add_history function])
+ )
+
+ AC_CHECK_HEADERS(history.h readline/history.h readline.h readline/readline.h)
+
+ AC_MSG_CHECKING(whether filename_completion_function is present)
+
+ AC_TRY_LINK([
+/* need stdio.h since readline.h uses FILE * */
+#include <stdio.h>
+
+#if defined(HAVE_READLINE_H)
+#include <readline.h>
+#elif defined(HAVE_READLINE_READLINE_H)
+#include <readline/readline.h>
+#endif
+],
+[void (*foo)() = filename_completion_function;],
+ AC_DEFINE(HAVE_FILENAME_COMPLETION_FUNCTION, 1,
+ [Define if you have filename_completion_function in readline])
+ AC_MSG_RESULT(yes),
+ AC_MSG_RESULT(no))
+
+ msg_readline="enabled"
+else
+ msg_readline="disabled"
+fi
+
+])
+
diff --git a/neon/macros/socklen-arg-type.m4 b/neon/macros/socklen-arg-type.m4
new file mode 100644
index 000000000..2d84ac445
--- /dev/null
+++ b/neon/macros/socklen-arg-type.m4
@@ -0,0 +1,43 @@
+dnl This function is (C) 1997,98,99 Stephan Kulow (coolo@kde.org)
+dnl Modifications (C) Joe Orton 1999,2000
+
+AC_DEFUN([SOCKLEN_ARG_TYPE],[
+
+dnl Check for the type of the third argument of getsockname
+AC_MSG_CHECKING(for the third argument of getsockname)
+AC_CACHE_VAL(ac_cv_ksize_t,
+[AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+],[
+socklen_t a=0;
+getsockname(0,(struct sockaddr*)0, &a);
+],
+ac_cv_ksize_t=socklen_t,
+ac_cv_ksize_t=)
+if test -z "$ac_cv_ksize_t"; then
+ac_safe_cflags="$CFLAGS"
+if test "$GCC" = "yes"; then
+ CFLAGS="-Werror $CFLAGS"
+fi
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+],[
+int a=0;
+getsockname(0,(struct sockaddr*)0, &a);
+],
+ac_cv_ksize_t=int,
+ac_cv_ksize_t=size_t)
+CFLAGS="$ac_safe_cflags"
+fi
+])
+
+if test -z "$ac_cv_ksize_t"; then
+ ac_cv_ksize_t=int
+fi
+
+AC_MSG_RESULT($ac_cv_ksize_t)
+AC_DEFINE_UNQUOTED(ksize_t, $ac_cv_ksize_t, [Define to be the type of the third argument to getsockname])
+
+]) \ No newline at end of file
diff --git a/neon/macros/strftime.m4 b/neon/macros/strftime.m4
new file mode 100644
index 000000000..1f95d7e77
--- /dev/null
+++ b/neon/macros/strftime.m4
@@ -0,0 +1,146 @@
+#serial 6
+
+dnl This macro is intended to be used solely in this file.
+dnl These are the prerequisite macros for GNU's strftime.c replacement.
+dnl FIXME: the list is far from complete
+AC_DEFUN(_jm_STRFTIME_PREREQS,
+[
+ dnl strftime.c uses localtime_r if it exists. Check for it.
+ AC_CHECK_FUNCS(localtime_r)
+ dnl FIXME: add tests for everything in strftime.c: e.g., HAVE_BCOPY,
+ dnl HAVE_TZNAME, HAVE_TZSET, HAVE_TM_ZONE, etc.
+])
+
+dnl Determine if the strftime function has all the features of the GNU one.
+dnl
+dnl From Jim Meyering.
+dnl
+AC_DEFUN(jm_FUNC_GNU_STRFTIME,
+[AC_REQUIRE([AC_HEADER_TIME])dnl
+
+ _jm_STRFTIME_PREREQS
+
+ AC_REQUIRE([AC_C_CONST])dnl
+ AC_REQUIRE([AC_HEADER_STDC])dnl
+ AC_CHECK_HEADERS(sys/time.h)
+ AC_CACHE_CHECK([for working GNU strftime], jm_cv_func_working_gnu_strftime,
+ [AC_TRY_RUN(
+changequote(<<, >>)dnl
+<< /* Ulrich Drepper provided parts of the test program. */
+#if STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+static int
+compare (const char *fmt, const struct tm *tm, const char *expected)
+{
+ char buf[99];
+ strftime (buf, 99, fmt, tm);
+ if (strcmp (buf, expected))
+ {
+#ifdef SHOW_FAILURES
+ printf ("fmt: \"%s\", expected \"%s\", got \"%s\"\n",
+ fmt, expected, buf);
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+int
+main ()
+{
+ int n_fail = 0;
+ struct tm *tm;
+ time_t t = 738367; /* Fri Jan 9 13:06:07 1970 */
+ tm = gmtime (&t);
+
+ /* This is necessary to make strftime give consistent zone strings and
+ e.g., seconds since the epoch (%s). */
+ putenv ("TZ=GMT0");
+
+#undef CMP
+#define CMP(Fmt, Expected) n_fail += compare ((Fmt), tm, (Expected))
+
+ CMP ("%-m", "1"); /* GNU */
+ CMP ("%A", "Friday");
+ CMP ("%^A", "FRIDAY"); /* The ^ is a GNU extension. */
+ CMP ("%B", "January");
+ CMP ("%^B", "JANUARY");
+ CMP ("%C", "19"); /* POSIX.2 */
+ CMP ("%D", "01/09/70"); /* POSIX.2 */
+ CMP ("%F", "1970-01-09");
+ CMP ("%G", "1970"); /* GNU */
+ CMP ("%H", "13");
+ CMP ("%I", "01");
+ CMP ("%M", "06");
+ CMP ("%M", "06");
+ CMP ("%R", "13:06"); /* POSIX.2 */
+ CMP ("%S", "07");
+ CMP ("%T", "13:06:07"); /* POSIX.2 */
+ CMP ("%U", "01");
+ CMP ("%V", "02");
+ CMP ("%W", "01");
+ CMP ("%X", "13:06:07");
+ CMP ("%Y", "1970");
+ CMP ("%Z", "GMT");
+ CMP ("%_m", " 1"); /* GNU */
+ CMP ("%a", "Fri");
+ CMP ("%^a", "FRI");
+ CMP ("%b", "Jan");
+ CMP ("%^b", "JAN");
+ CMP ("%c", "Fri Jan 9 13:06:07 1970");
+ CMP ("%^c", "FRI JAN 9 13:06:07 1970");
+ CMP ("%d", "09");
+ CMP ("%e", " 9"); /* POSIX.2 */
+ CMP ("%g", "70"); /* GNU */
+ CMP ("%h", "Jan"); /* POSIX.2 */
+ CMP ("%^h", "JAN");
+ CMP ("%j", "009");
+ CMP ("%k", "13"); /* GNU */
+ CMP ("%l", " 1"); /* GNU */
+ CMP ("%m", "01");
+ CMP ("%n", "\n"); /* POSIX.2 */
+ CMP ("%p", "PM");
+ CMP ("%r", "01:06:07 PM"); /* POSIX.2 */
+ CMP ("%s", "738367"); /* GNU */
+ CMP ("%t", "\t"); /* POSIX.2 */
+ CMP ("%u", "5"); /* POSIX.2 */
+ CMP ("%w", "5");
+ CMP ("%x", "01/09/70");
+ CMP ("%y", "70");
+ CMP ("%z", "+0000"); /* GNU */
+
+ exit (n_fail ? 1 : 0);
+}
+ >>,
+changequote([, ])dnl
+ jm_cv_func_working_gnu_strftime=yes,
+ jm_cv_func_working_gnu_strftime=no,
+ dnl When crosscompiling, assume strftime is missing or broken.
+ jm_cv_func_working_gnu_strftime=no)
+ ])
+ if test $jm_cv_func_working_gnu_strftime = no; then
+ AC_SUBST(LIBOBJS)
+ LIBOBJS="$LIBOBJS strftime.$ac_objext"
+ AC_DEFINE_UNQUOTED(strftime, gnu_strftime,
+ [Define to gnu_strftime if the replacement function should be used.])
+ fi
+])
+
+AC_DEFUN(jm_FUNC_STRFTIME,
+[
+ _jm_STRFTIME_PREREQS
+ AC_REPLACE_FUNCS(strftime)
+])
diff --git a/neon/neon-config.in b/neon/neon-config.in
new file mode 100644
index 000000000..d332354f5
--- /dev/null
+++ b/neon/neon-config.in
@@ -0,0 +1,73 @@
+#! /bin/sh
+# Originally from libxml, Copyright (C) Daniel Veillard
+# Modifications for neon Copyright (C) 2000 Joe Orton.
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+
+usage()
+{
+ cat <<EOF
+Usage: neon-config [OPTION]
+
+Known values for OPTION are:
+
+ --prefix=DIR change neon prefix [default $prefix]
+ --libs print library linking information
+ --cflags print pre-processor and compiler flags
+ --help display this help and exit
+ --version output version information
+EOF
+
+ exit $1
+}
+
+if test $# -eq 0; then
+ usage 1
+fi
+
+cflags=false
+libs=false
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+
+ case "$1" in
+ --prefix=*)
+ prefix=$optarg
+ ;;
+
+ --prefix)
+ echo $prefix
+ ;;
+
+ --version)
+ echo neon @NEON_VERSION@
+ exit 0
+ ;;
+
+ --help)
+ usage 0
+ ;;
+
+ --cflags)
+ echo @NEON_INCLUDEDIR@ @NEON_CFLAGS@
+ ;;
+
+ --libs)
+ echo -L@libdir@ @NEON_LIBS@
+ ;;
+
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+exit 0
diff --git a/neon/neon.dsp b/neon/neon.dsp
new file mode 100644
index 000000000..41c512c7e
--- /dev/null
+++ b/neon/neon.dsp
@@ -0,0 +1,215 @@
+# Microsoft Developer Studio Project File - Name="neon" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=neon - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "neon.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "neon.mak" CFG="neon - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "neon - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "neon - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "neon - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D
+"_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "$(NEON_EXPAT_LITE_WIN32)" /D "NDEBUG"
+/D "WIN32" /D "_MBCS" /D "_LIB" /D "HAVE_CONFIG_H" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"Release\neons.lib"
+
+!ELSEIF "$(CFG)" == "neon - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS"
+/D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "$(NEON_EXPAT_LITE_WIN32)" /D
+"_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "HAVE_CONFIG_H" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"Debug\neonsd.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "neon - Win32 Release"
+# Name "neon - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\src\base64.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dates.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_207.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_basic.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_locks.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_props.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\hip_xml.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_auth.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_basic.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_cookies.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_redirect.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_request.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_utils.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\ne_alloc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\neon_i18n.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslcerts.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\string_utils.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\uri.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Generated Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF "$(CFG)" == "neon - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ copy .\config.hw .\config.h > nul
+ echo Created config.h from config.hw
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "neon - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\src\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ copy .\config.hw .\src\config.h > nul
+ echo Created config.h from config.hw
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# End Group
+# End Target
+# End Project
+
diff --git a/neon/neon.dsw b/neon/neon.dsw
new file mode 100644
index 000000000..a2305f561
--- /dev/null
+++ b/neon/neon.dsw
@@ -0,0 +1,30 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "neon"=".\neon.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
+
diff --git a/neon/neon.mak b/neon/neon.mak
new file mode 100644
index 000000000..76b3c4eaf
--- /dev/null
+++ b/neon/neon.mak
@@ -0,0 +1,404 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on neon.dsp
+!IF "$(CFG)" == ""
+CFG=neon - Win32 Release
+!MESSAGE No configuration specified. Defaulting to neon - Win32 Release
+!ENDIF
+
+!IF "$(CFG)" != "neon - Win32 Release" && "$(CFG)" != "neon - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "neon.mak" CFG="neon - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "neon - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "neon - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(EXPAT_SRC)" == ""
+!MESSAGE
+!MESSAGE Need expat-lite source directory
+!MESSAGE
+!MESSAGE Call with
+!MESSAGE nmake /f neon.mak EXPAT_SRC=D:\apache_1.3.20\src\lib\expat-lite
+!MESSAGE
+!ERROR
+!ENDIF
+
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "neon - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+ALL : ".\config.h" "$(OUTDIR)\neons.lib"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\base64.obj"
+ -@erase "$(INTDIR)\ne_207.obj"
+ -@erase "$(INTDIR)\ne_alloc.obj"
+ -@erase "$(INTDIR)\ne_auth.obj"
+ -@erase "$(INTDIR)\ne_basic.obj"
+ -@erase "$(INTDIR)\ne_cookies.obj"
+ -@erase "$(INTDIR)\ne_dates.obj"
+ -@erase "$(INTDIR)\ne_i18n.obj"
+ -@erase "$(INTDIR)\ne_locks.obj"
+ -@erase "$(INTDIR)\ne_md5.obj"
+ -@erase "$(INTDIR)\ne_props.obj"
+ -@erase "$(INTDIR)\ne_redirect.obj"
+ -@erase "$(INTDIR)\ne_request.obj"
+ -@erase "$(INTDIR)\ne_session.obj"
+ -@erase "$(INTDIR)\ne_socket.obj"
+ -@erase "$(INTDIR)\ne_string.obj"
+ -@erase "$(INTDIR)\ne_uri.obj"
+ -@erase "$(INTDIR)\ne_utils.obj"
+ -@erase "$(INTDIR)\ne_xml.obj"
+ -@erase "$(OUTDIR)\neons.lib"
+ -@erase ".\config.h"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/c /nologo /MD /W3 /GX /O2 /I "$(EXPAT_SRC)" /D "NDEBUG" /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\"
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+RSC=rc.exe
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\neon.bsc"
+BSC32_SBRS= \
+
+LIB32=link.exe -lib
+LIB32_FLAGS=/nologo /out:"$(OUTDIR)\neons.lib"
+LIB32_OBJS= \
+ "$(INTDIR)\base64.obj" \
+ "$(INTDIR)\ne_xml.obj" \
+ "$(INTDIR)\ne_alloc.obj" \
+ "$(INTDIR)\ne_auth.obj" \
+ "$(INTDIR)\ne_basic.obj" \
+ "$(INTDIR)\ne_cookies.obj" \
+ "$(INTDIR)\ne_dates.obj" \
+ "$(INTDIR)\ne_i18n.obj" \
+ "$(INTDIR)\ne_locks.obj" \
+ "$(INTDIR)\ne_md5.obj" \
+ "$(INTDIR)\ne_props.obj" \
+ "$(INTDIR)\ne_redirect.obj" \
+ "$(INTDIR)\ne_request.obj" \
+ "$(INTDIR)\ne_session.obj" \
+ "$(INTDIR)\ne_socket.obj" \
+ "$(INTDIR)\ne_string.obj" \
+ "$(INTDIR)\ne_uri.obj" \
+ "$(INTDIR)\ne_utils.obj" \
+ "$(INTDIR)\ne_207.obj"
+
+"$(OUTDIR)\neons.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS)
+ $(LIB32) @<<
+ $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "neon - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+ALL : "$(OUTDIR)\neonsd.lib"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\base64.obj"
+ -@erase "$(INTDIR)\ne_207.obj"
+ -@erase "$(INTDIR)\ne_alloc.obj"
+ -@erase "$(INTDIR)\ne_auth.obj"
+ -@erase "$(INTDIR)\ne_basic.obj"
+ -@erase "$(INTDIR)\ne_cookies.obj"
+ -@erase "$(INTDIR)\ne_dates.obj"
+ -@erase "$(INTDIR)\ne_i18n.obj"
+ -@erase "$(INTDIR)\ne_locks.obj"
+ -@erase "$(INTDIR)\ne_md5.obj"
+ -@erase "$(INTDIR)\ne_props.obj"
+ -@erase "$(INTDIR)\ne_redirect.obj"
+ -@erase "$(INTDIR)\ne_request.obj"
+ -@erase "$(INTDIR)\ne_session.obj"
+ -@erase "$(INTDIR)\ne_socket.obj"
+ -@erase "$(INTDIR)\ne_string.obj"
+ -@erase "$(INTDIR)\ne_uri.obj"
+ -@erase "$(INTDIR)\ne_utils.obj"
+ -@erase "$(INTDIR)\ne_xml.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\vc60.pdb"
+ -@erase "$(OUTDIR)\neonsd.lib"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/c /nologo /MDd /W3 /Gm /GX /Zi /Od /I "$(EXPAT_SRC)" /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\"
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+RSC=rc.exe
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\neon.bsc"
+BSC32_SBRS= \
+
+LIB32=link.exe -lib
+LIB32_FLAGS=/nologo /out:"$(OUTDIR)\neonsd.lib"
+LIB32_OBJS= \
+ "$(INTDIR)\base64.obj" \
+ "$(INTDIR)\ne_xml.obj" \
+ "$(INTDIR)\ne_alloc.obj" \
+ "$(INTDIR)\ne_auth.obj" \
+ "$(INTDIR)\ne_basic.obj" \
+ "$(INTDIR)\ne_cookies.obj" \
+ "$(INTDIR)\ne_dates.obj" \
+ "$(INTDIR)\ne_i18n.obj" \
+ "$(INTDIR)\ne_locks.obj" \
+ "$(INTDIR)\ne_md5.obj" \
+ "$(INTDIR)\ne_props.obj" \
+ "$(INTDIR)\ne_redirect.obj" \
+ "$(INTDIR)\ne_request.obj" \
+ "$(INTDIR)\ne_session.obj" \
+ "$(INTDIR)\ne_socket.obj" \
+ "$(INTDIR)\ne_string.obj" \
+ "$(INTDIR)\ne_uri.obj" \
+ "$(INTDIR)\ne_utils.obj" \
+ "$(INTDIR)\ne_207.obj"
+
+"$(OUTDIR)\neonsd.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS)
+ $(LIB32) @<<
+ $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+!ENDIF
+
+
+#!IF "$(NO_EXTERNAL_DEPS)" != "1"
+#!IF EXISTS("neon.dep")
+#!INCLUDE "neon.dep"
+#!ELSE
+#!MESSAGE Warning: cannot find "neon.dep"
+#!ENDIF
+#!ENDIF
+
+
+!IF "$(CFG)" == "neon - Win32 Release" || "$(CFG)" == "neon - Win32 Debug"
+SOURCE=.\src\base64.c
+
+"$(INTDIR)\base64.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_207.c
+
+"$(INTDIR)\ne_207.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_alloc.c
+
+"$(INTDIR)\ne_alloc.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_auth.c
+
+"$(INTDIR)\ne_auth.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_basic.c
+
+"$(INTDIR)\ne_basic.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_cookies.c
+
+"$(INTDIR)\ne_cookies.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_dates.c
+
+"$(INTDIR)\ne_dates.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_i18n.c
+
+"$(INTDIR)\ne_i18n.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_locks.c
+
+"$(INTDIR)\ne_locks.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_md5.c
+
+"$(INTDIR)\ne_md5.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_props.c
+
+"$(INTDIR)\ne_props.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_redirect.c
+
+"$(INTDIR)\ne_redirect.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_request.c
+
+"$(INTDIR)\ne_request.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_session.c
+
+"$(INTDIR)\ne_session.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_socket.c
+
+"$(INTDIR)\ne_socket.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_string.c
+
+"$(INTDIR)\ne_string.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_uri.c
+
+"$(INTDIR)\ne_uri.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_utils.c
+
+"$(INTDIR)\ne_utils.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_xml.c
+
+"$(INTDIR)\ne_xml.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\sslcerts.c
+SOURCE=.\config.hw
+
+!IF "$(CFG)" == "neon - Win32 Release"
+
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ <<tempfile.bat
+ @echo off
+ copy .\config.hw .\src\config.h > nul
+ echo Created config.h from config.hw
+<<
+
+
+!ELSEIF "$(CFG)" == "neon - Win32 Debug"
+
+InputPath=.\config.hw
+
+".\src\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ <<tempfile.bat
+ @echo off
+ copy .\config.hw .\src\config.h > nul
+ echo Created config.h from config.hw
+<<
+
+
+!ENDIF
+
+
+!ENDIF
+
diff --git a/neon/src/.cvsignore b/neon/src/.cvsignore
new file mode 100644
index 000000000..70f06c308
--- /dev/null
+++ b/neon/src/.cvsignore
@@ -0,0 +1,2 @@
+*.lo
+.libs
diff --git a/neon/src/COPYING.LIB b/neon/src/COPYING.LIB
new file mode 100644
index 000000000..161a3d1d4
--- /dev/null
+++ b/neon/src/COPYING.LIB
@@ -0,0 +1,482 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/neon/src/ChangeLog b/neon/src/ChangeLog
new file mode 100644
index 000000000..aa54969af
--- /dev/null
+++ b/neon/src/ChangeLog
@@ -0,0 +1,351 @@
+Wed May 10 19:22:01 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon.h: Bumped version to 0.1.0.
+
+Wed May 10 17:46:48 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * uri.c (uri_parse, uri_free): New functions.
+
+Wed May 10 17:43:37 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (get_to_fd, http_get): Set error appropriately if
+ fwrite() fails.
+
+Wed May 10 14:25:38 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_utils.c (http_debug): New function.
+
+Wed May 10 14:25:08 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (get_callback): Call sock_call_progress.
+
+Wed May 10 14:24:20 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.c (sock_call_progress): New function. (many places): Use
+ it.
+
+Wed May 10 14:22:48 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * uri.c (uri_has_trailing_slash): Moved from being inline.
+
+Tue May 9 23:34:25 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.c: Use handler as userdata for 207 callbacks, unified
+ handler and context structures. (start_prop, end_prop,
+ start_propelm, end_propelm): Removed functions.
+ (dav_propfind_get_current_resource): New function.
+
+Tue May 9 23:29:44 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * xalloc.[ch]: New files.
+
+Tue May 9 23:05:47 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.[ch]: Removed property and property element callbacks.
+
+Tue May 9 23:01:00 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c: Use separate name/namespace for element names.
+ (dav_207_init_with_handler): New function. (end_element):
+ Unescape URI in href element.
+
+Tue May 9 19:54:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.c (dav_propfind_allprop, dav_propfind_named, propfind,
+ start_response, end_response, start_prop, end_prop, start_propelm,
+ end_propelm): New functions; PROPFIND support.
+
+Tue May 9 19:45:17 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (build_request): Renamed from make_request.
+
+Tue May 9 19:36:01 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.[ch]: Added sock_block_reader.
+
+Tue May 9 15:52:56 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * uri.c (uri_childof): Return false when parent is the same length
+ as child.
+
+Sun May 7 15:07:49 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c: Separated element namespace/names.
+
+Tue May 2 16:40:59 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.[ch]: Added HIP_XML_UTF8DECODE flag.
+
+Tue May 2 16:16:57 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.[ch]: Separate element name and namespace.
+
+Mon May 1 00:21:24 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (dav_accept_207): Moved function from dav_basic.c.
+
+ * dav_basic.c (dav_accept_207, dav_parse_xml_block): Removed
+ functions.
+
+Sun Apr 30 22:47:47 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.[ch]: Renamed dav_proppatch_item to
+ dav_proppatch_operation.
+
+Sun Apr 30 22:45:04 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c (start_element): Clearer error message.
+
+Sun Apr 30 19:12:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_content_type_handler, dav_hdr_handler): New
+ functions. (http_options): Handle DAV header.
+
+Sun Apr 30 18:08:53 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_props.c (dav_proppatch): New function.
+
+Sun Apr 30 18:05:55 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (handle_error): New function. (end_response,
+ end_propstat): Use it. (dav_simple_request): Don't return the 207
+ error string if we get all 2xx class status elements.
+
+Sun Apr 30 16:56:41 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (dav_add_depth_header): New function.
+
+Sun Apr 30 14:49:06 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (start_element): Unknown element is only a property if
+ the parent is DAV:propstat.
+
+Sun Apr 30 14:43:28 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (end_response, end_propstat): Only write error line
+ if we have status information and the status is not a 424.
+
+Sun Apr 30 14:28:23 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.h: Added DAV_DEPTH_*.
+
+Sun Apr 30 12:47:50 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (check_context): Allow (and ignore) unknown elements
+ anywhere other than as the root.
+
+Sun Apr 30 12:35:39 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.h (ASC2HEX, HEX2ASC): New macros.
+
+Sun Apr 30 12:34:37 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c [STANDALONE]: Removed. (everywhere): Switch to using
+ md5_to_ascii rather than md5_hexify.
+
+Sun Apr 30 12:32:35 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (read_response_block): Fixed to return errors
+ properly and block length to parameter. (read_response_body):
+ Changed accordingly.
+
+Sun Apr 30 12:29:45 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c (friendly_name): New function, was PRETTY_NAME macro.
+ (start_element, end_element): Fix COLLECT handling.
+ (hip_xml_parse): Only write parse error if the document has not
+ already been marked invalid.
+
+Sun Apr 30 12:28:36 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_basic.c (dav_simple_request): Rewritten for new 207
+ interface. (start_response, end_response, end_propstat): New
+ functions.
+
+Sun Apr 30 12:27:52 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c (dav_207_error): Return the parser error.
+
+Sat Apr 29 14:46:48 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.c (sock_register_progress, sock_register_notify): New
+ functions. (everywhere): Use progress + notify callbacks rather
+ than fe_*.
+
+Sat Apr 29 14:15:23 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.c (md5_to_ascii, ascii_to_md5): New functions.
+
+Sat Apr 29 13:55:39 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c (hip_xml_init): abort() on out-of-memory.
+
+Sat Apr 29 12:56:11 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon_i18n.h: New file.
+
+Sat Apr 29 12:55:24 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.[ch]: Re-implemented with sensible interface.
+
+Fri Apr 28 14:56:01 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c (http_auth_request_header): Renamed from
+ http_auth_request.
+
+ * http_request.c (make_request): As above.
+
+Thu Apr 13 11:52:14 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_put): Switched URI and stream arguments.
+
+Thu Apr 13 09:51:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c: Added user_agent field to session structure.
+ (http_set_useragent): New function. (add_fixed_headers): Only set
+ user-agent if sess->user_agent is set.
+
+Thu Apr 13 09:49:32 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (lookup_host): New function, split from
+ set_hostinfo. (set_hostinfo): Doesn't perform DNS lookup.
+ (http_session_server): Don't do a DNS lookup if we have a proxy.
+
+Wed Apr 12 22:32:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (http_request_dispatch, http_request_create):
+ Store auth header values in local variables rather than request
+ structure. (http_request_create): Don't leak everything on error.
+ Handle http_auth_challenge return value.
+
+Wed Apr 12 22:30:06 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_options): Pass server capabilites object,
+ parse Server header to detect Apache/1.3.6 and before, indicating
+ broken 100-continue support. (server_hdr_handler): New function.
+
+Mon Apr 10 17:42:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.c: Use 'int' for return values.
+
+Mon Apr 10 17:41:40 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c (is_in_domain): Dummy implementation.
+
+Mon Apr 10 17:40:21 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c: Handle read() returning 0 when it shouldn't.
+ i18n'ized error messages.
+
+Mon Apr 10 14:45:09 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dates.[ch], md5.[ch], base64.[ch]: Imported date handling
+ utilities, MD5 checksum functions, and text->base64 converter.
+
+Mon Apr 10 14:44:08 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * Makefile.incl: Dependancies updated for socket.[ch].
+
+Mon Apr 10 14:43:36 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * dav_207.c: Replaced malloc() calls with xmalloc() calls.
+
+Mon Apr 10 14:42:35 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.c, uri.c, string_utils.h: Replaced malloc() calls with
+ xmalloc() calls.
+
+Mon Apr 10 14:41:40 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * socket.[ch]: Imported socket handling utilities.
+
+Mon Apr 10 14:36:03 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.h (CONCAT*): Use xmalloc.
+
+Mon Apr 10 13:52:17 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (set_sockerr): Added handling for socket errors.
+
+Sat Apr 8 13:49:07 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * string_utils.[ch]: Imported string utilites.
+
+Sat Apr 8 00:26:06 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (http_set_persist, http_set_expect100): New
+ functions.
+
+Sat Apr 8 00:25:37 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_basic.c (http_options): New function.
+
+Fri Apr 7 13:01:35 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * neon.h: New file.
+
+Fri Apr 7 12:59:40 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (normalize_response_length, read_response_body):
+ New functions. (http_add_response_body_reader): Take a callback
+ to determine whether the body reader wants to read the response
+ body.
+
+Fri Apr 7 11:46:41 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (http_set_server_auth, http_set_proxy_auth): New
+ functions. (give_creds): Use supplied callbacks for
+ authentication. (get_request_bodysize): Send Content-Length: 0 if
+ no entity-body is being sent with a request. (te_hdr_handler,
+ connection_hdr_handler): New functions. (make_request): Don't use
+ Expect: 100-continue if server is not HTTP/1.1 compliant.
+ (read_message_header): Only read until HTTP_MAXIMUM_HEADER_LENGTH
+ bytes of header have been read. (read_response_headers): No
+ hard-coded header handling. (http_request_create): Set
+ req->method_is_head here.
+
+Thu Apr 6 14:39:28 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.c [HIP_XML_DECODE_UTF8] (decode_utf8_double): New
+ function. (char_data) [HIP_XML_DECODE_UTF8]: Decode UTF-8.
+
+Tue Mar 28 13:54:51 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * Makefile.incl: Imported makefile fragment.
+
+Tue Mar 28 13:54:09 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.[ch] (http_get_error): New function.
+
+Thu Mar 23 18:48:42 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * hip_xml.[ch]: Imported generic XML parsing layer.
+
+ * dav_207.[ch]: Imported generic WebDAV 207 response handling.
+
+ * dav_basic.[ch]: Imported/implemented DAV response handling and
+ basic Class 1 namespace methods.
+
+Thu Mar 23 18:46:14 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.c (add_hooks, run_hooks, http_add_destroy_hook):
+ Adding hooks support. (add_fixed_headers): Send TE token in
+ Connection header. Only send Keep-Alive header & token to pre-1.1
+ origin servers (i.e., not proxies).
+
+Thu Mar 23 12:49:01 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_auth.[ch], uri.[ch]: Imported HTTP authentication and URI
+ handling modules.
+
+Thu Mar 23 12:47:05 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_utils.c: Imported HTTP utility functions.
+
+Thu Mar 23 12:44:38 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * http_request.[ch]: Implemented modular HTTP request handling.
+
+ * http_basic.[ch]: Implemented basic HTTP methods GET, PUT, and
+ PUT with If-Unmodified.
+
diff --git a/neon/src/Makefile.in b/neon/src/Makefile.in
new file mode 100644
index 000000000..534214a90
--- /dev/null
+++ b/neon/src/Makefile.in
@@ -0,0 +1,109 @@
+#
+# TODO: document properly.
+#
+# You need to AC_SUBST:
+# - NEONOBJS
+# - NEON_TARGET (to libneon.la or libneon.a)
+# - NEON_INTERFACE_VERSION (if necessary, which it's probably not for you)
+# - OBJ_EXT (to .lo or .o)
+#
+
+SHELL = @SHELL@
+
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+DEFS = @DEFS@
+INCLUDES = -I.. -I.
+top_builddir = @top_builddir@
+CC = @CC@
+CCLD = $(CC)
+AR = @AR@
+RANLIB = @RANLIB@
+
+LIBS = @LIBS@
+
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+
+LIBTOOL = @LIBTOOL@
+LINK = $(LIBTOOL) --quiet --mode=link $(CCLD) $(LDFLAGS)
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CFLAGS)
+NEON_INTERFACE_VERSION = @NEON_INTERFACE_VERSION@
+
+OBJECTS = @NEONOBJS@
+
+obj_ext = @OBJ_EXT@
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o
+
+NEON_TARGET = @NEON_TARGET@
+
+all: $(NEON_TARGET)
+
+.c.lo:
+ $(LIBTOOL) --quiet --mode=compile $(COMPILE) -c $< -o $@
+.c.o:
+ $(COMPILE) -c $< -o $@
+
+libneon.la: $(OBJECTS)
+ $(LINK) -rpath $(libdir) -version-info $(NEON_INTERFACE_VERSION) -o $@ $(LDFLAGS) $(OBJECTS) $(LIBS)
+
+libneon.a: $(OBJECTS)
+ $(AR) cru $@ $(OBJECTS)
+ $(RANLIB) $@
+
+clean:
+ rm -f @NEONOBJS@ $(NEON_TARGET)
+
+neonreq = http_request.h http_utils.h string_utils.h nsocket.h \
+ ne_alloc.h $(top_builddir)/config.h
+
+http_request.$(obj_ext): http_request.c neon_config.h $(neonreq) \
+ neon_i18n.h http_private.h
+
+socket.$(obj_ext): socket.c nsocket.h $(top_builddir)/config.h string_utils.h
+
+http_auth.$(obj_ext): http_auth.c http_auth.h string_utils.h \
+ $(top_builddir)/config.h dates.h base64.h neon_md5.h
+
+dav_basic.$(obj_ext): dav_basic.c $(neonreq) dav_207.h hip_xml.$(obj_ext)
+
+http_basic.$(obj_ext): http_basic.c http_basic.h $(neonreq)
+
+http_utils.$(obj_ext): http_utils.c $(top_builddir)/config.h \
+ http_utils.h dates.h neon_config.h
+
+hip_xml.$(obj_ext): hip_xml.c hip_xml.h string_utils.h http_utils.h \
+ $(top_builddir)/config.h
+
+dav_207.$(obj_ext): dav_207.c dav_207.h hip_xml.h \
+ $(top_builddir)/config.h http_utils.h neon_i18n.h
+
+string_utils.$(obj_ext): string_utils.c string_utils.h ne_alloc.h \
+ $(top_builddir)/config.h
+
+ne_alloc.$(obj_ext): ne_alloc.c ne_alloc.h $(top_builddir)/config.h
+
+dates.$(obj_ext): dates.c dates.h $(top_builddir)/config.h
+
+base64.$(obj_ext): base64.c base64.h $(top_builddir)/config.h
+
+uri.$(obj_ext): uri.c uri.h http_utils.h string_utils.h ne_alloc.h \
+ $(top_builddir)/config.h
+
+md5.$(obj_ext): md5.c neon_md5.h $(top_builddir)/config.h
+
+dav_props.$(obj_ext): dav_props.c $(top_builddir)/config.h \
+ dav_props.h dav_207.h hip_xml.h
+
+dav_locks.$(obj_ext): dav_locks.c $(neonreq) dav_locks.h dav_207.h hip_xml.h
+
+http_redirect.$(obj_ext): http_redirect.c $(neonreq) http_redirect.h \
+ uri.h http_private.h
+
+http_cookies.$(obj_ext): http_cookies.c $(neonreq) http_cookies.h uri.h \
+ http_private.h
diff --git a/neon/src/Makefile.incl b/neon/src/Makefile.incl
new file mode 100644
index 000000000..87281b854
--- /dev/null
+++ b/neon/src/Makefile.incl
@@ -0,0 +1,45 @@
+# Emacs, are you listening? This is a -*- makefile -*-. Thankyou.
+#
+# This is a Makefile fragment.
+# To use it, set:
+# neon_dir to the location of the neon source directory
+# config_h to the location of the config.h file
+
+neonreq = $(neon_dir)/http_request.h $(neon_dir)/http_utils.h $(neon_dir)/string_utils.h
+
+$(neon_dir)/http_request.o: $(neon_dir)/http_request.c \
+ $(neonreq) $(config_h) $(neon_dir)/neon.h $(neon_dir)/socket.h \
+ $(neon_dir)/neon_i18n.h
+
+$(neon_dir)/socket.o: $(neon_dir)/socket.c $(neon_dir)/socket.h \
+ $(config_h) $(neon_dir)/string_utils.h
+
+$(neon_dir)/http_auth.o: $(neon_dir)/http_auth.c \
+ $(neon_dir)/http_auth.h $(neon_dir)/string_utils.h \
+ $(config_h) $(neon_dir)/dates.h $(neon_dir)/base64.h \
+ $(neon_dir)/md5.h
+
+$(neon_dir)/dav_basic.o: $(neon_dir)/dav_basic.c $(neon_dir)/dav_207.h \
+ $(neonreq) $(neon_dir)/hip_xml.o
+
+$(neon_dir)/http_basic.o: $(neon_dir)/http_basic.c $(neon_dir)/http_basic.h \
+ $(neonreq)
+
+$(neon_dir)/http_utils.o: $(neon_dir)/http_utils.c $(neon_dir)/http_utils.h \
+ $(neon_dir)/dates.h $(config_h)
+
+$(neon_dir)/hip_xml.o: $(neon_dir)/hip_xml.c $(neon_dir)/hip_xml.h \
+ $(neon_dir)/string_utils.h $(neon_dir)/http_utils.h $(config_h)
+
+$(neon_dir)/dav_207.o: $(neon_dir)/dav_207.c $(neon_dir)/dav_207.h \
+ $(neon_dir)/hip_xml.h $(config_h) $(neon_dir)/http_utils.h \
+ $(neon_dir)/neon_i18n.h
+
+$(neon_dir)/dates.o: $(neon_dir)/dates.c $(neon_dir)/dates.h \
+ $(config_h)
+
+$(neon_dir)/md5.o: $(neon_dir)/md5.c $(neon_dir)/md5.h $(config_h)
+
+$(neon_dir)/dav_props.o: $(neon_dir)/dav_props.c $(neon_dir)/dav_props.h \
+ $(neon_dir)/dav_207.h $(neon_dir)/hip_xml.h
+
diff --git a/neon/src/README b/neon/src/README
new file mode 100644
index 000000000..0cb9a3f45
--- /dev/null
+++ b/neon/src/README
@@ -0,0 +1,15 @@
+
+This is the source directory of the 'neon' HTTP/WebDAV client library,
+which can be bundled inside other packages. For the complete neon
+package, see:
+
+ http://www.webdav.org/neon/
+
+This source directory may be distributed and/or modified under the
+terms of the GNU Library General Public License, as given in
+COPYING.LIB.
+
+Please send questions, bug reports, feature requests, etc, regarding
+the neon library, to the mailing list at:
+
+ neon@webdav.org
diff --git a/neon/src/TODO b/neon/src/TODO
new file mode 100644
index 000000000..125bd331b
--- /dev/null
+++ b/neon/src/TODO
@@ -0,0 +1,98 @@
+
+To Do List for neon -*- text -*-
+------------------- Id: TODO,v 1.12 2000/05/10 18:15:47 joe Exp
+
+Please submit feature requests to <mailto:neon@webdav.org>
+
+1. Support for HTTP-extended authoring methods ala WebRFM etc; using
+ New-URI header etc. Also support the BROWSE and INDEX methods. The
+ protocol is documented at:
+ http://www.ics.uci.edu/pub/ietf/webdav/ns_dav.html
+
+2. Add proper domain support to authentication code. (requires full
+ URI parsing support). Need to tell the auth layer the server
+ details.
+
+3. Add a callback to determine whether a specific request should go
+ through the proxy server or not... based on URI, maybe method too
+ since lots of HTTP/1.0 proxies break on DAV requests?
+
+4. Better cnonce generation for authentication: use /dev/random or
+ whatever like mod_auth_digest.
+
+5. Add request hooks to remove all authentication code from
+ http_request.c.
+
+6. PUT/GET with ranges... http_get_range
+
+7. hip_xml/dav_207/dav_prop might need a revamp.
+
+ hip_xml: use an expat-esque interface; i.e. make hip_xml_parser
+ opaque and add API to set handlers rather than initialize
+ structures directly. Two problems need to be solved more elegantly:
+
+ 1) Allowing "sub-handler" for allowing parsing property element
+ contents independantly of the overally 207 response parse.
+
+ 2) Allow "mixed-mode" parsing of XML which mixes cdata + elements.
+
+ Probably, make hip_xml more of a "utility" layer rather than a
+ "driving" layer; abstract out expat/libxml, namespace lookups,
+ element name -> id mapping, easy CDATA handling...
+
+8. WebDAV class 2 locking. Problems: this requires parsing the XML
+ within a property element. This could be achieved by doing a
+ completely new XML parse of the property value returned by end_prop
+ from the 207 code, but this is a bit noddy. Options:
+
+ a) Ideally: extend hip_xml and the 207 layer to allow doing a
+ proper start/end_element/cdata parse of the XML *within* a
+ property. This is tricky since it means extending hip_xml to
+ *dynamically* determine whether to be in "collect" mode or not. Add
+ another callback for this?
+
+9. DeltaV support (http://www.webdav.org/deltav/). See also the
+ inversion project (http://inversion.tigris.org/) who might build a
+ versioning system over DAV.
+
+10. ACL support (http://www.webdav.org/acl/)
+
+11. DASL support (http://www.webdav.org/dasl/). Xythos have server
+ support for this (www.sharemation.com).
+
+12. SSL/TSL support... make it pluggable so we don't have to export
+ crypto-related code at ALL?
+
+13. Should we really be abort()'ing on out-of-memory? It makes a lot
+ of code MUCH simpler (esp. sbuffer_* usage).
+
+14. Nicer request-header manipulation... some kind of indexed data
+ structure, so we're sure we don't add the same header to the
+ request twice (e.g. Cache-Control). Must be at least as flexible
+ as sbuffer usage, though.
+
+16. Socket status notification (socket.c:sock_register_*) is awful.
+
+17. Should we really be i18n'izing the low-level error messages in
+ http_request.c, dav_207.c ? It seems nice and clever to, so the
+ user REALLY know what is going wrong with the server (probably),
+ but it is maybe a bit frightening.
+
+18. PROPFIND/propnames support.
+
+19. libtool support, for proper shared libraries.
+
+20. Add full URI parser + handling. Or stop pretending we are doing
+ "URI" parsing, and just handle HTTP URL's.
+
+21. Storing multiple authentication "sessions" within an actual
+ http_auth_session, so I log into e.g. /foo/ and /bar/ (which
+ are not in the same authentication domain)
+ switch between them without having to re-enter passwords all the
+ time.
+
+22. Handle PROPFIND property error responses properly.
+
+23. Mechanism for aborting a request mid-response; e.g., when a GET
+ fails due to out of disk space, abort the download.
+
diff --git a/neon/src/base64.c b/neon/src/base64.c
new file mode 100644
index 000000000..54bb2243e
--- /dev/null
+++ b/neon/src/base64.c
@@ -0,0 +1,103 @@
+/*
+ Text->Base64 convertion, from RFC1521
+ Copyright (C) 1998,1999,2000 Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: base64.c,v 1.3 2000/05/09 18:26:58 joe Exp
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "xalloc.h"
+#include "base64.h"
+
+/* Base64 specification from RFC 1521 */
+
+static const char b64_alphabet[65] = {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/=" };
+
+char *base64( const char *text )
+{
+ /* The tricky thing about this is doing the padding at the end,
+ * doing the bit manipulation requires a bit of concentration only */
+ char *buffer, *point;
+ int inlen, outlen;
+
+ /* Use 'buffer' to store the output. Work out how big it should be...
+ * This must be a multiple of 4 bytes */
+
+ inlen = strlen( text );
+ outlen = (inlen*4)/3;
+ if( (inlen % 3) > 0 ) /* got to pad */
+ outlen += 4 - (inlen % 3);
+
+ buffer = xmalloc( outlen + 1 ); /* +1 for the \0 */
+
+ /* now do the main stage of conversion, 3 bytes at a time,
+ * leave the trailing bytes (if there are any) for later */
+
+ for( point=buffer; inlen>=3; inlen-=3, text+=3 ) {
+ *(point++) = b64_alphabet[ (*text)>>2 ];
+ *(point++) = b64_alphabet[ ((*text)<<4 & 0x30) | (*(text+1))>>4 ];
+ *(point++) = b64_alphabet[ ((*(text+1))<<2 & 0x3c) | (*(text+2))>>6 ];
+ *(point++) = b64_alphabet[ (*(text+2)) & 0x3f ];
+ }
+
+ /* Now deal with the trailing bytes */
+ if( inlen ) {
+ /* We always have one trailing byte */
+ *(point++) = b64_alphabet[ (*text)>>2 ];
+ *(point++) = b64_alphabet[ ( ((*text)<<4 & 0x30) |
+ (inlen==2?(*(text+1))>>4:0) ) ];
+ *(point++) = (inlen==1?'=':b64_alphabet[ (*(text+1))<<2 & 0x3c ] );
+ *(point++) = '=';
+ }
+
+ /* Null-terminate */
+ *point = '\0';
+
+ return buffer;
+}
+
+#ifdef BASE64_TEST
+
+/* Standalone tester */
+
+#include <stdio.h>
+
+/* Tester for Base64 algorithm */
+int main( int argc, char **argv ) {
+ char *out;
+ if( argc != 2 ) {
+ printf( "Usage: %s plaintext\n", argv[0] );
+ exit(-1);
+ }
+ out = base64( argv[1] );
+ printf( "Plain: [%s], Base64: [%s]\n", argv[1], out );
+ return 0;
+}
+
+#endif /* BASE64_TEST */
+
diff --git a/neon/src/base64.h b/neon/src/base64.h
new file mode 100644
index 000000000..3c4f55a17
--- /dev/null
+++ b/neon/src/base64.h
@@ -0,0 +1,32 @@
+/*
+ Text->Base64 convertion, from RFC1521
+ Copyright (C) 1998-2000 Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef BASE64_H
+#define BASE64_H
+
+/* Plain->Base64 converter - will malloc the buffer to the correct size,
+ * and fill it with the Base64 conversion of the parameter.
+ * Base64 specification from RFC 1521. You must free() it. */
+
+char *base64( const char *text );
+
+#endif /* BASE64_H */
+
diff --git a/neon/src/dates.c b/neon/src/dates.c
new file mode 100644
index 000000000..6c5e93d8f
--- /dev/null
+++ b/neon/src/dates.c
@@ -0,0 +1,163 @@
+/*
+ Date manipulation routines
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: dates.c,v 1.5 2000/05/09 18:25:37 joe Exp
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#include <time.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+
+#include "xalloc.h"
+#include "dates.h"
+
+/* Generic date manipulation routines. */
+
+/* RFC1123: Sun, 06 Nov 1994 08:49:37 GMT */
+#define RFC1123_FORMAT "%3s, %02d %3s %4d %02d:%02d:%02d GMT"
+/* RFC850: Sunday, 06-Nov-94 08:49:37 GMT */
+#define RFC1036_FORMAT "%s, %2d-%3s-%2d %2d:%2d:%2d GMT"
+/* asctime: Wed Jun 30 21:49:08 1993 */
+#define ASCTIME_FORMAT "%3s %3s %2d %2d:%2d:%2d %4d"
+
+static const char *rfc1123_weekdays[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+static const char *short_months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* Returns the time/date GMT, in RFC1123-type format: eg
+ * Sun, 06 Nov 1994 08:49:37 GMT. */
+char *rfc1123_date( time_t anytime ) {
+ struct tm *gmt;
+ char *ret;
+ gmt = gmtime( &anytime );
+ ret = xmalloc( 29 + 1 ); /* dates are 29 chars long */
+/* it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+ snprintf( ret, 30, RFC1123_FORMAT,
+ rfc1123_weekdays[gmt->tm_wday], gmt->tm_mday,
+ short_months[gmt->tm_mon], 1900 + gmt->tm_year,
+ gmt->tm_hour, gmt->tm_min, gmt->tm_sec );
+
+ return ret;
+}
+
+/* Takes an RFC1123-formatted date string and returns the time_t.
+ * Returns (time_t)-1 if the parse fails. */
+time_t rfc1123_parse( const char *date ) {
+ struct tm gmt = {0};
+ static char wkday[4], mon[4];
+ int n;
+/* it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+ n = sscanf( date, RFC1123_FORMAT,
+ wkday, &gmt.tm_mday, mon, &gmt.tm_year, &gmt.tm_hour,
+ &gmt.tm_min, &gmt.tm_sec );
+ /* Is it portable to check n==7 here? */
+ gmt.tm_year -= 1900;
+ for( n=0; n<12; n++ )
+ if( strcmp( mon, short_months[n] ) == 0 )
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ return mktime( &gmt );
+}
+
+/* Takes a string containing a RFC1036-style date and returns the time_t */
+time_t rfc1036_parse( const char *date ) {
+ struct tm gmt = {0};
+ int n;
+ static char wkday[10], mon[4];
+ /* RFC850/1036 style dates: Sunday, 06-Nov-94 08:49:37 GMT */
+ n = sscanf( RFC1036_FORMAT,
+ wkday, &gmt.tm_mday, mon, &gmt.tm_year,
+ &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec );
+ /* portable to check n here? */
+ for( n=0; n<12; n++ )
+ if( strcmp( mon, short_months[n] ) == 0 )
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ return mktime( &gmt );
+}
+
+
+/* (as)ctime dates are like:
+ * Wed Jun 30 21:49:08 1993
+ */
+time_t asctime_parse( const char *date ) {
+ struct tm gmt = {0};
+ int n;
+ static char wkday[4], mon[4];
+ n = sscanf( ASCTIME_FORMAT,
+ wkday, mon, &gmt.tm_mday,
+ &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec,
+ &gmt.tm_year );
+ /* portable to check n here? */
+ for( n=0; n<12; n++ )
+ if( strcmp( mon, short_months[n] ) == 0 )
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ return mktime( &gmt );
+}
+
+#undef RFC1036_FORMAT
+#undef ASCTIME_FORMAT
+#undef RFC1123_FORMAT
+
+#ifdef RFC1123_TEST
+
+int main( int argc, char **argv ) {
+ time_t now, in;
+ char *out;
+ if( argc > 1 ) {
+ printf( "Got: %s\n", argv[1] );
+ in = rfc1123_parse( argv[1] );
+ printf( "Parsed: %d\n", in );
+ out = rfc1123_date( in );
+ printf( "Back again: %s\n", out );
+ } else {
+ now = time(NULL);
+ out = rfc1123_date(now);
+ in = rfc1123_parse(out);
+ printf( "time(NULL) = %d\n", now );
+ printf( "RFC1123 Time: [%s]\n", out );
+ printf( "Parsed = %d\n", in );
+ out = rfc1123_date( in );
+ printf( "Back again: [%s]\n", out );
+ }
+ return 0;
+}
+
+#endif
+
+
diff --git a/neon/src/dates.h b/neon/src/dates.h
new file mode 100644
index 000000000..9e6a99579
--- /dev/null
+++ b/neon/src/dates.h
@@ -0,0 +1,42 @@
+/*
+ Date manipulation routines
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DATES_H
+#define DATES_H
+
+#include "config.h"
+
+#include <sys/types.h>
+
+/* Date manipulation routines as per RFC1123 and RFC1036 */
+
+/* Return current date/time in RFC1123 format */
+char *rfc1123_date( time_t anytime );
+
+/* Returns time from date/time in RFC1123 format */
+time_t rfc1123_parse( const char *date );
+
+time_t rfc1036_parse( const char *date );
+
+/* Parses asctime date string */
+time_t asctime_parse( const char *date );
+
+#endif /* DATES_H */
diff --git a/neon/src/dav_207.c b/neon/src/dav_207.c
new file mode 100644
index 000000000..6ee7807f3
--- /dev/null
+++ b/neon/src/dav_207.c
@@ -0,0 +1,262 @@
+/*
+ WebDAV 207 multi-status response handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: dav_207.c,v 1.17 2000/05/10 12:02:23 joe Exp
+*/
+
+/* Generic handling for WebDAV 207 Multi-Status responses. */
+
+#include <config.h>
+
+#include "http_utils.h"
+#include "hip_xml.h"
+#include "dav_207.h"
+#include "uri.h"
+#include "xalloc.h"
+
+#include "neon_i18n.h"
+
+struct dav_207_parser_s {
+ dav_207_start_response start_response;
+ dav_207_end_response end_response;
+ dav_207_start_propstat start_propstat;
+ dav_207_end_propstat end_propstat;
+ struct hip_xml_parser parser;
+ struct hip_xml_handler handler;
+ void *userdata;
+ /* current position */
+ void *response, *propstat;
+ /* caching */
+ http_status status;
+ char *description, *href, *status_line;
+};
+
+const static struct hip_xml_elm elements[] = {
+ { "DAV:", "multistatus", DAV_ELM_multistatus, 0 },
+ { "DAV:", "response", DAV_ELM_response, 0 },
+ { "DAV:", "responsedescription", DAV_ELM_responsedescription, 0 },
+ { "DAV:", "href", DAV_ELM_href, HIP_XML_CDATA },
+ { "DAV:", "propstat", DAV_ELM_propstat, 0 },
+ { "DAV:", "prop", DAV_ELM_prop, 0 },
+ { "DAV:", "status", DAV_ELM_status, HIP_XML_CDATA },
+#if 0
+ { "", "", HIP_ELM_unknown, HIP_XML_COLLECT },
+#endif
+ { NULL }
+};
+
+/* Set the callbacks for the parser */
+void dav_207_set_response_handlers( dav_207_parser *p,
+ dav_207_start_response start,
+ dav_207_end_response end )
+{
+ p->start_response = start;
+ p->end_response = end;
+}
+
+void dav_207_set_propstat_handlers( dav_207_parser *p,
+ dav_207_start_propstat start,
+ dav_207_end_propstat end )
+{
+ p->start_propstat = start;
+ p->end_propstat = end;
+}
+
+/* Parse an input block, as hip_xml_parse */
+void dav_207_parse( void *parser, const char *block, size_t len )
+{
+ dav_207_parser *p = parser;
+ hip_xml_parse( &p->parser, block, len );
+}
+
+static int
+start_element( void *userdata, const struct hip_xml_state *s, const hip_xml_char **atts )
+{
+ dav_207_parser *p = userdata;
+
+ switch( s->elm->id ) {
+ case DAV_ELM_response:
+ /* Create new response delayed until we get HREF */
+ break;
+ case DAV_ELM_propstat:
+ if( p->start_propstat ) {
+ p->propstat = (*p->start_propstat)( p->userdata, p->response );
+ }
+ break;
+ }
+ return 0;
+}
+
+static int
+end_element( void *userdata, const struct hip_xml_state *s, const char *cdata )
+{
+ dav_207_parser *p = userdata;
+
+ switch( s->elm->id ) {
+ case DAV_ELM_responsedescription:
+ p->description = xstrdup(cdata);
+ break;
+ case DAV_ELM_href:
+ /* Now we have the href, begin the response */
+ if( p->start_response && cdata != NULL ) {
+ char *uri = uri_unescape( cdata );
+ p->response = (*p->start_response)( p->userdata, uri );
+ free( uri );
+ }
+ break;
+ case DAV_ELM_status:
+ switch( s->parent->elm->id ) {
+ case DAV_ELM_propstat:
+ case DAV_ELM_response:
+ p->status_line = xstrdup(cdata);
+ if( http_parse_statusline( p->status_line, &p->status ) ) {
+ snprintf( p->parser.error, BUFSIZ,
+ _("Invalid HTTP status line in status element at line %d of response"),
+ hip_xml_currentline(&p->parser) );
+ HTTP_FREE( p->status_line );
+ return -1;
+ }
+ break;
+ }
+ break;
+ case DAV_ELM_propstat:
+ if( p->end_propstat ) {
+ (*p->end_propstat)( p->userdata, p->propstat, p->status_line,
+ p->status_line?&p->status:NULL, p->description );
+ }
+ HTTP_FREE( p->status_line );
+ break;
+ case DAV_ELM_response:
+ if( p->end_response ) {
+ (*p->end_response)( p->userdata, p->response, p->status_line,
+ p->status_line?&p->status:NULL );
+ }
+ HTTP_FREE( p->status_line );
+ break;
+ }
+ return 0;
+}
+
+/* This should map directly from the DTD... with the addition of
+ * ignoring anything we don't understand, being liberal in what we
+ * accept. */
+static int check_context( hip_xml_elmid parent, hip_xml_elmid child )
+{
+ switch( parent ) {
+ case HIP_ELM_root:
+ switch( child ) {
+ case DAV_ELM_multistatus:
+ case DAV_ELM_response: /* not sure why this is here... */
+ return 0;
+ default:
+ }
+ break;
+ case DAV_ELM_multistatus:
+ /* <!ELEMENT multistatus (response+, responsedescription?) > */
+ switch( child ) {
+ case DAV_ELM_response:
+ case DAV_ELM_responsedescription:
+ case HIP_ELM_unknown:
+ return 0;
+ default:
+ }
+ break;
+ case DAV_ELM_response:
+ /* <!ELEMENT response (href, ((href*, status)|(propstat+)),
+ responsedescription?) > */
+ switch( child ) {
+ case DAV_ELM_href:
+ case DAV_ELM_status:
+ case DAV_ELM_propstat:
+ case DAV_ELM_responsedescription:
+ case HIP_ELM_unknown:
+ return 0;
+ default:
+ }
+ break;
+ case DAV_ELM_propstat:
+ /* <!ELEMENT propstat (prop, status, responsedescription?) > */
+ switch( child ) {
+ case DAV_ELM_prop:
+ case DAV_ELM_status:
+ case DAV_ELM_responsedescription:
+ case HIP_ELM_unknown:
+ return 0;
+ default:
+ }
+ break;
+ case DAV_ELM_prop:
+ /* <!ELEMENT prop ANY > */
+ switch( child ) {
+ case HIP_ELM_unknown:
+ return 0;
+ default:
+ break;
+ }
+ break;
+ case HIP_ELM_unknown:
+ return 0;
+ default:
+ break;
+ }
+ return -1;
+}
+
+static dav_207_parser *init_parser( void *userdata, struct hip_xml_handler *extra )
+{
+ dav_207_parser *p = xmalloc( sizeof(dav_207_parser) );
+ memset( p, 0, sizeof(dav_207_parser) );
+ p->handler.validate_cb = check_context;
+ p->handler.startelm_cb = start_element;
+ p->handler.endelm_cb = end_element;
+ p->handler.userdata = p;
+ p->handler.elements = elements;
+ p->handler.next = extra;
+ p->userdata = userdata;
+ hip_xml_init( &p->parser, &p->handler );
+ return p;
+}
+
+dav_207_parser *dav_207_init_with_handler( void *userdata,
+ struct hip_xml_handler *extra )
+{
+ return init_parser( userdata, extra );
+}
+
+dav_207_parser *dav_207_init( void *userdata )
+{
+ return init_parser( userdata, NULL );
+}
+
+int dav_207_finish( dav_207_parser *p ) {
+ int ret = hip_xml_finish( &p->parser );
+ HTTP_FREE( p->response );
+ free( p );
+ return ret;
+}
+
+const char *dav_207_error( dav_207_parser *p )
+{
+ return p->parser.error;
+}
+
+int dav_accept_207( void *userdata, http_req *req, http_status *status )
+{
+ return (status->code == 207);
+}
diff --git a/neon/src/dav_207.h b/neon/src/dav_207.h
new file mode 100644
index 000000000..2881d62a7
--- /dev/null
+++ b/neon/src/dav_207.h
@@ -0,0 +1,91 @@
+/*
+ WebDAV 207 multi-status response handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DAV207_H
+#define DAV207_H
+
+#include "hip_xml.h"
+#include "http_utils.h" /* for struct http_status */
+#include "http_request.h" /* for http_req */
+
+#define DAV_ELM_multistatus (101)
+#define DAV_ELM_response (102)
+#define DAV_ELM_responsedescription (103)
+#define DAV_ELM_href (104)
+#define DAV_ELM_propstat (105)
+#define DAV_ELM_prop (106)
+#define DAV_ELM_status (107)
+
+struct dav_207_parser_s;
+typedef struct dav_207_parser_s dav_207_parser;
+
+/* The name of a WebDAV property. */
+typedef struct {
+ const char *nspace, *name;
+} dav_propname;
+
+/* The handler structure: you provide a set of callbacks.
+ * They are called in the order they are listed... start/end_prop
+ * multiple times before end_prop, start/end_propstat multiple times
+ * before an end_response, start/end_response multiple times.
+ */
+
+/* TODO: do we need to pass userdata to ALL of these? We could get away with
+ * only passing the userdata to the start_'s and relying on the caller
+ * to send it through as the _start return value if they need it. */
+
+typedef void *(*dav_207_start_response)( void *userdata, const char *href );
+typedef void (*dav_207_end_response)(
+ void *userdata, void *response, const char *status_line, const http_status *status );
+
+typedef void *(*dav_207_start_propstat)( void *userdata, void *response );
+typedef void (*dav_207_end_propstat)(
+ void *userdata, void *propstat, const char *status_line, const http_status *status,
+ const char *description );
+
+/* Create a 207 parser */
+
+dav_207_parser *dav_207_init( void *userdata );
+
+dav_207_parser *dav_207_init_with_handler( void *userdata,
+ struct hip_xml_handler *extra );
+
+/* Set the callbacks for the parser */
+
+void dav_207_set_response_handlers(
+ dav_207_parser *p, dav_207_start_response start, dav_207_end_response end );
+
+void dav_207_set_propstat_handlers(
+ dav_207_parser *p, dav_207_start_propstat start, dav_207_end_propstat end );
+
+/* Parse an input block, as hip_xml_parse */
+void dav_207_parse( void *parser, const char *block, size_t len );
+
+/* Finish parsing... returns 0 if the parse was successful, else
+ * non-zero. */
+int dav_207_finish( dav_207_parser *p );
+
+const char *dav_207_error( dav_207_parser *p );
+
+/* An acceptance function which only accepts 207 responses */
+int dav_accept_207( void *userdata, http_req *req, http_status *status );
+
+#endif /* DAV207_H */
diff --git a/neon/src/dav_basic.c b/neon/src/dav_basic.c
new file mode 100644
index 000000000..eccc2acb3
--- /dev/null
+++ b/neon/src/dav_basic.c
@@ -0,0 +1,232 @@
+/*
+ WebDAV Class 1 namespace operations and 207 error handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: dav_basic.c,v 1.12 2000/05/09 18:28:00 joe Exp
+*/
+
+#include "config.h"
+
+#include "http_request.h"
+
+#include "dav_basic.h"
+#include "uri.h" /* for uri_has_trailing_slash */
+#include "http_basic.h" /* for http_content_type */
+#include "string_utils.h" /* for sbuffer */
+#include "dav_207.h"
+
+/* Handling of 207 errors: we keep a string buffer, and append
+ * messages to it as they come down.
+ *
+ * Note, 424 means it would have worked but something else went wrong.
+ * We will have had the error for "something else", so we display
+ * that, and skip 424 errors. TODO: should display 'description' too,
+ * but find out whether servers actually use this first. */
+
+/* This is passed as userdata to the 207 code. */
+struct context {
+ char *href;
+ sbuffer buf;
+ unsigned int is_error;
+};
+
+static void *start_response( void *userdata, const char *href )
+{
+ struct context *ctx = userdata;
+ HTTP_FREE( ctx->href );
+ ctx->href = xstrdup(href);
+ return NULL;
+}
+
+static void handle_error( struct context *ctx,
+ const char *status_line, const http_status *status )
+{
+ if( status && status->class != 2 ) {
+ ctx->is_error = 1;
+ if( status->code != 424 ) {
+ sbuffer_concat( ctx->buf, ctx->href, ": ", status_line, "\n", NULL );
+ }
+ }
+}
+
+static void end_response( void *userdata, void *response, const char *status_line,
+ const http_status *status )
+{
+ struct context *ctx = userdata;
+ handle_error( ctx, status_line, status );
+}
+
+static void
+end_propstat( void *userdata, void *propstat, const char *status_line,
+ const http_status *status, const char *description )
+{
+ struct context *ctx = userdata;
+ handle_error( ctx, status_line, status );
+}
+
+void dav_add_depth_header( http_req *req, int depth )
+{
+ sbuffer hdrs = http_get_request_header( req );
+ switch( depth ) {
+ case DAV_DEPTH_ZERO:
+ sbuffer_zappend( hdrs, "Depth: 0" EOL );
+ break;
+ case DAV_DEPTH_ONE:
+ sbuffer_zappend( hdrs, "Depth: 1" EOL );
+ break;
+ default:
+ sbuffer_zappend( hdrs, "Depth: infinity" EOL );
+ break;
+ }
+}
+
+/* Dispatch a DAV request and handle a 207 error response appropriately */
+int dav_simple_request( http_session *sess, http_req *req )
+{
+ int ret, invalid;
+ http_status status;
+ http_content_type ctype = {0};
+ struct context ctx = {0};
+ dav_207_parser *p;
+
+ p = dav_207_init( &ctx );
+ ctx.buf = sbuffer_create();
+
+ dav_207_set_response_handlers( p, start_response, end_response );
+ dav_207_set_propstat_handlers( p, NULL, end_propstat );
+
+ http_add_response_body_reader( req, dav_accept_207, dav_207_parse, p );
+ http_add_response_header_handler( req, "Content-Type",
+ http_content_type_handler, &ctype );
+
+ ret = http_request_dispatch( req, &status );
+
+ invalid = dav_207_finish( p );
+
+ if( ret == HTTP_OK ) {
+ if( status.code == 207 ) {
+ if( invalid ) {
+ http_set_error( sess, dav_207_error(p) );
+ ret = HTTP_ERROR;
+ } else if( ctx.is_error ) {
+ http_set_error( sess, sbuffer_data(ctx.buf) );
+ ret = HTTP_ERROR;
+ }
+ } else if( status.class != 2 ) {
+ ret = HTTP_ERROR;
+ }
+ }
+
+ sbuffer_destroy(ctx.buf);
+ HTTP_FREE(ctx.href);
+
+ http_request_destroy( req );
+
+ return ret;
+}
+
+static int copy_or_move( http_session *sess, int is_move, int no_overwrite,
+ const char *src, const char *dest ) {
+ http_req *req = http_request_create( sess, is_move?"MOVE":"COPY", src );
+ const char *str;
+ sbuffer hdrs;
+
+#ifdef USE_DAV_LOCKS
+ /* Copy needs to read/write the source, and write to the destination */
+ dav_lock_using_resource( req, src,
+ is_move?dav_lockusage_write:dav_lockusage_read,
+ DAV_DEPTH_INFINITE );
+ dav_lock_using_resource( req, dest, dav_lockusage_write,
+ DAV_DEPTH_INFINITE );
+ /* And we need to be able to add members to the destination's parent */
+ dav_lock_using_parent( req, dest );
+#endif
+
+ hdrs = http_get_request_header( req );
+ str = http_get_server_hostport( sess );
+ sbuffer_concat( hdrs, "Destination: http://", str, dest, EOL, NULL );
+
+ if( no_overwrite )
+ sbuffer_zappend( hdrs, "Overwrite: F" EOL );
+
+#if 0
+ /* FIXME */
+ if( ! http_webdav_server ) {
+ /* For non-WebDAV servers */
+ strcat( req.headers, "New-URI: " );
+ strcat( req.headers, to );
+ strcat( req.headers, EOL );
+ }
+#endif
+
+ return dav_simple_request( sess, req );
+}
+
+int dav_copy( http_session *sess, const char *src, const char *dest ) {
+ return copy_or_move( sess, 0, 1, src, dest );
+}
+
+int dav_move( http_session *sess, const char *src, const char *dest ) {
+ return copy_or_move( sess, 1, 1, src, dest );
+}
+
+/* Deletes the specified resource. (and in only two lines of code!) */
+int dav_delete( http_session *sess, const char *uri ) {
+ http_req *req = http_request_create( sess, "DELETE", uri );
+
+#ifdef USE_DAV_LOCKS
+ dav_lock_using_resource( req, uri, dav_lockusage_write, DAV_DEPTH_INFINITE );
+ dav_lock_using_parent( req, uri );
+#endif
+
+ /* joe: I asked on the DAV WG list about whether we might get a
+ * 207 error back from a DELETE... conclusion, you shouldn't if
+ * you don't send the Depth header, since we might be an HTTP/1.1
+ * client and a 2xx response indicates success to them. But
+ * it's all a bit unclear. In any case, DAV servers today do
+ * return 207 to DELETE even if we don't send the Depth header.
+ * So we handle 207 errors appropriately. */
+
+ return dav_simple_request( sess, req );
+}
+
+int dav_mkcol( http_session *sess, const char *uri )
+{
+ http_req *req;
+ char *real_uri;
+ int ret;
+
+ if( uri_has_trailing_slash( uri ) ) {
+ real_uri = xstrdup( uri );
+ } else {
+ CONCAT2( real_uri, uri, "/" );
+ }
+
+ req = http_request_create( sess, "MKCOL", real_uri );
+
+#ifdef USE_DAV_LOCKS
+ dav_lock_using_resource( req, real_uri, dav_lockusage_write, 0 );
+ dav_lock_using_parent( req, real_uri );
+#endif
+
+ ret = dav_simple_request( sess, req );
+
+ free( real_uri );
+
+ return ret;
+}
diff --git a/neon/src/dav_basic.h b/neon/src/dav_basic.h
new file mode 100644
index 000000000..a2363e52f
--- /dev/null
+++ b/neon/src/dav_basic.h
@@ -0,0 +1,64 @@
+/*
+ Basic WebDAV support
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DAV_BASIC_H
+#define DAV_BASIC_H
+
+#include "http_request.h"
+
+#include "dav_207.h"
+
+#define DAV_DEPTH_ZERO (0)
+#define DAV_DEPTH_ONE (1)
+#define DAV_DEPTH_INFINITE (2)
+
+/* Adds a Depth: header to a request */
+void dav_add_depth_header( http_req *req, int depth );
+
+/* Handle a simple WebDAV request.
+ *
+ * Usage:
+ * 1. Create the request using http_request_create()
+ * 2. Set any headers, the request body, whatever.
+ * 3. Call dav_simple_request to dispatch and destroy the request.
+ *
+ * (note the request IS destroyed by this function, don't do it
+ * yourself).
+ *
+ * Returns HTTP_* as http_request_dispatch() would. If the response is
+ * a 207, a user-friendly error message is written to the session
+ * error buffer; e.g. DELETE /foo/ might give the error:
+ * /foo/bar: HTTP/1.1 423 Locked
+ */
+int dav_simple_request( http_session *sess, http_req *req );
+
+/* Basic WebDAV methods:
+ * dav_copy: copy resoure from src to dest
+ * dav_move: move resource from src to dest
+ * dav_delete: delete resource at uri
+ * dav_mkcol: create a collection at uri (uri MUST have a trailing slash).
+ */
+int dav_copy( http_session *sess, const char *src, const char *dest );
+int dav_move( http_session *sess, const char *src, const char *dest );
+int dav_delete( http_session *sess, const char *uri );
+int dav_mkcol( http_session *sess, const char *uri );
+
+#endif
diff --git a/neon/src/dav_locks.c b/neon/src/dav_locks.c
new file mode 100644
index 000000000..65acd3171
--- /dev/null
+++ b/neon/src/dav_locks.c
@@ -0,0 +1,622 @@
+/*
+ WebDAV Class 2 locking operations
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: dav_locks.c,v 1.3 2000/07/16 15:39:25 joe Exp
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "http_request.h"
+#include "dav_locks.h"
+#include "dav_basic.h"
+#include "dav_props.h"
+#include "uri.h"
+#include "dav_207.h"
+#include "neon_i18n.h"
+#include "hip_xml.h"
+
+#include "xalloc.h"
+
+#define HOOK_ID "http://webdav.org/neon/hooks/webdav-locking"
+
+/* The list of locks to submit in an If header
+ * for the current requests */
+struct submit_locks {
+ const struct dav_lock *lock;
+ const char *uri;
+ struct submit_locks *next;
+};
+
+struct dav_lock_session_s {
+ struct dav_lock *locks;
+};
+
+/* Per-request lock structure */
+struct request_locks {
+ struct submit_locks *locks; /* for the If header */
+ dav_lock_session *session;
+};
+
+/* Context for PROPFIND/lockdiscovery callbacks */
+struct lock_discovery {
+ struct dav_lock_result *list;
+};
+
+/* Hook callbacks */
+static void *create(void *session, http_req *req,
+ const char *method, const char *uri);
+static void pre_send(void *private, sbuffer req);
+static void destroy(void *private);
+
+/* The hooks are needed for construction of the If header */
+static http_request_hooks lock_hooks = {
+ HOOK_ID, /* unique id for the locking hooks */
+ create,
+ NULL, /* use_body not needed */
+ pre_send,
+ NULL, /* post_send not needed */
+ destroy
+};
+
+/* bit random, these */
+#define DAV_ELM_lockdiscovery 2
+#define DAV_ELM_activelock 3
+#define DAV_ELM_lockscope 4
+#define DAV_ELM_locktype 5
+#define DAV_ELM_depth 6
+#define DAV_ELM_owner 7
+#define DAV_ELM_timeout 9
+#define DAV_ELM_locktoken 10
+#define DAV_ELM_lockinfo 11
+#define DAV_ELM_write 14
+#define DAV_ELM_exclusive 15
+#define DAV_ELM_shared 16
+
+static const struct hip_xml_elm lock_elms[] = {
+#define A(x) { "DAV:", #x, DAV_ELM_ ## x, HIP_XML_COLLECT } /* ANY */
+#define D(x) { "DAV:", #x, DAV_ELM_ ## x, 0 } /* normal */
+#define C(x) { "DAV:", #x, DAV_ELM_ ## x, HIP_XML_CDATA } /* (#PCDATA) */
+#define E(x) { "DAV:", #x, DAV_ELM_ ## x, 0 /* LEAF */ } /* EMPTY */
+ D(lockdiscovery), D(activelock),
+ D(prop),
+ D(lockscope), D(locktype), C(depth), A(owner), C(timeout), D(locktoken),
+ /* no lockentry */
+ D(lockinfo), D(lockscope), D(locktype),
+ E(write), E(exclusive), E(shared),
+ C(href),
+#undef A
+#undef D
+#undef C
+#undef E
+ { NULL, 0, 0 }
+};
+
+static void *create(void *session, http_req *req,
+ const char *method, const char *uri)
+{
+ struct request_locks *rl = xcalloc(sizeof *rl);
+ rl->session = session;
+ return rl;
+}
+
+static void pre_send(void *private, sbuffer req)
+{
+ struct request_locks *rl = private;
+
+ if (rl->locks != NULL) {
+ struct submit_locks *item;
+
+ /* Add in the If header */
+ sbuffer_zappend(req, "If:");
+ for (item = rl->locks; item != NULL; item = item->next) {
+ sbuffer_concat(req, " <", item->lock->uri, "> (<",
+ item->lock->token, ">)", NULL);
+ }
+ sbuffer_zappend(req, EOL);
+ }
+}
+
+static void destroy(void *priv)
+{
+ struct request_locks *rl = priv;
+ struct submit_locks *lock, *next;
+
+ for (lock = rl->locks; lock != NULL; lock = next) {
+ next = lock->next;
+ free(lock);
+ }
+
+ free(rl);
+}
+
+dav_lock_session *dav_lock_register(http_session *sess)
+{
+ dav_lock_session *locksess = xcalloc(sizeof *locksess);
+
+ /* Register the hooks */
+ http_add_hooks(sess, &lock_hooks, locksess);
+
+ return locksess;
+}
+
+void dav_lock_unregister(dav_lock_session *sess)
+{
+ /* FIXME: free the lock list */
+ free(sess);
+}
+
+/* Submit the given lock for the given URI */
+static void submit_lock(struct request_locks *rl, struct dav_lock *lock,
+ const char *uri)
+{
+ struct submit_locks *slock;
+
+ /* Check for dups */
+ for (slock = rl->locks; slock != NULL; slock = slock->next) {
+ if (strcasecmp(slock->lock->token, lock->token) == 0)
+ return;
+ }
+
+ slock = xcalloc(sizeof *slock);
+ slock->lock = lock;
+ slock->uri = uri;
+ slock->next = rl->locks;
+ rl->locks = slock;
+}
+
+struct dav_lock *dav_lock_find(dav_lock_session *sess, const char *uri)
+{
+ struct dav_lock *cur;
+ for (cur = sess->locks; cur != NULL; cur = cur->next) {
+ if (uri_compare(uri, cur->uri) == 0)
+ return cur;
+ }
+ return NULL;
+}
+
+void dav_lock_using_parent(http_req *req, const char *uri)
+{
+ struct request_locks *rl = http_get_hook_private(req, HOOK_ID);
+ char *parent = uri_parent(uri);
+
+ if (parent != NULL) {
+ struct dav_lock *lock;
+ /* Find any locks on the parent resource.
+ * FIXME: should check for depth-infinity locks too. */
+ lock = dav_lock_find(rl->session, parent);
+ if (lock) {
+ DEBUG(DEBUG_LOCKS, "Locked parent, %s on %s\n", lock->token,
+ lock->uri);
+ submit_lock(rl, lock, uri);
+ }
+ free(parent);
+ }
+}
+
+int dav_lock_iterate(dav_lock_session *sess,
+ dav_lock_walkfunc func, void *userdata)
+{
+ struct dav_lock *lock;
+ int count = 0;
+
+ for (lock = sess->locks; lock != NULL; lock = lock->next) {
+ (*func)(lock, userdata);
+ count++;
+ }
+
+ return count;
+}
+
+void dav_lock_using_resource(http_req *req, const char *uri, int depth)
+{
+ /* Grab the private cookie for this request */
+ struct request_locks *rl = http_get_hook_private(req, HOOK_ID);
+ struct dav_lock *lock; /* all the known locks */
+ int match;
+
+ /* Iterate over the list of session locks to see if any of
+ * them apply to this resource */
+ for (lock = rl->session->locks; lock != NULL; lock = lock->next) {
+
+ match = 0;
+
+ if (depth == DAV_DEPTH_INFINITE && uri_childof(uri, lock->uri)) {
+ /* Case 1: this is a depth-infinity request which will
+ * modify a lock somewhere inside the collection. */
+ DEBUG(DEBUG_LOCKS, "Has child: %s\n", lock->token);
+ match = 1;
+ }
+ else if (uri_compare(uri, lock->uri) == 0) {
+ /* Case 2: this request is directly on a locked resource */
+ DEBUG(DEBUG_LOCKS, "Has direct lock: %s\n", lock->token);
+ match = 1;
+ }
+ else if (lock->depth == DAV_DEPTH_INFINITE &&
+ uri_childof(lock->uri, uri)) {
+ /* Case 3: there is a higher-up infinite-depth lock which
+ * covers the resource that this request will modify. */
+ DEBUG(DEBUG_LOCKS, "Is child of: %s\n", lock->token);
+ match = 1;
+ }
+
+ if (match) {
+ submit_lock(rl, lock, uri);
+ }
+ }
+
+}
+
+void dav_lock_add(dav_lock_session *sess, struct dav_lock *lock)
+{
+ if (sess->locks != NULL) {
+ sess->locks->prev = lock;
+ }
+ lock->prev = NULL;
+ lock->next = sess->locks;
+ sess->locks = lock;
+}
+
+void dav_lock_remove(dav_lock_session *sess, struct dav_lock *lock)
+{
+ if (lock->prev != NULL) {
+ lock->prev->next = lock->next;
+ } else {
+ sess->locks = lock->next;
+ }
+ if (lock->next != NULL) {
+ lock->next->prev = lock->prev;
+ }
+}
+
+void dav_lock_free(struct dav_lock *lock)
+{
+ HTTP_FREE(lock->uri);
+ HTTP_FREE(lock->owner);
+ HTTP_FREE(lock->token);
+ free(lock);
+}
+
+int dav_unlock(http_session *sess, struct dav_lock *lock)
+{
+ http_req *req = http_request_create(sess, "UNLOCK", lock->uri);
+ http_status status;
+ int ret;
+
+ http_print_request_header(req, "Lock-Token", "<%s>", lock->token);
+
+ /* TODO: need this or not?
+ * it definitely goes away when lock-null resources go away */
+ dav_lock_using_parent(req, lock->uri);
+
+ ret = http_request_dispatch(req, &status);
+
+ if (ret == HTTP_OK && status._class == 2) {
+ ret = HTTP_OK;
+ } else {
+ ret = HTTP_ERROR;
+ }
+
+ http_request_destroy(req);
+
+ return ret;
+}
+
+static int check_context(hip_xml_elmid parent, hip_xml_elmid child) {
+ DEBUG(DEBUG_XML, "dav_locks: check_context %d in %d\n", child, parent);
+ switch (parent) {
+ case HIP_ELM_root:
+ /* TODO: for LOCK requests only...
+ * shouldn't allow this for PROPFIND really */
+ if (child == DAV_ELM_prop)
+ return HIP_XML_VALID;
+ break;
+ case DAV_ELM_prop:
+ if (child == DAV_ELM_lockdiscovery)
+ return HIP_XML_VALID;
+ break;
+ case DAV_ELM_lockdiscovery:
+ if (child == DAV_ELM_activelock)
+ return HIP_XML_VALID;
+ break;
+ case DAV_ELM_activelock:
+ switch (child) {
+ case DAV_ELM_lockscope:
+ case DAV_ELM_locktype:
+ case DAV_ELM_depth:
+ case DAV_ELM_owner:
+ case DAV_ELM_timeout:
+ case DAV_ELM_locktoken:
+ return HIP_XML_VALID;
+ default:
+ break;
+ }
+ break;
+ case DAV_ELM_lockscope:
+ switch (child) {
+ case DAV_ELM_exclusive:
+ case DAV_ELM_shared:
+ return HIP_XML_VALID;
+ default:
+ break;
+ }
+ case DAV_ELM_locktype:
+ if (child == DAV_ELM_write)
+ return HIP_XML_VALID;
+ break;
+ /* ... depth is PCDATA, owner is COLLECT, timeout is PCDATA */
+ case DAV_ELM_locktoken:
+ if (child == DAV_ELM_href)
+ return HIP_XML_VALID;
+ break;
+ }
+ return HIP_XML_DECLINE;
+}
+
+static int parse_depth(const char *depth) {
+ if (strcasecmp(depth, "infinity") == 0) {
+ return DAV_DEPTH_INFINITE;
+ } else if (isdigit(depth[0])) {
+ return atoi(depth);
+ } else {
+ return -1;
+ }
+}
+
+static long parse_timeout(const char *timeout) {
+ if (strcasecmp(timeout, "infinite") == 0) {
+ return DAV_TIMEOUT_INFINITE;
+ } else if (strncasecmp(timeout, "Second-", 7) == 0) {
+ long to = strtol(timeout, NULL, 10);
+ if (to == LONG_MIN || to == LONG_MAX)
+ return DAV_TIMEOUT_INVALID;
+ return to;
+ } else {
+ return DAV_TIMEOUT_INVALID;
+ }
+}
+
+static void *start_resource(void *userdata, const char *href)
+{
+ struct dav_lock_result *res = xcalloc(sizeof *res);
+ res->href = xstrdup(href);
+ return res;
+}
+
+static void end_resource(void *userdata, void *response,
+ const char *status_line, const http_status *status,
+ const char *description)
+{
+ struct lock_discovery *ctx = userdata;
+ struct dav_lock_result *res = response;
+
+ if (response == NULL)
+ return;
+
+ res->next = ctx->list;
+ if (status_line != NULL && status != NULL) {
+ res->status_line = xstrdup(status_line);
+ res->status = *status;
+ res->status.reason_phrase = /* eeeeekkk.... HACKACKACKACK */
+ res->status_line + (status->reason_phrase - res->status_line);
+ }
+ ctx->list = res;
+ DEBUG(DEBUG_LOCKS, "End of response for %s\n", res->href);
+}
+
+static int end_element_common(struct dav_lock *l, const struct hip_xml_elm *elm,
+ const char *cdata)
+{
+ switch (elm->id){
+ case DAV_ELM_write:
+ l->type = dav_locktype_write;
+ break;
+ case DAV_ELM_exclusive:
+ l->scope = dav_lockscope_exclusive;
+ break;
+ case DAV_ELM_shared:
+ l->scope = dav_lockscope_shared;
+ break;
+ case DAV_ELM_depth:
+ DEBUG(DEBUG_LOCKS, "Got depth: %s\n", cdata);
+ l->depth = parse_depth(cdata);
+ if (l->depth == -1) {
+ return -1;
+ }
+ break;
+ case DAV_ELM_timeout:
+ DEBUG(DEBUG_LOCKS, "Got timeout: %s\n", cdata);
+ l->timeout = parse_timeout(cdata);
+ if (l->timeout == DAV_TIMEOUT_INVALID) {
+ return -1;
+ }
+ break;
+ case DAV_ELM_owner:
+ l->owner = strdup(cdata);
+ break;
+ case DAV_ELM_href:
+ l->token = strdup(cdata);
+ break;
+ }
+ return 0;
+}
+
+static int
+start_element_ldisc(void *userdata, const struct hip_xml_elm *elm,
+ const char **atts)
+{
+ struct dav_lock_result *l = dav_propfind_get_current_resource(userdata);
+ struct dav_lock *lck;
+
+ if (l == NULL)
+ return -1;
+
+ switch (elm->id) {
+ case DAV_ELM_activelock:
+ lck = xcalloc(sizeof *lck);
+ lck->next = l->lock;
+ if (l->lock != NULL)
+ l->lock->prev = l->lock;
+ l->lock = lck;
+ lck->uri = xstrdup(l->href);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* End-element handler for lock discovery PROPFIND response */
+static int
+end_element_ldisc(void *userdata, const struct hip_xml_elm *elm,
+ const char *cdata)
+{
+ struct dav_lock_result *l = dav_propfind_get_current_resource(userdata);
+
+ return end_element_common(l->lock, elm, cdata);
+}
+
+/* End-element handler for LOCK response */
+static int
+end_element_lock(void *userdata, const struct hip_xml_elm *elm,
+ const char *cdata)
+{
+ struct dav_lock *lock = userdata;
+ return end_element_common(lock, elm, cdata);
+}
+
+/* Discover all locks on URI */
+int dav_lock_discover(http_session *sess,
+ const char *uri, struct dav_lock_result **locks)
+{
+ dav_propfind_handler *handler = dav_propfind_create(sess, uri, 0);
+ struct lock_discovery ctx = {0};
+ const dav_propname props[] = {
+ { "DAV:", "lockdiscovery" },
+ { NULL }
+ };
+ int ret;
+
+ dav_propfind_set_resource_handlers(handler, start_resource, end_resource);
+ hip_xml_add_handler(dav_propfind_get_parser(handler), lock_elms,
+ check_context, start_element_ldisc, end_element_ldisc,
+ handler);
+
+ ret = dav_propfind_named(handler, props, &ctx);
+
+ if (ret == HTTP_OK) {
+ if (ctx.list != NULL) {
+ *locks = ctx.list;
+ ret = HTTP_OK;
+ } else {
+ http_set_error(sess, _("No locks found.\n"));
+ ret = HTTP_ERROR;
+ }
+ } else {
+ /* FIXME: free the list */
+ }
+
+ return ret;
+}
+
+int dav_lock(http_session *sess, struct dav_lock *lock)
+{
+ http_req *req = http_request_create(sess, "LOCK", lock->uri);
+ http_status status;
+ sbuffer body = sbuffer_create();
+ hip_xml_parser *parser = hip_xml_create();
+ int ret, parse_failed;
+ char *locktoken = NULL;
+
+ hip_xml_add_handler(parser, lock_elms, check_context,
+ NULL, end_element_lock, lock);
+
+ /* Create the body */
+ sbuffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<lockinfo xmlns='DAV:'>" EOL " <lockscope>",
+ lock->scope==dav_lockscope_exclusive?
+ "<exclusive/>":"<shared/>",
+ "</lockscope>" EOL
+ "<locktype><write/></locktype>", NULL);
+
+ if (lock->owner) {
+ sbuffer_concat(body, "<owner>", lock->owner, "</owner>" EOL, NULL);
+ }
+ sbuffer_zappend(body, "</lockinfo>" EOL);
+
+ http_set_request_body_buffer(req, sbuffer_data(body));
+ http_add_response_body_reader(req, http_accept_2xx,
+ hip_xml_parse_v, parser);
+ http_add_response_header_handler(req, "Lock-Token",
+ http_duplicate_header, &locktoken);
+ http_add_request_header(req, "Content-Type", "text/xml");
+ dav_add_depth_header(req, lock->depth);
+
+ /* TODO:
+ * By 2518, we need this only if we are creating a lock-null resource.
+ * Since we don't KNOW whether the lock we're given is a lock-null
+ * or not, we cover our bases.
+ */
+ dav_lock_using_parent(req, lock->uri);
+ /* This one is clearer from 2518 sec 8.10.4. */
+ dav_lock_using_resource(req, lock->uri, lock->depth);
+
+ ret = http_request_dispatch(req, &status);
+
+ sbuffer_destroy(body);
+ parse_failed = !hip_xml_valid(parser);
+
+ if (ret == HTTP_OK && status._class == 2) {
+ if (parse_failed) {
+ ret = HTTP_ERROR;
+ http_set_error(sess, hip_xml_get_error(parser));
+ }
+ else if (status.code == 207) {
+ ret = HTTP_ERROR;
+ /* TODO: set the error string appropriately */
+ }
+ else if (locktoken == NULL || strlen(locktoken) < 3) {
+ /* No valid Lock-Token header: IIS5 does this */
+ /* FIXME */
+ } else {
+ /* We have a locktoken header: strip of the angle brackets */
+ if (locktoken[0] == '<')
+ locktoken++;
+ if (locktoken[strlen(locktoken)-1] == '>')
+ locktoken[strlen(locktoken)-1] = '\0';
+
+ /* FIXME */
+ }
+ } else {
+ ret = HTTP_ERROR;
+ }
+
+ http_request_destroy(req);
+
+ /* TODO: free the list */
+ return ret;
+}
diff --git a/neon/src/dav_locks.h b/neon/src/dav_locks.h
new file mode 100644
index 000000000..3c2371746
--- /dev/null
+++ b/neon/src/dav_locks.h
@@ -0,0 +1,86 @@
+/*
+ WebDAV Class 2 locking operations
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DAV_LOCKS_H
+#define DAV_LOCKS_H
+
+enum dav_lock_scope {
+ dav_lockscope_exclusive,
+ dav_lockscope_shared
+};
+
+enum dav_lock_type {
+ dav_locktype_write
+};
+
+/* Would be typedef'ed to dav_lock except lock is a verb and a noun.
+ * Damn the English language. */
+struct dav_lock {
+ char *uri;
+ int depth;
+ enum dav_lock_type type;
+ enum dav_lock_scope scope;
+ char *token;
+ char *owner;
+ long timeout;
+ struct dav_lock *next;
+ struct dav_lock *prev;
+};
+
+struct dav_lock_result {
+ struct dav_lock *lock;
+ char *href;
+ http_status status;
+ char *status_line;
+ struct dav_lock_result *next;
+};
+
+#define DAV_TIMEOUT_INFINITE -1
+#define DAV_TIMEOUT_INVALID -2
+
+typedef struct dav_lock_session_s dav_lock_session;
+
+dav_lock_session *dav_lock_register(http_session *sess);
+void dav_lock_unregister(dav_lock_session *sess);
+
+void dav_lock_add(dav_lock_session *sess, struct dav_lock *lock);
+void dav_lock_remove(dav_lock_session *sess, struct dav_lock *lock);
+
+typedef void (*dav_lock_walkfunc)(struct dav_lock *lock, void *userdata);
+
+int dav_lock_iterate(dav_lock_session *sess,
+ dav_lock_walkfunc func, void *userdata);
+
+/* Indicate that this request is of depth n on given uri */
+void dav_lock_using_resource(http_req *req, const char *uri, int depth);
+/* Indicate that this request will modify parent collection of given URI */
+void dav_lock_using_parent(http_req *req, const char *uri);
+
+int dav_lock(http_session *sess, struct dav_lock *lock);
+int dav_lock_discover(http_session *sess,
+ const char *uri, struct dav_lock_result **locks);
+
+struct dav_lock *dav_lock_find(dav_lock_session *sess, const char *uri);
+int dav_unlock(http_session *sess, struct dav_lock *lock);
+
+void dav_lock_free(struct dav_lock *lock);
+
+#endif /* DAV_LOCKS_H */
diff --git a/neon/src/dav_props.c b/neon/src/dav_props.c
new file mode 100644
index 000000000..1b86a1204
--- /dev/null
+++ b/neon/src/dav_props.c
@@ -0,0 +1,192 @@
+/*
+ WebDAV Properties manipulation
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: dav_props.c,v 1.12 2000/05/09 22:36:55 joe Exp
+*/
+
+#include "config.h"
+
+#include "xalloc.h"
+#include "dav_props.h"
+#include "dav_basic.h"
+#include "hip_xml.h"
+
+struct dav_propfind_handler_s {
+ dav_pf_start_resource start_resource;
+ dav_pf_end_resource end_resource;
+ struct hip_xml_handler *handler;
+ http_session *sess;
+ const char *uri;
+ int depth;
+ char *href;
+ http_status status;
+ void *userdata, *resource;
+};
+
+void *dav_propfind_get_current_resource( dav_propfind_handler *handler )
+{
+ return handler->resource;
+}
+
+static void *start_response( void *userdata, const char *href )
+{
+ dav_propfind_handler *ctx = userdata;
+ ctx->resource = NULL;
+ if( ctx->start_resource ) {
+ ctx->resource = (*ctx->start_resource)( ctx->userdata, href );
+ }
+ return ctx->resource;
+}
+
+static void end_response( void *userdata, void *response,
+ const char *status_line, const http_status *status )
+{
+ dav_propfind_handler *ctx = userdata;
+ if( ctx->end_resource ) {
+ (*ctx->end_resource)( ctx->userdata, response, status_line, status );
+ }
+}
+
+dav_propfind_handler *
+dav_propfind_create( http_session *sess, const char *uri, int depth )
+{
+ dav_propfind_handler *ret = xmalloc(sizeof(dav_propfind_handler));
+ memset( ret, 0, sizeof(dav_propfind_handler));
+ ret->uri = uri;
+ ret->depth = depth;
+ ret->sess = sess;
+ return ret;
+}
+
+void dav_propfind_set_resource_handlers( dav_propfind_handler *handler,
+ dav_pf_start_resource start_res,
+ dav_pf_end_resource end_res )
+{
+ handler->start_resource = start_res;
+ handler->end_resource = end_res;
+}
+
+void dav_propfind_set_element_handler( dav_propfind_handler *handler,
+ struct hip_xml_handler *elm_handler )
+{
+ handler->handler = elm_handler;
+}
+
+static int propfind( dav_propfind_handler *handler, const dav_propname *names,
+ void *userdata )
+{
+ int ret, is_allprop = (names==NULL);
+ http_req *req = http_request_create( handler->sess, "PROPFIND", handler->uri );
+ http_status status;
+ dav_207_parser *parser;
+ sbuffer body, hdrs;
+
+ body = sbuffer_create();
+
+ sbuffer_concat( body,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<propfind xmlns=\"DAV:\">", NULL );
+
+ if( !is_allprop ) {
+ int n;
+ sbuffer_zappend( body, "<prop>" EOL );
+ for( n = 0; names[n].name != NULL; n++ ) {
+ sbuffer_concat( body, "<", names[n].name, " xmlns=\"",
+ names[n].nspace, "\"/>" EOL, NULL );
+ }
+ sbuffer_zappend( body, "</prop></propfind>" EOL );
+ } else {
+ sbuffer_zappend( body, "</allprop></propfind>" EOL );
+ }
+
+ parser = dav_207_init_with_handler( handler, handler->handler );
+ dav_207_set_response_handlers( parser, start_response, end_response );
+ handler->userdata = userdata;
+
+ http_set_request_body_buffer( req, sbuffer_data(body) );
+
+ hdrs = http_get_request_header( req );
+ sbuffer_zappend( hdrs, "Content-Type: text/xml" EOL ); /* TODO: UTF-8? */
+ dav_add_depth_header( req, handler->depth );
+
+ http_add_response_body_reader( req, dav_accept_207, dav_207_parse, parser );
+
+ ret = http_request_dispatch( req, &status );
+
+ http_request_destroy( req );
+
+ return ret;
+}
+
+int dav_propfind_allprop( dav_propfind_handler *handler, void *userdata )
+{
+ return propfind( handler, NULL, userdata );
+}
+
+int dav_propfind_named( dav_propfind_handler *handler,
+ const dav_propname *names, void *userdata )
+{
+ return propfind( handler, names, userdata );
+}
+
+
+/* The easy one... PROPPATCH */
+int dav_proppatch( http_session *sess, const char *uri,
+ const dav_proppatch_operation *items )
+{
+ http_req *req = http_request_create( sess, "PROPPATCH", uri );
+ sbuffer body = sbuffer_create(), hdrs;
+ int n, ret;
+
+ /* Create the request body */
+ sbuffer_zappend( body, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
+ "<propertyupdate xmlns=\"DAV:\">" );
+
+ for( n = 0; items[n].name != NULL; n++ ) {
+ switch( items[n].type ) {
+ case dav_propset:
+ /* <set><prop><prop-name>value</prop-name></prop></set> */
+ sbuffer_concat( body, "<set><prop>"
+ "<", items[n].name->name, " xmlns=\"",
+ items[n].name->nspace, "\">", items[n].value,
+ "</", items[n].name->name, "></prop></set>" EOL, NULL );
+ break;
+
+ case dav_propremove:
+ /* <remove><prop><prop-name/></prop></remove> */
+ sbuffer_concat( body, "<remove><prop><", items[n].name->name, " xmlns=\"",
+ items[n].name->nspace, "\"/></prop></remove>" EOL, NULL );
+ break;
+ }
+ }
+
+ sbuffer_zappend( body, "</propertyupdate>" EOL );
+
+ http_set_request_body_buffer( req, sbuffer_data(body) );
+
+ hdrs = http_get_request_header( req );
+ sbuffer_zappend( hdrs, "Content-Type: text/xml" EOL ); /* TODO: UTF-8? */
+
+ ret = dav_simple_request( sess, req );
+
+ sbuffer_destroy( body );
+
+ return ret;
+}
+
diff --git a/neon/src/dav_props.h b/neon/src/dav_props.h
new file mode 100644
index 000000000..9f0ac0fe2
--- /dev/null
+++ b/neon/src/dav_props.h
@@ -0,0 +1,89 @@
+/*
+ WebDAV Properties manipulation
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DAV_PROPS_H
+#define DAV_PROPS_H
+
+#include "http_request.h"
+#include "dav_207.h"
+
+/* PROPFIND results via three callbacks: */
+
+struct dav_propfind_handler_s;
+typedef struct dav_propfind_handler_s dav_propfind_handler;
+
+/* Callback types */
+typedef void *(*dav_pf_start_resource)( void *userdata, const char *href );
+typedef void (*dav_pf_got_property)( void *userdata, void *resource,
+ const dav_propname *name, const hip_xml_char **atts,
+ const char *value );
+typedef void (*dav_pf_end_resource)( void *userdata, void *resource,
+ const char *status_line, const http_status *status );
+
+/* Find properties with names in set *props on resource at URI
+ * in set *props, with given depth. If props == NULL all properties
+ * are returned.
+ * Returns:
+ * HTTP_OK on success.
+ * else other HTTP_* code
+ */
+dav_propfind_handler *
+dav_propfind_create( http_session *sess, const char *uri, int depth );
+
+void dav_propfind_register( dav_propfind_handler *handler,
+ dav_pf_start_resource start_res,
+ dav_pf_got_property got_prop,
+ dav_pf_end_resource end_res );
+
+void dav_propfind_set_resource_handlers( dav_propfind_handler *handler,
+ dav_pf_start_resource start_res,
+ dav_pf_end_resource end_res );
+
+void dav_propfind_set_element_handler( dav_propfind_handler *handler,
+ struct hip_xml_handler *elm_handler );
+
+int dav_propfind_allprop( dav_propfind_handler *handler, void *userdata );
+
+int dav_propfind_named( dav_propfind_handler *handler,
+ const dav_propname *names, void *userdata );
+
+/* A PROPPATCH request may include any number of operations. Pass an
+ * array of these operations to dav_proppatch, with the last item
+ * having the name element being NULL. If the type is propset, the
+ * property of the given name is set to the new value. If the type is
+ * propremove, the property of the given name is deleted, and the
+ * value is ignored. */
+typedef struct {
+ const dav_propname *name;
+ enum {
+ dav_propset,
+ dav_propremove
+ } type;
+ const char *value;
+} dav_proppatch_operation;
+
+/* FIXME: shouldn't need this. */
+void *dav_propfind_get_current_resource( dav_propfind_handler *handler );
+
+int dav_proppatch( http_session *sess, const char *uri,
+ const dav_proppatch_operation *items );
+
+#endif /* DAV_PROPS_H */
diff --git a/neon/src/hip_xml.c b/neon/src/hip_xml.c
new file mode 100644
index 000000000..1761504ce
--- /dev/null
+++ b/neon/src/hip_xml.c
@@ -0,0 +1,605 @@
+/*
+ Higher Level Interface to XML Parsers.
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: hip_xml.c,v 1.14 2000/05/09 22:33:54 joe Exp
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "neon_i18n.h"
+
+#include "xalloc.h"
+#include "http_utils.h"
+#include "string_utils.h"
+#include "hip_xml.h"
+
+static const char *friendly_name( const struct hip_xml_elm *elm )
+{
+ switch( elm->id ) {
+ case HIP_ELM_root:
+ return _("document root");
+ case HIP_ELM_unknown:
+ return _("unknown element");
+ default:
+ if( elm->name ) {
+ return elm->name;
+ } else {
+ return _("unspecified");
+ }
+ }
+}
+
+/* TODO:
+ * could move 'valid' into state, maybe allow optional
+ * continuation past an invalid branch.
+ */
+
+const static struct hip_xml_elm root_element =
+{ "@<root>@", HIP_ELM_root, 0 };
+
+/* The callback handlers */
+static void start_element( void *userdata, const hip_xml_char *name, const hip_xml_char **atts );
+static void end_element( void *userdata, const hip_xml_char *name );
+static void char_data( void *userdata, const hip_xml_char *cdata, int len );
+
+#ifdef HAVE_EXPAT
+/* libxml doesn't seem to pass stuff back in UTF-8.
+ * maybe libxml-2.0 will do. */
+#define HIP_XML_DECODE_UTF8
+#endif
+
+#ifdef HIP_XML_DECODE_UTF8
+
+/* UTF-8 decoding */
+
+/* Single byte range 0x00 -> 0x7F */
+#define SINGLEBYTE_UTF8( ch ) ( ((unsigned char) (ch)) < 0x80 )
+
+/* Decode a double byte UTF8 string.
+ * Returns 0 on success or non-zero on error. */
+static inline int decode_utf8_double( char *dest, const char *src );
+
+#endif
+
+/* Linked list of namespace scopes */
+struct hip_xml_nspace {
+ hip_xml_char *name;
+ hip_xml_char *uri;
+ struct hip_xml_nspace *next;
+};
+
+/* And an auxiliary */
+static int parse_element( struct hip_xml_parser *p, struct hip_xml_state *state,
+ const hip_xml_char *name, const hip_xml_char **atts );
+
+
+#ifdef HAVE_LIBXML
+
+/* Could be const as far as we care, but libxml doesn't want that */
+static xmlSAXHandler sax_handler = {
+ NULL, /* internalSubset */
+ NULL, /* isStandalone */
+ NULL, /* hasInternalSubset */
+ NULL, /* hasExternalSubset */
+ NULL, /* resolveEntity */
+ NULL, /* getEntity */
+ NULL, /* entityDecl */
+ NULL, /* notationDecl */
+ NULL, /* attributeDecl */
+ NULL, /* elementDecl */
+ NULL, /* unparsedEntityDecl */
+ NULL, /* setDocumentLocator */
+ NULL, /* startDocument */
+ NULL, /* endDocument */
+ start_element, /* startElement */
+ end_element, /* endElement */
+ NULL, /* reference */
+ char_data, /* characters */
+ NULL, /* ignorableWhitespace */
+ NULL, /* processingInstruction */
+ NULL, /* comment */
+ NULL, /* xmlParserWarning */
+ NULL, /* xmlParserError */
+ NULL, /* xmlParserError */
+ NULL, /* getParameterEntity */
+};
+
+#endif /* HAVE_LIBXML */
+
+#ifdef HIP_XML_DECODE_UTF8
+
+static inline int
+decode_utf8_double( char *dest, const char *src )
+{
+ /* From utf-8 man page; two-byte encoding is:
+ * 0x00000080 - 0x000007FF:
+ * 110xxxxx 10xxxxxx
+ * If more than 8-bits of those x's are set, we fail.
+ * So, we check that the first 6 bits of the first byte are:
+ * 110000.
+ * Then decode like:
+ * 110000xx 10yyyyyy -> xxyyyyyy
+ * Do this with a mask and a compare:
+ * zzzzzzzz
+ * & 11111100 <=> 0xFC
+ * == 11000000 <=> 0XC0
+ *
+ * joe: A real C hacker would probably do some funky bit
+ * inversion, and turn this into an is-not-zero test,
+ * but I'm a fake, so...
+ */
+ if( (src[0] & 0xFC) == 0xC0 ) {
+ dest[0] = ((src[0] & 0x03) << 6) | (src[1] & 0x3F);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+#endif
+
+int hip_xml_currentline( struct hip_xml_parser *p ) {
+#ifdef HAVE_EXPAT
+ return XML_GetCurrentLineNumber(p->parser);
+#else
+ return p->parser->input->line;
+#endif
+}
+
+static int find_handler( struct hip_xml_handler *list, struct hip_xml_state *state ) {
+ struct hip_xml_handler *cur, *unk_handler = NULL;
+ const struct hip_xml_elm *unk_elm = NULL;
+ int n;
+ for( cur = list; cur != NULL; cur = cur->next ) {
+ for( n = 0; cur->elements[n].nspace != NULL; n++ ) {
+ if( strcasecmp( cur->elements[n].name, state->name ) == 0 &&
+ strcasecmp( cur->elements[n].nspace, state->nspace ) == 0 ) {
+ state->handler = cur;
+ state->elm = &cur->elements[n];
+ return 0;
+ }
+ if( !unk_elm && cur->elements[n].id == HIP_ELM_unknown ) {
+ unk_elm = &cur->elements[n];
+ unk_handler = cur;
+ }
+ }
+ }
+ if( !cur && unk_elm ) {
+ /* Give them the unknown handler */
+ state->handler = unk_handler;
+ state->elm = unk_elm;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+char *hip_xml_escape( const char *text )
+{
+#if 0
+ sbuffer buf = sbuffer_create();
+ const char *pnt;
+ if( !buf ) return NULL;
+ /* FIXME: implement */
+#endif
+ return NULL;
+}
+
+/* Called with the start of a new element. */
+static void
+start_element( void *userdata, const hip_xml_char *name, const hip_xml_char **atts )
+{
+ struct hip_xml_parser *p = (struct hip_xml_parser *)userdata;
+ struct hip_xml_state *s;
+
+ if( !p->valid ) {
+ /* We've stopped parsing */
+ DEBUG( DEBUG_XML, "Parse died. Ignoring start of element: %s\n", name );
+ return;
+ }
+ if( p->collect ) {
+ /* In Collect Mode. */
+ sbuffer_concat( p->buffer, "<", name, NULL );
+ if( atts != NULL ) {
+ int n;
+ for( n = 0; atts[n] != NULL; n+=2 ) {
+ sbuffer_concat( p->buffer, " ", atts[n], "=", atts[n+1],
+ NULL );
+ }
+ }
+ sbuffer_zappend( p->buffer, ">" );
+ /* One deeper */
+ p->collect++;
+ return;
+ }
+ /* Set the new state */
+ s = xmalloc( sizeof(struct hip_xml_state) );
+ memset( s, 0, sizeof(struct hip_xml_state) );
+ s->parent = p->current;
+ p->current = s;
+ /* We need to handle namespaces ourselves */
+ if( parse_element( p, s, name, atts ) ) {
+ /* it bombed. */
+ p->valid = 0;
+ return;
+ }
+ /* Map the element name to an id */
+ DEBUG( DEBUG_XMLPARSE, "Mapping element name %s@@%s... ", s->nspace, s->name );
+ if( find_handler( p->handlers, s ) ) {
+ DEBUG( DEBUG_XMLPARSE, "Unexpected element\n" );
+ snprintf( p->error, BUFSIZ, "Unknown XML element `%s'", s->name );
+ p->valid = 0;
+ return;
+ }
+
+ DEBUG( DEBUG_XMLPARSE, "mapped to id %d\n", s->elm->id );
+
+ /* Do we want cdata? */
+ p->want_cdata = ((p->current->elm->flags & HIP_XML_CDATA)
+ == HIP_XML_CDATA);
+ p->collect = ((p->current->elm->flags & HIP_XML_COLLECT)
+ == HIP_XML_COLLECT);
+
+ /* expat is not a validating parser - check the new element
+ * is valid in the current context.
+ */
+ DEBUG( DEBUG_XML, "Checking context of %s@@%s: element %s (parent: %s)\n",
+ s->nspace, s->name, friendly_name(s->elm), friendly_name(s->parent->elm) );
+ if( (*s->handler->validate_cb)( s->parent->elm->id, s->elm->id ) ) {
+ DEBUG( DEBUG_XML, "Invalid context.\n" );
+ snprintf( p->error, BUFSIZ,
+ _("XML is not valid (%s found in parent %s)"),
+ friendly_name(s->elm), friendly_name(s->parent->elm) );
+ p->valid = 0;
+ } else {
+ DEBUG( DEBUG_XML, "Valid context.\n" );
+ if( s->handler->startelm_cb ) {
+ if( (*s->handler->startelm_cb)( s->handler->userdata, s, atts ) ) {
+ DEBUG( DEBUG_XML, "Startelm callback failed.\n" );
+ p->valid = 0;
+ }
+ } else {
+ DEBUG( DEBUG_XML, "No startelm handler.\n" );
+ }
+ }
+
+}
+
+/* Destroys given state */
+static void destroy_state( struct hip_xml_state *s ) {
+ struct hip_xml_nspace *this_ns, *next_ns;
+ DEBUG( DEBUG_XMLPARSE, "Freeing namespaces...\n" );
+ HTTP_FREE( s->default_ns );
+ HTTP_FREE( s->name );
+ /* Free the namespaces */
+ this_ns = s->nspaces;
+ while( this_ns != NULL ) {
+ next_ns = this_ns->next;
+ free( this_ns->name );
+ free( this_ns->uri );
+ free( this_ns );
+ this_ns = next_ns;
+ };
+ DEBUG( DEBUG_XMLPARSE, "Finished freeing namespaces.\n" );
+ free( s );
+}
+
+static void char_data( void *userdata, const hip_xml_char *data, int len ) {
+ struct hip_xml_parser *p = userdata;
+
+ if( !p->want_cdata || !p->valid ) return;
+ /* First, if this is the beginning of the CDATA, skip all
+ * leading whitespace, we don't want it. */
+ DEBUG( DEBUG_XMLPARSE, "Given %d bytes of cdata.\n", len );
+ if( sbuffer_size(p->buffer) == 0 ) {
+ size_t wslen = 0;
+ /* Ignore any leading whitespace */
+ while( wslen < len &&
+ ( data[wslen] == ' ' || data[wslen] == '\r' ||
+ data[wslen] == '\n' || data[wslen] == '\t' ) ) {
+ wslen++;
+ }
+ data += wslen;
+ len -= wslen;
+ DEBUG( DEBUG_XMLPARSE, "Skipped %d bytes of leading whitespace.\n",
+ wslen );
+ if( len == 0 ) {
+ DEBUG( DEBUG_XMLPARSE, "Zero bytes of content.\n" );
+ return;
+ }
+ }
+
+#ifdef HIP_XML_DECODE_UTF8
+
+ if( (p->current->elm->flags & HIP_XML_UTF8DECODE) == HIP_XML_UTF8DECODE ) {
+ int n, m, clen;
+ char *dest;
+
+ clen = sbuffer_size(p->buffer);
+ sbuffer_grow( p->buffer, clen + len + 1 );
+ dest = sbuffer_data( p->buffer ) + clen;
+
+ for( n = 0, m = 0; n < len; n++, m++ ) {
+ if( SINGLEBYTE_UTF8( data[n] ) ) {
+ dest[m] = data[n];
+ } else {
+ /* An optimisation here: we only deal with 8-bit
+ * data, which will be encoded as two bytes of UTF-8 */
+ if( (len - n < 2) ||
+ decode_utf8_double( &dest[m], &data[n] ) ) {
+ /* Failed to decode! */
+ DEBUG( DEBUG_XML, "Could not decode UTF-8 data.\n" );
+ strcpy( p->error, "XML parser received non-8-bit data" );
+ p->valid = 0;
+ return;
+ } else {
+#if 0
+ DEBUG( DEBUG_XML, "UTF-8 two-bytes decode: "
+ "0x%02hx 0x%02hx -> 0x%02hx!\n",
+ data[n] & 0xFF, data[n+1] & 0xFF, dest[m] & 0xFF );
+#endif
+ /* Skip the second byte */
+ n += 2;
+ }
+ }
+ }
+ sbuffer_altered( p->buffer );
+ } else {
+ sbuffer_append( p->buffer, data, len );
+ }
+
+#else /* !HIP_XML_DECODE_UTF8 */
+
+ sbuffer_append( p->buffer, data, len );
+
+#endif
+
+}
+
+/* Called with the end of an element */
+static void end_element( void *userdata, const hip_xml_char *name ) {
+ struct hip_xml_parser *p = userdata;
+ struct hip_xml_state *s = p->current;
+ if( !p->valid ) {
+ /* We've stopped parsing */
+ DEBUG( DEBUG_XML, "Parse died. Ignoring end of element: %s\n", name );
+ return;
+ }
+ if( p->collect > 0 ) {
+ if( --p->collect ) {
+ sbuffer_concat( p->buffer, "</", name, ">", NULL );
+ return;
+ }
+ }
+
+ /* process it */
+ if( s->handler->endelm_cb ) {
+ DEBUG( DEBUG_XMLPARSE, "Calling endelm callback for %s.\n", s->elm->name );
+ if( (*s->handler->endelm_cb)( s->handler->userdata, s,
+ p->want_cdata?sbuffer_data(p->buffer):
+ NULL ) ) {
+ DEBUG( DEBUG_XML, "Endelm callback failed.\n" );
+ p->valid = 0;
+ }
+ }
+ p->current = s->parent;
+ /* Move the current pointer up the branch */
+ DEBUG( DEBUG_XML, "Back in element: %s\n", friendly_name(p->current->elm) );
+ if( p->want_cdata ) {
+ sbuffer_clear( p->buffer );
+ }
+ destroy_state( s );
+}
+
+/* Parses the attributes, and handles XML namespaces.
+ * With a little bit of luck.
+ * Returns:
+ * the element name on success
+ * or NULL on error.
+ */
+static int parse_element( struct hip_xml_parser *p, struct hip_xml_state *state,
+ const hip_xml_char *name, const hip_xml_char **atts )
+{
+ struct hip_xml_nspace *ns;
+ const hip_xml_char *pnt;
+ struct hip_xml_state *xmlt;
+
+ DEBUG( DEBUG_XMLPARSE, "Parsing elm of name: [%s]\n", name );
+ /* Parse the atts for namespace declarations... if we have any atts.
+ * expat will never pass us atts == NULL, but libxml will. */
+ if( atts != NULL ) {
+ int attn;
+ for( attn = 0; atts[attn]!=NULL; attn+=2 ) {
+ DEBUG( DEBUG_XMLPARSE, "Got attribute: [%s] = [%s]\n", atts[attn], atts[attn+1] );
+ if( strcasecmp( atts[attn], "xmlns" ) == 0 ) {
+ /* New default namespace */
+ state->default_ns = xstrdup( atts[attn+1] );
+ DEBUG( DEBUG_XMLPARSE, "New default namespace: %s\n",
+ state->default_ns );
+ } else if( strncasecmp( atts[attn], "xmlns:", 6 ) == 0 ) {
+ /* New namespace scope */
+ ns = xmalloc( sizeof( struct hip_xml_nspace ) );
+ ns->next = state->nspaces;
+ state->nspaces = ns;
+ ns->name = xstrdup( atts[attn]+6 ); /* skip the xmlns= */
+ ns->uri = xstrdup( atts[attn+1] );
+ DEBUG( DEBUG_XMLPARSE, "New namespace scope: %s -> %s\n",
+ ns->name, ns->uri );
+ }
+ }
+ }
+ /* Now check the elm name for a namespace scope */
+ pnt = strchr( name, ':' );
+ if( pnt == NULL ) {
+ /* No namespace prefix - have we got a default? */
+ state->name = xstrdup(name);
+ DEBUG( DEBUG_XMLPARSE, "No prefix found, searching for default.\n" );
+ for( xmlt = state; xmlt!=NULL; xmlt=xmlt->parent ) {
+ if( xmlt->default_ns != NULL ) {
+ state->nspace = xmlt->default_ns;
+ break;
+ }
+ }
+ if( state->nspace == NULL ) {
+ DEBUG( DEBUG_XMLPARSE, "No default namespace, using empty.\n" );
+ state->nspace = "";
+ }
+ } else {
+ DEBUG( DEBUG_XMLPARSE, "Got namespace scope. Trying to resolve..." );
+ /* Have a scope - resolve it */
+ for( xmlt = state; state->nspace==NULL && xmlt!=NULL; xmlt=xmlt->parent ) {
+ for( ns = xmlt->nspaces; ns!=NULL; ns=ns->next ) {
+ /* Just compare against the bit before the :
+ * pnt points to the colon. */
+ if( strncasecmp( ns->name, name, pnt-name ) == 0 ) {
+ /* Scope matched! Hoorah */
+ state->nspace = ns->uri;
+ /* end the search */
+ break;
+ }
+ }
+ }
+ if( state->nspace != NULL ) {
+ DEBUG( DEBUG_XMLPARSE, "Resolved prefix to [%s]\n", state->nspace );
+ /* The name is everything after the ':' */
+ if( pnt[1] == '\0' ) {
+ snprintf( p->error, BUFSIZ,
+ "Element name missing in '%s' at line %d.",
+ name, hip_xml_currentline(p) );
+ DEBUG( DEBUG_XMLPARSE, "No element name after ':'. Failed.\n" );
+ return -1;
+ }
+ state->name = xstrdup(pnt+1);
+ } else {
+ DEBUG( DEBUG_XMLPARSE, "Undeclared namespace.\n" );
+ snprintf( p->error, BUFSIZ,
+ "Undeclared namespace in '%s' at line %d.",
+ name, hip_xml_currentline(p) );
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void hip_xml_init( struct hip_xml_parser *p, struct hip_xml_handler *handlers )
+{
+ /* Initialize the expat stuff */
+ memset( p, 0, sizeof( struct hip_xml_parser ) );
+ /* Initialize other stuff */
+ p->valid = 1;
+ /* Placeholder for the root element */
+ p->current = p->root = xmalloc( sizeof(struct hip_xml_state) );
+ memset( p->root, 0, sizeof(struct hip_xml_state) );
+ p->root->elm = &root_element;
+ p->handlers = handlers;
+ /* Initialize the cdata buffer */
+ p->buffer = sbuffer_create();
+#ifdef HAVE_EXPAT
+ p->parser = XML_ParserCreate( NULL );
+ if( p->parser == NULL ) {
+ abort();
+ }
+ XML_SetElementHandler( p->parser, start_element, end_element );
+ XML_SetCharacterDataHandler( p->parser, char_data );
+ XML_SetUserData( p->parser, (void *) p );
+#else
+ p->parser = xmlCreatePushParserCtxt( &sax_handler, (void *)p, NULL, 0, NULL );
+ if( p->parser == NULL ) {
+ abort();
+ }
+#endif
+}
+
+void hip_xml_parse_v( void *userdata, const char *block, size_t len )
+{
+ struct hip_xml_parser *p = userdata;
+ /* FIXME: The two XML parsers break all our nice abstraction by
+ * choosing different char *'s. The swine. This may kill us some
+ * day. */
+ hip_xml_parse( p, (const hip_xml_char *) block, len );
+}
+
+/* Parse the given block of input of length len */
+void hip_xml_parse( struct hip_xml_parser *p, const hip_xml_char *block, size_t len )
+{
+ int ret, flag;
+ /* duck out if it's broken */
+ if( !p->valid ) {
+ DEBUG( DEBUG_XML, "Not parsing %d bytes.\n", len );
+ return;
+ }
+ if( len == 0 ) {
+ flag = -1;
+ block = "";
+ DEBUG( DEBUG_XML, "Got 0-length buffer, end of document.\n" );
+ } else {
+ DEBUG( DEBUG_XML, "Parsing %d length buffer.\n", len );
+ flag = 0;
+ }
+ /* Note, don't write a parser error if !p->valid, since an error
+ * will already have been written in that case. */
+#ifdef HAVE_EXPAT
+ ret = XML_Parse( p->parser, block, len, flag );
+ DEBUG( DEBUG_XMLPARSE, "XML_Parse returned %d\n", ret );
+ if( ret == 0 && p->valid ) {
+ snprintf( p->error, BUFSIZ,
+ "XML parse error at line %d: %s",
+ XML_GetCurrentLineNumber(p->parser),
+ XML_ErrorString(XML_GetErrorCode(p->parser)) );
+ p->valid = 0;
+ }
+#else
+ ret = xmlParseChunk( p->parser, block, len, flag );
+ DEBUG( DEBUG_XMLPARSE, "xmlParseChunk returned %d\n", ret );
+ if( p->parser->errNo && p->valid ) {
+ /* FIXME: error handling */
+ snprintf( p->error, BUFSIZ, "XML parse error at line %d.",
+ hip_xml_currentline(p) );
+ p->valid = 0;
+ }
+#endif
+
+}
+
+int hip_xml_finish( struct hip_xml_parser *p )
+{
+ struct hip_xml_state *s, *parent;
+ sbuffer_destroy( p->buffer );
+ /* Clean up any states which may remain.
+ * If p.valid, then this should be only the root element. */
+ for( s = p->current; s!=NULL; s=parent ) {
+ parent = s->parent;
+ destroy_state( s );
+ }
+#ifdef HAVE_EXPAT
+ XML_ParserFree( p->parser );
+#else
+ xmlFreeParserCtxt( p->parser );
+#endif
+ return !p->valid;
+}
+
diff --git a/neon/src/hip_xml.h b/neon/src/hip_xml.h
new file mode 100644
index 000000000..eeadff637
--- /dev/null
+++ b/neon/src/hip_xml.h
@@ -0,0 +1,254 @@
+/*
+ Higher Level Interface to XML Parsers.
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef HIP_XML_H
+#define HIP_XML_H
+
+/*
+ TODO:
+
+ There is a whopping great whole in this design. It doesn't
+ correctly handle XML with elements interspersed with CDATA, e.g.
+ <foo>this is cdata<bar>still is</bar>still cdata here</foo>
+ To handle this, we probably need an extra flag, HIP_XML_MIXED,
+ and a cdata callback. Then, on start_element, check if we're in
+ mixed mode, and call the cdata callback with everything-up-to-now,
+ if we are, then empty the cdata buffer. Note that even with
+ this flaw the code is useful, since DAV XML responses don't
+ use this kind of XML.
+
+ To Consider:
+
+ The sitecopy XML parsing code started off as a fairly nasty
+ hack. This, then, is the abstraction of that hack.
+
+ Need to evaluate whether simply building the entire XML tree
+ up is actually better.
+
+ My intuition says it's not, but, this is still quite a laborious
+ method of handling XML.
+
+*/
+
+#include <config.h>
+
+/* for BUFSIZ */
+#include <stdio.h>
+
+/* for sbuffer */
+#include "string_utils.h"
+
+#ifdef HAVE_EXPAT
+/******** Expat ***********/
+# include <xmlparse.h>
+typedef XML_Char hip_xml_char;
+
+#else /* not HAVE_EXPAT */
+# ifdef HAVE_LIBXML
+/******** libxml **********/
+# include <parser.h>
+typedef xmlChar hip_xml_char;
+
+# else /* not HAVE_LIBXML */
+# error need an XML parser
+# endif /* not HAVE_LIBXML */
+#endif /* not HAVE_EXPAT */
+
+/* Generic XML response handling...
+
+ Basic principle is that you provide these things:
+ 1) a list of elements which you wish to handle
+ 2) a context checking function which will validate whether a
+ given element is valid in the given parent.
+ 3) a callback function which is called when a complete element
+ has been parsed.
+
+ This code then deals with the boring stuff like:
+ - Dealing with XML namespaces
+ - Collecting CDATA
+
+ The element list is stored in a 'struct hip_xml_elm' array.
+ For each element, you specify the element name, an element id,
+ and some flags for that element: whether it can have children,
+ and whether it can have cdata.
+
+ Lists of element lists can be chained together, so the elements
+ from another module don't have to be defined twice.
+
+ Say we have XML docs like:
+
+ <foo attr="yes">
+ <bar>norman</bar>
+ <bee>yesterday</bee>
+ <bar>fishing</bar>
+ </foo>
+
+ and we have one module, which handles <foo> elements, and
+ another module, which handles bar+bee elements.
+
+ The element lists are:
+
+const static struct hip_xml_elm barbee_elms[] = {
+ {"bar", HIP_ELM_bar, HIP_XML_CDATA },
+ {"bee", HIP_ELM_bee, HIP_XML_CDATA },
+ { NULL }
+};
+
+ Note that foo doesn't take CDATA:
+
+const static struct hip_xml_elm foo_elms[] = {
+ {"foo", HIP_ELM_foo, 0 },
+ { NULL }
+};
+
+ The context validation functions are:
+
+ int check_barbee_context( parent, child ) {
+ return ((child == HIP_ELM_bar ||
+ child == HIP_ELM_bee) && parent == HIP_ELM_foo);
+ }
+
+ int check_foo_context( a, parent, child ) {
+ return (child == HIP_ELM_foo && parent = HIP_ELM_root);
+ }
+
+ The list-of-lists are declared:
+
+struct hip_xml_elmlist listfoo = { foo_elms, NULL },
+ listbarbee = { barbee_elms, &listfoo };
+
+ Note that listbarbee chains listfoo on the end.
+
+*/
+
+/* Reserved element id's */
+#define HIP_ELM_unknown -1
+#define HIP_ELM_root 0
+
+typedef int hip_xml_elmid;
+
+struct hip_xml_state;
+struct hip_xml_elm;
+struct hip_xml_handler;
+
+/* An element */
+struct hip_xml_elm {
+ const char *nspace, *name;
+ hip_xml_elmid id;
+ unsigned int flags;
+};
+
+/* Function to check element context... returns 0 if child is a valid
+ * child tag of parent, else non-zero. */
+typedef int (*hip_xml_validate_cb)
+ ( hip_xml_elmid child, hip_xml_elmid parent );
+
+typedef int (*hip_xml_startelm_cb)
+ ( void *userdata, const struct hip_xml_state *s, const hip_xml_char **atts );
+
+/* Called when a complete element is parsed */
+typedef int (*hip_xml_endelm_cb)
+ ( void *userdata, const struct hip_xml_state *s, const char *cdata );
+
+/* A list of elements */
+struct hip_xml_handler {
+ const struct hip_xml_elm *elements; /* put it in static memory */
+ hip_xml_validate_cb validate_cb; /* validation function */
+ hip_xml_startelm_cb startelm_cb; /* on-complete element function */
+ hip_xml_endelm_cb endelm_cb; /* on-complete element function */
+ void *userdata;
+ struct hip_xml_handler *next;
+};
+
+struct hip_xml_state {
+ /* The element details */
+ const struct hip_xml_elm *elm;
+ hip_xml_char *name;
+ const hip_xml_char *nspace;
+ /* Namespaces declared in this element */
+ hip_xml_char *default_ns; /* A default namespace */
+ struct hip_xml_nspace *nspaces; /* List of other namespace scopes */
+ /* Extras */
+ struct hip_xml_handler *handler; /* Where the element was declared */
+ struct hip_xml_state *parent; /* The parent in the tree */
+};
+
+/* We pass around a hip_xml_parser as the userdata in the parsing
+ * library. This maintains the current state of the parse and various
+ * other bits and bobs. Within the parse, we store the current branch
+ * of the tree, i.e., the current element and all its parents, up to
+ * the root, but nothing other than that. */
+struct hip_xml_parser {
+ struct hip_xml_state *root; /* the root of the document */
+ struct hip_xml_state *current; /* current element in the branch */
+ sbuffer buffer; /* the CDATA/collect buffer */
+ unsigned int valid:1; /* currently valid? */
+ unsigned int want_cdata:1; /* currently collecting CDATA? */
+ unsigned int collect; /* currently collecting all children? */
+ struct hip_xml_handler *handlers; /* list of handlers */
+#ifdef HAVE_EXPAT
+ XML_Parser parser;
+#else
+ xmlParserCtxtPtr parser;
+#endif
+ char error[BUFSIZ];
+};
+
+/* Flags */
+/* This element has no children */
+#define HIP_XML_CDATA (1<<1)
+/* Collect complete contents of this node as cdata */
+#define HIP_XML_COLLECT ((1<<2) | HIP_XML_CDATA)
+/* Decode UTF-8 data in cdata. */
+#define HIP_XML_UTF8DECODE (1<<3)
+
+/* Initialise the parser p, with the list of elements we accept,
+ * the context checking function, the element callback, and the userdata
+ * for the element callback.
+ */
+void hip_xml_init( struct hip_xml_parser *p, struct hip_xml_handler *elms );
+
+/* Cleans up the parser's internal structures allocated by hip_xml_init.
+ * Returns:
+ * 0 if the parse was successful
+ * non-zero if the parse failed.
+ */
+int hip_xml_finish( struct hip_xml_parser *p );
+
+/* Parse the given block of input of length len. Block does
+ * not need to be NULL-terminated. Note that signed-ness of
+ * the block characters depends upon whether libxml or expat
+ * is being used as the underlying parser. */
+void hip_xml_parse( struct hip_xml_parser *p,
+ const hip_xml_char *block, size_t len );
+
+/* As above taking a void * userdata, and a const char * data block,
+ * both of which are casted appropriately and passed to
+ * hip_xml_parse. */
+void hip_xml_parse_v( void *userdata, const char *block, size_t len );
+
+/* Return current parse line for errors */
+int hip_xml_currentline( struct hip_xml_parser *p );
+
+/* Returns XML-escaped text, allocated with malloc() */
+char *hip_xml_escape( const char *text );
+
+#endif /* HIP_XML_H */
diff --git a/neon/src/http_auth.c b/neon/src/http_auth.c
new file mode 100644
index 000000000..37eb218e9
--- /dev/null
+++ b/neon/src/http_auth.c
@@ -0,0 +1,887 @@
+/*
+ HTTP Authentication routines
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: http_auth.c,v 1.8 2000/05/09 18:33:37 joe Exp
+*/
+
+
+/* HTTP Authentication, as per RFC2617.
+ *
+ * TODO:
+ * - Improve cnonce? Make it really random using /dev/random or whatever?
+ * - Test auth-int support
+ */
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+
+#include "base64.h"
+#include "md5.h"
+#include "dates.h"
+
+#include "http_auth.h"
+#include "string_utils.h"
+#include "uri.h"
+#include "http_utils.h"
+
+/* The MD5 digest of a zero-length entity-body */
+#define DIGEST_MD5_EMPTY "d41d8cd98f00b204e9800998ecf8427e"
+
+/* A challenge */
+struct http_auth_chall {
+ http_auth_scheme scheme;
+ char *realm;
+ char *domain;
+ char *nonce;
+ char *opaque;
+ unsigned int stale:1; /* if stale=true */
+ unsigned int got_qop:1; /* we were given a qop directive */
+ unsigned int qop_auth:1; /* "auth" token in qop attrib */
+ unsigned int qop_auth_int:1; /* "auth-int" token in qop attrib */
+ http_auth_algorithm alg;
+ struct http_auth_chall *next;
+};
+
+static const char *qop_values[] = {
+ NULL,
+ "auth",
+ "auth-int"
+};
+static const char *algorithm_names[] = {
+ "MD5",
+ "MD5-sess",
+ NULL
+};
+
+/* Private prototypes */
+static char *get_cnonce(void);
+static void clean_session( http_auth_session *sess );
+static int digest_challenge( http_auth_session *, struct http_auth_chall * );
+static int basic_challenge( http_auth_session *, struct http_auth_chall * );
+static char *request_digest( http_auth_session * );
+static char *request_basic( http_auth_session * );
+
+/* Domain handling */
+static int is_in_domain( http_auth_session *sess, const char *uri );
+static int parse_domain( http_auth_session *sess, const char *domain );
+
+/* Get the credentials, passing a temporary store for the password value */
+static int get_credentials( http_auth_session *sess, char **password );
+
+/* Initialize an auth session */
+void http_auth_init( http_auth_session *sess )
+{
+ memset( sess, 0, sizeof( http_auth_session ) );
+}
+
+http_auth_session *http_auth_create( void )
+{
+ http_auth_session *sess = xmalloc(sizeof(http_auth_session));
+ http_auth_init( sess );
+ return sess;
+}
+
+void http_auth_destroy( http_auth_session *sess )
+{
+ http_auth_finish( sess );
+ free( sess );
+}
+
+void http_auth_set_creds_cb( http_auth_session *sess,
+ http_auth_request_creds callback, void *userdata )
+{
+ sess->reqcreds = callback;
+ sess->reqcreds_udata = userdata;
+}
+
+#if 0
+void http_auth_set_server( http_auth_session *sess,
+ const char *host, unsigned int port, const char *scheme )
+{
+ sess->host = host;
+ sess->port = port;
+ sess->req_scheme = scheme;
+}
+#endif
+
+/* Start a new request. */
+void http_auth_new_request( http_auth_session *sess,
+ const char *method, const char *uri,
+ const char *body_buffer, FILE *body_stream )
+{
+ if( !sess->can_handle ) {
+ DEBUG( DEBUG_HTTPAUTH, "Not handling session.\n" );
+ } else if( !is_in_domain( sess, uri ) ) {
+ /* We have moved out of the authentication domain */
+ DEBUG( DEBUG_HTTPAUTH, "URI %s outside session domain, not handling.\n", uri );
+ sess->will_handle = 0;
+ } else
+
+ {
+ DEBUG( DEBUG_HTTPAUTH, "URI %s inside session domain, will handle.\n", uri );
+
+ sess->will_handle = 1;
+ sess->uri = uri;
+ sess->method = method;
+ sess->got_body = (body_buffer!=NULL) || (body_stream!=NULL);
+ sess->body_buffer = body_buffer;
+ sess->body_stream = body_stream;
+ md5_init_ctx( &sess->response_body );
+ }
+}
+
+static void clean_session( http_auth_session *sess )
+{
+ sess->can_handle = 0;
+ HTTP_FREE( sess->basic );
+ HTTP_FREE( sess->unq_realm );
+ HTTP_FREE( sess->unq_nonce );
+ HTTP_FREE( sess->unq_cnonce );
+ HTTP_FREE( sess->opaque );
+ HTTP_FREE( sess->username );
+ if( sess->domain_count > 0 ) {
+ split_string_free( sess->domain );
+ sess->domain_count = 0;
+ }
+}
+
+void http_auth_finish( http_auth_session *sess ) {
+ clean_session( sess );
+}
+
+/* Returns cnonce-value. We just use base64( time ).
+ * TODO: Could improve this? */
+static char *get_cnonce(void)
+{
+ char *ret, *tmp;
+ tmp = rfc1123_date( time(NULL) );
+ ret = base64( tmp );
+ free( tmp );
+ return ret;
+}
+
+static int
+get_credentials( http_auth_session *sess, char **password)
+{
+ return (*sess->reqcreds)( sess->reqcreds_udata, sess->unq_realm,
+ &sess->username, password );
+}
+
+static int parse_domain( http_auth_session *sess, const char *domain ) {
+ char *unq, **doms;
+ int count, ret;
+
+ unq = shave_string(domain, '"' );
+ doms = split_string_c( unq, ' ', NULL, HTTP_WHITESPACE, &count );
+ if( doms != NULL ) {
+ if( count > 0 ) {
+ sess->domain = doms;
+ sess->domain_count = count;
+ ret = 0;
+ } else {
+ free( doms );
+ ret = -1;
+ }
+ } else {
+ ret = -1;
+ }
+ free( unq );
+ return ret;
+}
+
+/* Returns:
+ * 0: if uri is in NOT in domain of session
+ * else: uri IS in domain of session (or no domain known)
+ */
+static int is_in_domain( http_auth_session *sess, const char *uri )
+{
+#if 1
+ return 1;
+#else
+ /* TODO: Need proper URI comparison for this to work. */
+ int ret, dom;
+ const char *abs_path;
+ if( sess->domain_count == 0 ) {
+ DEBUG( DEBUG_HTTPAUTH, "No domain, presuming okay.\n" );
+ return 1;
+ }
+ ret = 0;
+ abs_path = uri_abspath( uri );
+ for( dom = 0; dom < sess->domain_count; dom++ ) {
+ DEBUG( DEBUG_HTTPAUTH, "Checking domain: %s\n", sess->domain[dom] );
+ if( uri_childof( sess->domain[dom], abs_path ) ) {
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+#endif
+}
+
+/* Add authentication creditials to a request */
+char *http_auth_request_header( http_auth_session *sess )
+{
+ if( sess->will_handle ) {
+ switch( sess->scheme ) {
+ case http_auth_scheme_basic:
+ return request_basic( sess );
+ break;
+ case http_auth_scheme_digest:
+ return request_digest( sess );
+ break;
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+/* Examine a Basic auth challenge.
+ * Returns 0 if an valid challenge, else non-zero. */
+static int
+basic_challenge( http_auth_session *sess, struct http_auth_chall *parms )
+{
+ char *tmp, *password;
+
+ /* Verify challenge... must have a realm */
+ if( parms->realm == NULL ) {
+ return -1;
+ }
+
+ DEBUG( DEBUG_HTTPAUTH, "Got Basic challenge with realm [%s]\n",
+ parms->realm );
+
+ clean_session( sess );
+
+ sess->unq_realm = shave_string(parms->realm, '"' );
+
+ if( get_credentials( sess, &password ) ) {
+ /* Failed to get credentials */
+ HTTP_FREE( sess->unq_realm );
+ return -1;
+ }
+
+ sess->scheme = http_auth_scheme_basic;
+
+ CONCAT3( tmp, sess->username, ":", password?password:"" );
+ sess->basic = base64( tmp );
+ free( tmp );
+
+ HTTP_FREE( password );
+
+ return 0;
+}
+
+/* Add Basic authentication credentials to a request */
+static char *request_basic( http_auth_session *sess )
+{
+ char *buf;
+ CONCAT3( buf, "Basic ", sess->basic, "\r\n" );
+ return buf;
+}
+
+/* Examine a digest challenge: return 0 if it is a valid Digest challenge,
+ * else non-zero. */
+static int digest_challenge( http_auth_session *sess,
+ struct http_auth_chall *parms )
+{
+ struct md5_ctx tmp;
+ unsigned char tmp_md5[16];
+ char *password;
+
+ /* Do we understand this challenge? */
+ if( parms->alg == http_auth_alg_unknown ) {
+ DEBUG( DEBUG_HTTPAUTH, "Unknown algorithm.\n" );
+ return -1;
+ }
+ if( (parms->alg == http_auth_alg_md5_sess) &&
+ !( parms->qop_auth || parms->qop_auth_int ) ) {
+ DEBUG( DEBUG_HTTPAUTH, "Server did not give qop with MD5-session alg.\n" );
+ return -1;
+ }
+ if( (parms->realm==NULL) || (parms->nonce==NULL) ) {
+ DEBUG( DEBUG_HTTPAUTH, "Challenge missing nonce or realm.\n" );
+ return -1;
+ }
+
+ if( parms->stale ) {
+ /* Just a stale response, don't need to get a new username/password */
+ DEBUG( DEBUG_HTTPAUTH, "Stale digest challenge.\n" );
+ } else {
+ /* Forget the old session details */
+ DEBUG( DEBUG_HTTPAUTH, "In digest challenge.\n" );
+
+ clean_session( sess );
+ sess->unq_realm = shave_string(parms->realm, '"' );
+
+ /* Not a stale response: really need user authentication */
+ if( get_credentials( sess, &password ) ) {
+ /* Failed to get credentials */
+ HTTP_FREE( sess->unq_realm );
+ return -1;
+ }
+ }
+ sess->alg = parms->alg;
+ sess->scheme = http_auth_scheme_digest;
+ sess->unq_nonce = shave_string(parms->nonce, '"' );
+ sess->unq_cnonce = get_cnonce();
+ if( parms->domain ) {
+ if( parse_domain( sess, parms->domain ) ) {
+ /* TODO: Handle the error? */
+ }
+ } else {
+ sess->domain = NULL;
+ sess->domain_count = 0;
+ }
+ if( parms->opaque != NULL ) {
+ sess->opaque = xstrdup( parms->opaque ); /* don't strip the quotes */
+ }
+
+ if( parms->got_qop ) {
+ /* What type of qop are we to apply to the message? */
+ DEBUG( DEBUG_HTTPAUTH, "Got qop directive.\n" );
+ sess->nonce_count = 0;
+ if( parms->qop_auth_int ) {
+ sess->qop = http_auth_qop_auth_int;
+ } else {
+ sess->qop = http_auth_qop_auth;
+ }
+ } else {
+ /* No qop at all/ */
+ sess->qop = http_auth_qop_none;
+ }
+
+ if( !parms->stale ) {
+ /* Calculate H(A1).
+ * tmp = H( unq(username-value) ":" unq(realm-value) ":" passwd
+ */
+ DEBUG( DEBUG_HTTPAUTH, "Calculating H(A1).\n" );
+ md5_init_ctx( &tmp );
+ md5_process_bytes( sess->username, strlen(sess->username), &tmp);
+ md5_process_bytes( ":", 1, &tmp );
+ md5_process_bytes( sess->unq_realm, strlen(sess->unq_realm), &tmp );
+ md5_process_bytes( ":", 1, &tmp );
+ if( password != NULL )
+ md5_process_bytes( password, strlen(password), &tmp);
+ md5_finish_ctx( &tmp, tmp_md5 );
+ if( sess->alg == http_auth_alg_md5_sess ) {
+ unsigned char a1_md5[16];
+ struct md5_ctx a1;
+ char tmp_md5_ascii[33];
+ /* Now we calculate the SESSION H(A1)
+ * A1 = H( ...above...) ":" unq(nonce-value) ":" unq(cnonce-value)
+ */
+ md5_to_ascii( tmp_md5, tmp_md5_ascii );
+ md5_init_ctx( &a1 );
+ md5_process_bytes( tmp_md5_ascii, 32, &a1 );
+ md5_process_bytes( ":", 1, &a1 );
+ md5_process_bytes( sess->unq_nonce, strlen(sess->unq_nonce), &a1 );
+ md5_process_bytes( ":", 1, &a1 );
+ md5_process_bytes( sess->unq_cnonce, strlen(sess->unq_cnonce), &a1 );
+ md5_finish_ctx( &a1, a1_md5 );
+ md5_to_ascii( a1_md5, sess->h_a1 );
+ DEBUG( DEBUG_HTTPAUTH, "Session H(A1) is [%s]\n", sess->h_a1 );
+ } else {
+ md5_to_ascii( tmp_md5, sess->h_a1 );
+ DEBUG( DEBUG_HTTPAUTH, "H(A1) is [%s]\n", sess->h_a1 );
+ }
+
+ HTTP_FREE( password );
+
+ }
+
+ DEBUG( DEBUG_HTTPAUTH, "I like this Digest challenge.\n" );
+
+ return 0;
+}
+
+/* Return Digest authentication credentials header value for the given
+ * session. */
+static char *request_digest( http_auth_session *sess )
+{
+ struct md5_ctx a2, rdig;
+ unsigned char a2_md5[16], rdig_md5[16];
+ char a2_md5_ascii[33], rdig_md5_ascii[33];
+ char nc_value[9] = {0}, *ret;
+ const char *qop_value; /* qop-value */
+ size_t retlen;
+
+ /* Increase the nonce-count */
+ if( sess->qop != http_auth_qop_none ) {
+ sess->nonce_count++;
+ snprintf( nc_value, 9, "%08x", sess->nonce_count );
+ DEBUG( DEBUG_HTTPAUTH, "Nonce count is %d, nc is [%s]\n",
+ sess->nonce_count, nc_value );
+ }
+ qop_value = qop_values[sess->qop];
+
+ /* Calculate H(A2). */
+ md5_init_ctx( &a2 );
+ md5_process_bytes( sess->method, strlen(sess->method), &a2 );
+ md5_process_bytes( ":", 1, &a2 );
+ md5_process_bytes( sess->uri, strlen(sess->uri), &a2 );
+ if( sess->qop == http_auth_qop_auth_int ) {
+ /* Calculate H(entity-body) */
+ if( sess->got_body ) {
+ char tmp_md5_ascii[33], tmp_md5[16];
+ if( sess->body_stream != NULL ) {
+ DEBUG( DEBUG_HTTPAUTH, "Digesting body stream.\n" );
+ md5_stream( sess->body_stream, tmp_md5 );
+ rewind( sess->body_stream ); /* leave it at the beginning */
+ } else if( sess->body_buffer ) {
+ DEBUG( DEBUG_HTTPAUTH, "Digesting body buffer.\n" );
+ md5_buffer( sess->body_buffer, strlen(sess->body_buffer),
+ tmp_md5 );
+ }
+ md5_to_ascii( tmp_md5, tmp_md5_ascii );
+ DEBUG( DEBUG_HTTPAUTH, "H(entity-body) is [%s]\n", tmp_md5_ascii );
+ /* Append to A2 */
+ md5_process_bytes( ":", 1, &a2 );
+ md5_process_bytes( tmp_md5_ascii, 32, &a2 );
+ } else {
+ /* No entity-body. */
+ DEBUG( DEBUG_HTTPAUTH, "Digesting empty entity-body.\n" );
+ md5_process_bytes( ":" DIGEST_MD5_EMPTY, 33, &a2 );
+ }
+ }
+ md5_finish_ctx( &a2, a2_md5 );
+ md5_to_ascii( a2_md5, a2_md5_ascii );
+ DEBUG( DEBUG_HTTPAUTH, "H(A2): %s\n", a2_md5_ascii );
+
+ DEBUG( DEBUG_HTTPAUTH, "Calculating Request-Digest.\n" );
+ /* Now, calculation of the Request-Digest.
+ * The first section is the regardless of qop value
+ * H(A1) ":" unq(nonce-value) ":" */
+ md5_init_ctx( &rdig );
+
+ /* Use the calculated H(A1) */
+ md5_process_bytes( sess->h_a1, 32, &rdig );
+
+ md5_process_bytes( ":", 1, &rdig );
+ md5_process_bytes( sess->unq_nonce, strlen(sess->unq_nonce), &rdig );
+ md5_process_bytes( ":", 1, &rdig );
+ if( sess->qop != http_auth_qop_none ) {
+ /* Add on:
+ * nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":"
+ */
+ DEBUG( DEBUG_HTTPAUTH, "Have qop directive, digesting: [%s:%s:%s]\n",
+ nc_value, sess->unq_cnonce, qop_value );
+ md5_process_bytes( nc_value, 8, &rdig );
+ md5_process_bytes( ":", 1, &rdig );
+ md5_process_bytes( sess->unq_cnonce, strlen(sess->unq_cnonce), &rdig );
+ md5_process_bytes( ":", 1, &rdig );
+ /* Store a copy of this structure (see note below) */
+ sess->stored_rdig = rdig;
+ md5_process_bytes( qop_value, strlen(qop_value), &rdig );
+ md5_process_bytes( ":", 1, &rdig );
+ } else {
+ /* Store a copy of this structure... we do this because the
+ * calculation of the rspauth= field in the Auth-Info header
+ * is the same as this digest, up to this point. */
+ sess->stored_rdig = rdig;
+ }
+ /* And finally, H(A2) */
+ md5_process_bytes( a2_md5_ascii, 32, &rdig );
+ md5_finish_ctx( &rdig, rdig_md5 );
+ md5_to_ascii( rdig_md5, rdig_md5_ascii );
+
+ /* Buffer size calculation. */
+
+ retlen =
+ 6 /* Digest */
+ + 1 + 8 + 1 + 2 + strlen(sess->username) /* username="..." */
+ + 2 + 5 + 1 + 2 + strlen(sess->unq_realm) /* , realm="..." */
+ + 2 + 5 + 1 + 2 + strlen(sess->unq_nonce) /* , nonce="..." */
+ + 2 + 3 + 1 + 2 + strlen(sess->uri) /* , uri="..." */
+ + 2 + 8 + 1 + 2 + 32 /* , response="..." */
+ + 2 + 9 + 1 + strlen(algorithm_names[sess->alg]) /* , algorithm= */
+ ;
+
+ if( sess->opaque != NULL )
+ retlen += 2 + 6 + 1 + strlen(sess->opaque); /* , opaque=... */
+
+ if( sess->qop != http_auth_qop_none )
+ retlen +=
+ 2 + 6 + 2 + 1 + strlen(sess->unq_cnonce) + /* , cnonce="..." */
+ 2 + 2 + 1 + 8 + /* , nc=... */
+ 2 + 3 + 1 + strlen(qop_values[sess->qop]) /* , qop=... */
+ ;
+
+ retlen += 2; /* \r\n */
+
+ DEBUG( DEBUG_HTTPAUTH, "Calculated length of buffer: %d\n", retlen );
+
+ ret = xmalloc( retlen + 1 );
+
+ sprintf( ret,
+ "Digest username=\"%s\", realm=\"%s\""
+ ", nonce=\"%s\", uri=\"%s\", response=\"%s\""
+ ", algorithm=%s",
+ sess->username, sess->unq_realm,
+ sess->unq_nonce, sess->uri, rdig_md5_ascii,
+ algorithm_names[sess->alg]);
+
+ if( sess->opaque != NULL ) {
+ /* We never unquote it, so it's still quoted here */
+ strcat( ret, ", opaque=" );
+ strcat( ret, sess->opaque );
+ }
+
+ if( sess->qop != http_auth_qop_none ) {
+ /* Add in cnonce and nc-value fields */
+ strcat( ret, ", cnonce=\"" );
+ strcat( ret, sess->unq_cnonce );
+ strcat( ret, "\", nc=" );
+ strcat( ret, nc_value );
+ strcat( ret, ", qop=" );
+ strcat( ret, qop_values[sess->qop] );
+ }
+
+ DEBUG( DEBUG_HTTPAUTH, "Digest header field value:\n%s\n", ret );
+
+ strcat( ret, "\r\n" );
+
+ DEBUG( DEBUG_HTTPAUTH, "Calculated length: %d, actual length: %d\n",
+ retlen, strlen( ret ) );
+
+ return ret;
+}
+
+inline void http_auth_response_body( http_auth_session *sess,
+ const char *buffer, size_t buffer_len )
+{
+ if( !sess->will_handle ||
+ sess->scheme != http_auth_scheme_digest ) return;
+ DEBUG( DEBUG_HTTPAUTH, "Digesting %d bytes of response body.\n",
+ buffer_len );
+ md5_process_bytes( buffer, buffer_len, &sess->response_body );
+}
+
+/* Pass this the value of the 'Authentication-Info:' header field, if
+ * one is received.
+ * Returns:
+ * 0 if it gives a valid authentication for the server
+ * non-zero otherwise (don't believe the response in this case!).
+ */
+int http_auth_verify_response( http_auth_session *sess, const char *value )
+{
+ char **pairs;
+ http_auth_qop qop = http_auth_qop_none;
+ char *nextnonce = NULL, /* for the nextnonce= value */
+ *rspauth = NULL, /* for the rspauth= value */
+ *cnonce = NULL, /* for the cnonce= value */
+ *nc = NULL, /* for the nc= value */
+ *unquoted, *qop_value = NULL;
+ int n, nonce_count, okay;
+
+ if( !sess->will_handle ) {
+ /* Ignore it */
+ return 0;
+ }
+
+ if( sess->scheme != http_auth_scheme_digest ) {
+ DEBUG( DEBUG_HTTPAUTH, "Found Auth-Info header not in response to Digest credentials - dodgy.\n" );
+ return -1;
+ }
+
+ DEBUG (DEBUG_HTTPAUTH, "Auth-Info header: %s\n", value );
+
+ pairs = pair_string( value, ',', '=', HTTP_QUOTES, HTTP_WHITESPACE );
+
+ for( n = 0; pairs[n]!=NULL; n+=2 ) {
+ unquoted = shave_string( pairs[n+1], '"' );
+ if( strcasecmp( pairs[n], "qop" ) == 0 ) {
+ qop_value = xstrdup( pairs[n+1] );
+ if( strcasecmp( pairs[n+1], "auth-int" ) == 0 ) {
+ qop = http_auth_qop_auth_int;
+ } else if( strcasecmp( pairs[n+1], "auth" ) == 0 ) {
+ qop = http_auth_qop_auth;
+ } else {
+ qop = http_auth_qop_none;
+ }
+ } else if( strcasecmp( pairs[n], "nextnonce" ) == 0 ) {
+ nextnonce = xstrdup( unquoted );
+ } else if( strcasecmp( pairs[n], "rspauth" ) == 0 ) {
+ rspauth = xstrdup( unquoted );
+ } else if( strcasecmp( pairs[n], "cnonce" ) == 0 ) {
+ cnonce = xstrdup( unquoted );
+ } else if( strcasecmp( pairs[n], "nc" ) == 0 ) {
+ nc = xstrdup( pairs[n] );
+ if( sscanf( pairs[n+1], "%x", &nonce_count ) != 1 ) {
+ DEBUG( DEBUG_HTTPAUTH, "Couldn't scan [%s] for nonce count.\n",
+ pairs[n+1] );
+ } else {
+ DEBUG( DEBUG_HTTPAUTH, "Got nonce_count: %d\n", nonce_count );
+ }
+ }
+ free( unquoted );
+ }
+ pair_string_free( pairs );
+
+ /* Presume the worst */
+ okay = -1;
+
+ if( (qop != http_auth_qop_none) && (qop_value != NULL) ) {
+ if( (rspauth == NULL) || (cnonce == NULL) || (nc == NULL) ) {
+ DEBUG( DEBUG_HTTPAUTH, "Missing rspauth, cnonce or nc with qop.\n" );
+ } else { /* Have got rspauth, cnonce and nc */
+ if( strcmp( cnonce, sess->unq_cnonce ) != 0 ) {
+ DEBUG( DEBUG_HTTPAUTH, "Response cnonce doesn't match.\n" );
+ } else if( nonce_count != sess->nonce_count ) {
+ DEBUG( DEBUG_HTTPAUTH, "Response nonce count doesn't match.\n" );
+ } else {
+ /* Calculate and check the response-digest value.
+ * joe: IMO the spec is slightly ambiguous as to whether
+ * we use the qop which WE sent, or the qop which THEY
+ * sent... */
+ struct md5_ctx a2;
+ unsigned char a2_md5[16], rdig_md5[16];
+ char a2_md5_ascii[33], rdig_md5_ascii[33];
+
+ DEBUG( DEBUG_HTTPAUTH, "Calculating response-digest.\n" );
+
+ /* First off, H(A2) again. */
+ md5_init_ctx( &a2 );
+ md5_process_bytes( ":", 1, &a2 );
+ md5_process_bytes( sess->uri, strlen(sess->uri), &a2 );
+ if( qop == http_auth_qop_auth_int ) {
+ unsigned char heb_md5[16];
+ char heb_md5_ascii[33];
+ /* Add on ":" H(entity-body) */
+ md5_finish_ctx( &sess->response_body, heb_md5 );
+ md5_to_ascii( heb_md5, heb_md5_ascii );
+ md5_process_bytes( ":", 1, &a2 );
+ md5_process_bytes( heb_md5_ascii, 32, &a2 );
+ DEBUG( DEBUG_HTTPAUTH, "Digested [:%s]\n", heb_md5_ascii );
+ }
+ md5_finish_ctx( &a2, a2_md5 );
+ md5_to_ascii( a2_md5, a2_md5_ascii );
+
+ /* We have the stored digest-so-far of
+ * H(A1) ":" unq(nonce-value)
+ * [ ":" nc-value ":" unq(cnonce-value) ] for qop
+ * in sess->stored_rdig, to save digesting them again.
+ *
+ */
+ if( qop != http_auth_qop_none ) {
+ /* Add in qop-value */
+ DEBUG( DEBUG_HTTPAUTH, "Digesting qop-value [%s:].\n",
+ qop_value );
+ md5_process_bytes( qop_value, strlen(qop_value),
+ &sess->stored_rdig );
+ md5_process_bytes( ":", 1, &sess->stored_rdig );
+ }
+ /* Digest ":" H(A2) */
+ md5_process_bytes( a2_md5_ascii, 32, &sess->stored_rdig );
+ /* All done */
+ md5_finish_ctx( &sess->stored_rdig, rdig_md5 );
+ md5_to_ascii( rdig_md5, rdig_md5_ascii );
+
+ DEBUG( DEBUG_HTTPAUTH, "Calculated response-digest of: [%s]\n",
+ rdig_md5_ascii );
+ DEBUG( DEBUG_HTTPAUTH, "Given response-digest of: [%s]\n",
+ rspauth );
+
+ /* And... do they match? */
+ okay = (strcasecmp( rdig_md5_ascii, rspauth ) == 0)?0:-1;
+ DEBUG( DEBUG_HTTPAUTH, "Matched: %s\n", okay?"nope":"YES!" );
+ }
+ free( rspauth );
+ free( cnonce );
+ free( nc );
+ }
+ free( qop_value );
+ } else {
+ DEBUG( DEBUG_HTTPAUTH, "No qop directive, auth okay.\n" );
+ okay = 0;
+ }
+
+ /* Check for a nextnonce */
+ if( nextnonce != NULL ) {
+ DEBUG( DEBUG_HTTPAUTH, "Found nextnonce of [%s].\n", nextnonce );
+ if( sess->unq_nonce != NULL )
+ free( sess->unq_nonce );
+ sess->unq_nonce = nextnonce;
+ }
+
+ return okay;
+}
+
+/* A new challenge presented by the server */
+int http_auth_challenge( http_auth_session *sess, const char *value )
+{
+ char **pairs, *pnt, *unquoted, *key;
+ struct http_auth_chall *chall = NULL, *challenges = NULL;
+ int n, success;
+
+ DEBUG( DEBUG_HTTPAUTH, "Got new auth challenge: %s\n", value );
+
+ /* The header value may be made up of one or more challenges.
+ * We split it down into attribute-value pairs, then search for
+ * schemes in the pair keys.
+ */
+ pairs = pair_string( value, ',', '=', HTTP_QUOTES, HTTP_WHITESPACE );
+
+ for( n = 0; pairs[n]!=NULL; n+=2 ) {
+ /* Look for an auth-scheme in the key */
+ pnt = strchr( pairs[n], ' ' );
+ if( pnt != NULL ) {
+ /* We have a new challenge */
+ DEBUG( DEBUG_HTTPAUTH, "New challenge.\n" );
+ chall = xmalloc( sizeof(struct http_auth_chall) );
+ memset( chall, 0, sizeof(struct http_auth_chall) );
+ chall->next = challenges;
+ challenges = chall;
+ /* Initialize the challenge parameters */
+ /* Which auth-scheme is it (case-insensitive matching) */
+ if( strncasecmp( pairs[n], "basic ", 6 ) == 0 ) {
+ DEBUG( DEBUG_HTTPAUTH, "Basic scheme.\n" );
+ chall->scheme = http_auth_scheme_basic;
+ } else if( strncasecmp( pairs[n], "digest ", 7 ) == 0 ) {
+ DEBUG( DEBUG_HTTPAUTH, "Digest scheme.\n" );
+ chall->scheme = http_auth_scheme_digest;
+ } else {
+ DEBUG( DEBUG_HTTPAUTH, "Unknown scheme.\n" );
+ free( chall );
+ challenges = NULL;
+ break;
+ }
+ /* Now, the real key for this pair starts after the
+ * auth-scheme... skipping whitespace */
+ while( strchr( HTTP_WHITESPACE, *(++pnt) ) != NULL )
+ /* nullop */;
+ key = pnt;
+ } else if( chall == NULL ) {
+ /* If we haven't got an auth-scheme, and we're
+ * haven't yet found a challenge, skip this pair.
+ */
+ continue;
+ } else {
+ key = pairs[n];
+ }
+ DEBUG( DEBUG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, pairs[n+1] );
+ /* Most values are quoted, so unquote them here */
+ unquoted = shave_string( pairs[n+1], '"' );
+ /* Now parse the attribute */
+ DEBUG( DEBUG_HTTPAUTH, "Unquoted pair is: [%s]\n", unquoted );
+ if( strcasecmp( key, "realm" ) == 0 ) {
+ chall->realm = pairs[n+1];
+ } else if( strcasecmp( key, "nonce" ) == 0 ) {
+ chall->nonce = pairs[n+1];
+ } else if( strcasecmp( key, "opaque" ) == 0 ) {
+ chall->opaque = pairs[n+1];
+ } else if( strcasecmp( key, "domain" ) == 0 ) {
+ chall->domain = pairs[n+1];
+ } else if( strcasecmp( key, "stale" ) == 0 ) {
+ /* Truth value */
+ chall->stale =
+ ( strcasecmp( unquoted, "true" ) == 0 );
+ } else if( strcasecmp( key, "algorithm" ) == 0 ) {
+ if( strcasecmp( unquoted, "md5" ) == 0 ) {
+ chall->alg = http_auth_alg_md5;
+ } else if( strcasecmp( unquoted, "md5-sess" ) == 0 ) {
+ chall->alg = http_auth_alg_md5_sess;
+ } else {
+ chall->alg = http_auth_alg_unknown;
+ }
+ } else if( strcasecmp( key, "qop" ) == 0 ) {
+ char **qops;
+ int qop;
+ qops = split_string( unquoted, ',', NULL, HTTP_WHITESPACE );
+ chall->got_qop = 1;
+ for( qop = 0; qops[qop] != NULL; qop++ ) {
+ if( strcasecmp( qops[qop], "auth" ) == 0 ) {
+ chall->qop_auth = 1;
+ } else if( strcasecmp( qops[qop], "auth-int" ) == 0 ) {
+ chall->qop_auth_int = 1;
+ }
+ }
+ split_string_free( qops );
+ }
+ free( unquoted );
+ }
+
+ DEBUG( DEBUG_HTTPAUTH, "Finished parsing parameters.\n" );
+
+ /* Did we find any challenges */
+ if( challenges == NULL ) {
+ pair_string_free( pairs );
+ return -1;
+ }
+
+ success = 0;
+
+ DEBUG( DEBUG_HTTPAUTH, "Looking for Digest challenges.\n" );
+
+ /* Try a digest challenge */
+ for( chall = challenges; chall != NULL; chall = chall->next ) {
+ if( chall->scheme == http_auth_scheme_digest ) {
+ if( !digest_challenge( sess, chall ) ) {
+ success = 1;
+ break;
+ }
+ }
+ }
+
+ if( !success ) {
+ DEBUG( DEBUG_HTTPAUTH, "No good Digest challenges, looking for Basic.\n" );
+ for( chall = challenges; chall != NULL; chall = chall->next ) {
+ if( chall->scheme == http_auth_scheme_basic ) {
+ if( !basic_challenge( sess, chall ) ) {
+ success = 1;
+ break;
+ }
+ }
+ }
+
+ if( !success ) {
+ /* No good challenges - record this in the session state */
+ DEBUG( DEBUG_HTTPAUTH, "Did not understand any challenges.\n" );
+ }
+
+ }
+
+ /* Remember whether we can now supply the auth details */
+ sess->can_handle = success;
+
+ while( challenges != NULL ) {
+ chall = challenges->next;
+ free( challenges );
+ challenges = chall;
+ }
+
+ /* Free up the parsed header values */
+ pair_string_free( pairs );
+
+ return !success;
+}
diff --git a/neon/src/http_auth.h b/neon/src/http_auth.h
new file mode 100644
index 000000000..8b0a430b7
--- /dev/null
+++ b/neon/src/http_auth.h
@@ -0,0 +1,206 @@
+/*
+ HTTP authentication routines
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef HTTPAUTH_H
+#define HTTPAUTH_H
+
+#include <sys/types.h>
+
+#include "md5.h"
+
+/* HTTP Authentication - a pretty complete client implementation of RFC2617.
+ */
+
+/*
+ To use:
+
+ 1. A session state variable (http_auth_session) is needed for each of
+ server state and proxy state. These may be statically declared (use
+ _init/_finish), or dynamically (use _create/_destroy).
+
+ 2. To begin a new session, call http_auth_init() or http_auth_create().
+ Set up a callback function with http_auth_set_creds_cb() for
+ supplying the username and password on demand. See below for details.
+
+ 3. Before sending a request, pass http_auth_new_request its details,
+ on BOTH auth session variables if you are using a proxy server too.
+
+ 4. Call http_auth_request_header() and add your 'Authentication:'
+ header to the request if returns non-NULL. Similarly for
+ Proxy-Authentication.
+
+ 5. Send the request.
+
+ 6. Read the response:
+ - Pass the value of the '(Proxy|WWW)-Authenticate' header to
+ http_auth_challenge.
+ - If there is 'Authentication-Info', save its value for later.
+ - Pass each block of the response entity-body to http_auth_response_body.
+
+ 7. After reading the complete response, if an Auth-Info header was
+ received, pass its value to http_auth_verify_response to check
+ whether the SERVER was authenticated okay, passing the saved value.
+
+ 8. If a 401 or a 407 response is received, retry once for each, by
+ going back to step 3. Note that http_auth_new_request MUST be called
+ again if the SAME request is being retried.
+
+*/
+
+/* The authentication scheme we are using */
+typedef enum {
+ http_auth_scheme_basic,
+ http_auth_scheme_digest
+} http_auth_scheme;
+
+typedef enum {
+ http_auth_alg_md5,
+ http_auth_alg_md5_sess,
+ http_auth_alg_unknown
+} http_auth_algorithm;
+
+/* Selected method of qop which the client is using */
+typedef enum {
+ http_auth_qop_none,
+ http_auth_qop_auth,
+ http_auth_qop_auth_int
+} http_auth_qop;
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocate
+ * memory.
+ * Must return:
+ * 0 on success,
+ * -1 to cancel.
+ */
+typedef int (*http_auth_request_creds)(
+ void *userdata, const char *realm,
+ char **username, char **password );
+
+/* Authentication session state. */
+typedef struct {
+ /* The scheme used for this authentication session */
+ http_auth_scheme scheme;
+ /* The callback used to request new username+password */
+ http_auth_request_creds reqcreds;
+ void *reqcreds_udata;
+
+ /*** Session details ***/
+
+ /* The username and password we are using to authenticate with */
+ char *username;
+ /* Whether we CAN supply authentication at the moment */
+ unsigned int can_handle:1;
+ /* This used for Basic auth */
+ char *basic;
+ /* These all used for Digest auth */
+ char *unq_realm;
+ char *unq_nonce;
+ char *unq_cnonce;
+ char *opaque;
+ /* A list of domain strings */
+ unsigned int domain_count;
+ char **domain;
+ http_auth_qop qop;
+ http_auth_algorithm alg;
+ int nonce_count;
+ /* The ASCII representation of the session's H(A1) value */
+ char h_a1[33];
+ /* Used for calculation of H(entity-body) of the response */
+ struct md5_ctx response_body;
+ /* Temporary store for half of the Request-Digest
+ * (an optimisation - used in the response-digest calculation) */
+ struct md5_ctx stored_rdig;
+
+ /* Details of server... needed to reconstruct absoluteURI's when
+ * necessary */
+ const char *host;
+ const char *uri_scheme;
+ unsigned int port;
+
+ /*** Details of current request ***/
+
+ /* The method and URI we are using for the current request */
+ const char *uri;
+ const char *method;
+ /* Whether we WILL supply authentication for this request or not */
+ unsigned int will_handle:1;
+ /* Whether we have a request body for the current request */
+ unsigned int got_body:1;
+ /* And what the body is - stream or buffer */
+ FILE *body_stream;
+ const char *body_buffer;
+
+} http_auth_session;
+
+/* Initializes the authentication state for the given session,
+ * which will use the given username and password. */
+void http_auth_init( http_auth_session *sess );
+
+void http_auth_set_creds_cb( http_auth_session *sess,
+ http_auth_request_creds callback, void *userdata );
+
+/* Finishes off the given authentication session, freeing
+ * any memory used. */
+void http_auth_finish( http_auth_session *sess );
+
+/* Creates a new authentication session.
+ * Returns non-NULL on success */
+http_auth_session * http_auth_create( void );
+
+/* Destroys an authentication session, freeing the session state
+ * itself too. */
+void http_auth_destroy( http_auth_session *sess );
+
+/* Call this before sending a request. Pass ONE OF body_buffer or
+ * body_stream as non-NULL if the request will include an
+ * entity-body. If body_buffer is non-NULL, it MUST be
+ * \0-terminated. If body_stream is non-NULL, it may be read once
+ * during http_auth_challenge, then rewound. uri must identical to
+ * Request-URI, EXCEPT for server auth state, where if the request is
+ * passing through a proxy, then uri should be the same as abs_path. */
+void http_auth_new_request( http_auth_session *sess,
+ const char *method, const char *uri,
+ const char *body_buffer, FILE *body_stream );
+
+/* Returns the value of the authentication field if one is to be sent,
+ * else NULL. The return value will be taken from malloc()'ed memory,
+ * so should be free()'ed after use. */
+char *http_auth_request_header( http_auth_session *sess );
+
+/* Pass this the value of the "(Proxy,WWW)-Authenticate: " header field.
+ * Returns:
+ * 0 if we can now authenticate ourselves with the server.
+ * non-zero if we can't
+ */
+int http_auth_challenge( http_auth_session *sess, const char *value );
+
+/* As you receive sections of the response entity-body, pass them to
+ * this function. */
+void http_auth_response_body( http_auth_session *sess,
+ const char *buffer, size_t buffer_len );
+
+/* If you receive a "(Proxy-)Authentication-Info:" header, pass its value to
+ * this function. Returns zero if this successfully authenticates
+ * the response as coming from the server, and false if it hasn't. */
+int http_auth_verify_response( http_auth_session *sess, const char *value );
+
+#endif /* HTTPAUTH_H */
diff --git a/neon/src/http_basic.c b/neon/src/http_basic.c
new file mode 100644
index 000000000..ac8682d55
--- /dev/null
+++ b/neon/src/http_basic.c
@@ -0,0 +1,357 @@
+/*
+ HTTP/1.1 methods
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: http_basic.c,v 1.12 2000/05/10 16:45:58 joe Exp
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <errno.h>
+
+#include "http_request.h"
+#include "http_basic.h"
+#include "dates.h"
+#include "socket.h"
+#include "neon_i18n.h"
+
+/* Header parser to retrieve Last-Modified date */
+static void get_lastmodified( void *userdata, const char *value ) {
+ time_t *modtime = userdata;
+ *modtime = http_dateparse( value );
+}
+
+int http_getmodtime( http_session *sess, const char *uri, time_t *modtime )
+{
+ http_req *req = http_request_create( sess, "HEAD", uri );
+ http_status st;
+ int ret;
+
+#ifdef USE_DAV_LOCKS
+ dav_lock_using_resource( req, uri, dav_lockusage_read, 0 );
+#endif
+
+ http_add_response_header_handler( req, "Last-Modified", get_lastmodified,
+ modtime );
+
+ *modtime = -1;
+
+ ret = http_request_dispatch( req, &st );
+
+ if( ret == HTTP_OK && st.class != 2 ) {
+ *modtime = -1;
+ ret = HTTP_ERROR;
+ }
+
+ http_request_destroy( req );
+
+ return ret;
+}
+
+/* PUT's stream to URI */
+int http_put( http_session *sess, const char *uri, FILE *stream )
+{
+ http_req *req = http_request_create( sess, "PUT", uri );
+ http_status status;
+ int ret;
+
+#ifdef USE_DAV_LOCKS
+ dav_lock_using_resource( req, uri, dav_lockusage_write, 0 );
+ dav_lock_using_parent( req, uri );
+#endif
+
+ http_set_request_body_stream( req, stream );
+
+ ret = http_request_dispatch( req, &status );
+
+ if( ret == HTTP_OK && status.class != 2 )
+ ret = HTTP_ERROR;
+
+ http_request_destroy( req );
+
+ return ret;
+}
+
+/* Conditional HTTP put.
+ * PUTs stream to uri, returning HTTP_FAILED if resource as URI has
+ * been modified more recently than 'since'.
+ */
+int
+http_put_if_unmodified( http_session *sess, const char *uri,
+ FILE *stream, time_t since ) {
+ http_req *req;
+ http_status status;
+ sbuffer hdrs;
+ char *date;
+ int ret;
+
+ if( http_version_pre_http11(sess) ) {
+ time_t modtime;
+ /* Server is not minimally HTTP/1.1 compliant. Do a HEAD to
+ * check the remote mod time. Of course, this makes the
+ * operation very non-atomic, but better than nothing. */
+ ret = http_getmodtime( sess, uri, &modtime );
+ if( ret != HTTP_OK ) return ret;
+ if( modtime != since )
+ return HTTP_FAILED;
+ }
+
+ req = http_request_create( sess, "PUT", uri );
+
+ date = rfc1123_date( since );
+ /* Add in the conditionals */
+ hdrs = http_get_request_header( req );
+ sbuffer_concat( hdrs, "If-Unmodified-Since: ", date, EOL, NULL );
+ free( date );
+
+#ifdef USE_DAV_LOCKS
+ dav_lock_using_resource( &req, uri, dav_lockusage_write, 0 );
+ /* NB: this will give 412 if the resource doesn't exist, since PUT
+ * may modify the parent, but thats okay, since that will give
+ * a HTTP_FAILED response too. */
+#endif
+
+ http_set_request_body_stream( req, stream );
+
+ ret = http_request_dispatch( req, &status );
+
+ if( ret == HTTP_OK ) {
+ if( status.code == 412 ) {
+ ret = HTTP_FAILED;
+ } else if( status.class != 2 ) {
+ ret = HTTP_ERROR;
+ }
+ }
+
+ http_request_destroy( req );
+
+ return ret;
+}
+
+struct get_context {
+ int error;
+ size_t total, progress;
+ http_block_reader callback; /* used in read_file */
+ FILE *file; /* used in get_to_fd */
+ void *userdata;
+};
+
+static void get_callback( void *userdata, const char *block, size_t length )
+{
+ struct get_context *ctx = userdata;
+
+ DEBUG( DEBUG_HTTP, "Got progress: %d out of %d\n",
+ ctx->progress, ctx->total );
+
+ (*ctx->callback)( ctx->userdata, block, length );
+
+ /* Increase progress */
+ ctx->progress += length;
+ if( ctx->progress > ctx->total ) {
+ /* Reset the counter if we're uploading it again */
+ ctx->progress -= ctx->total;
+ }
+ sock_call_progress( ctx->progress, ctx->total );
+}
+
+int http_read_file( http_session *sess, const char *uri,
+ http_block_reader reader, void *userdata ) {
+ struct get_context ctx;
+ http_req *req = http_request_create( sess, "GET", uri );
+ http_status st;
+ int ret;
+
+ ctx.total = -1;
+ ctx.progress = 0;
+ ctx.callback = reader;
+ ctx.userdata = userdata;
+
+ /* Read the value of the Content-Length header into ctx.total */
+ http_add_response_header_handler( req, "Content-Length",
+ http_handle_numeric_header,
+ &ctx.total );
+
+ http_add_response_body_reader( req, http_accept_2xx, get_callback, &ctx );
+
+#ifdef USE_DAV_LOCKS
+ dav_lock_using_resource( req, uri, dav_lockusage_read, 0 );
+#endif
+
+ ret = http_request_dispatch( req, &st );
+
+ if( ret == HTTP_OK && st.class != 2 )
+ ret = HTTP_ERROR;
+
+ http_request_destroy( req );
+
+ return ret;
+}
+
+static void get_to_fd( void *userdata, const char *block, size_t length )
+{
+ struct get_context *ctx = userdata;
+ FILE *f = ctx->file;
+ if( !ctx->error ) {
+ if( fwrite( block, length, 1, f ) < length ) {
+ ctx->error = errno;
+ }
+ }
+}
+
+/* Get to given stream */
+int http_get( http_session *sess, const char *uri, FILE *f )
+{
+ http_req *req = http_request_create( sess, "GET", uri );
+ http_status status;
+ struct get_context ctx;
+ int ret;
+
+ ctx.total = -1;
+ ctx.progress = 0;
+ ctx.callback = get_to_fd;
+ ctx.userdata = &ctx;
+ ctx.file = f;
+ ctx.error = 0;
+
+ /* Read the value of the Content-Length header into ctx.total */
+ http_add_response_header_handler( req, "Content-Length",
+ http_handle_numeric_header,
+ &ctx.total );
+
+ http_add_response_body_reader( req, http_accept_2xx, get_callback, &ctx );
+
+#ifdef USE_DAV_LOCKS
+ dav_lock_using_resource( req, uri, dav_lockusage_read, 0 );
+#endif
+
+ ret = http_request_dispatch( req, &status );
+
+ if( ctx.error ) {
+ char buf[BUFSIZ];
+ snprintf( buf, BUFSIZ,
+ _("Could not write to file: %s"), strerror(ctx.error) );
+ http_set_error( sess, buf );
+ ret = HTTP_ERROR;
+ }
+
+ if( ret == HTTP_OK && status.class != 2 ) {
+ ret = HTTP_ERROR;
+ }
+
+ http_request_destroy( req );
+
+ return ret;
+}
+
+static void server_hdr_handler( void *userdata, const char *value )
+{
+ char **tokens = split_string( value, ' ', HTTP_QUOTES, NULL );
+ http_server_capabilities *caps = userdata;
+ int n;
+
+ for( n = 0; tokens[n] != NULL; n++ ) {
+ if( strncasecmp( tokens[n], "Apache/", 7 ) == 0 &&
+ strlen(tokens[n]) > 11 ) { /* 12 == "Apache/1.3.0" */
+ const char *ver = tokens[n] + 7;
+ int count;
+ char **vers;
+ vers = split_string_c( ver, '.', NULL, NULL, &count );
+ /* Apache/1.3.6 and before have broken Expect: 100 support */
+ if( count > 1 && atoi(vers[0]) < 2 && atoi(vers[1]) < 4 && atoi(vers[2]) < 7 ) {
+ caps->broken_expect100 = 1;
+ }
+ split_string_free( vers );
+ }
+ }
+
+ split_string_free( tokens );
+}
+
+void http_content_type_handler( void *userdata, const char *value )
+{
+ http_content_type *ct = userdata;
+ char *sep, *parms;
+
+ ct->value = xstrdup(value);
+
+ sep = strchr( ct->value, '/' );
+ if( !sep ) {
+ HTTP_FREE( ct->value );
+ return;
+ }
+
+ *++sep = '\0';
+ ct->type = ct->value;
+ ct->subtype = sep;
+
+ parms = strchr( ct->value, ';' );
+
+ if( parms ) {
+ *parms = '\0';
+ /* TODO: handle charset. */
+ }
+}
+
+static void dav_hdr_handler( void *userdata, const char *value )
+{
+ char **classes, **class;
+ http_server_capabilities *caps = userdata;
+
+ classes = split_string( value, ',', HTTP_QUOTES, HTTP_WHITESPACE );
+ for( class = classes; *class!=NULL; class++ ) {
+
+ if( strcmp( *class, "1" ) == 0 ) {
+ caps->dav_class1 = 1;
+ } else if( strcmp( *class, "2" ) == 0 ) {
+ caps->dav_class2 = 1;
+ } else if( strcmp( *class, "<http://apache.org/dav/propset/fs/1>" ) == 0 ) {
+ caps->dav_executable = 1;
+ }
+ }
+
+ split_string_free( classes );
+
+}
+
+int http_options( http_session *sess, const char *uri,
+ http_server_capabilities *caps )
+{
+ http_req *req = http_request_create( sess, "OPTIONS", uri );
+ http_status status;
+
+ int ret;
+
+ http_add_response_header_handler( req, "Server", server_hdr_handler, caps );
+ http_add_response_header_handler( req, "DAV", dav_hdr_handler, caps );
+
+ ret = http_request_dispatch( req, &status );
+
+ if( ret == HTTP_OK && status.class != 2 ) {
+ ret = HTTP_ERROR;
+ }
+
+ http_request_destroy( req );
+
+ return ret;
+}
diff --git a/neon/src/http_basic.h b/neon/src/http_basic.h
new file mode 100644
index 000000000..365b84e8c
--- /dev/null
+++ b/neon/src/http_basic.h
@@ -0,0 +1,103 @@
+/*
+ HTTP/1.1 methods
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef HTTP_BASIC_H
+#define HTTP_BASIC_H
+
+#include "config.h"
+
+#include <sys/types.h> /* for time_t */
+
+#include <stdio.h> /* for FILE * */
+
+/* PUT resource at uri, reading request body from f */
+int http_put( http_session *sess, const char *uri, FILE *f );
+
+/* PUT resource at uri as above, only if it has not been modified
+ * since given modtime. If server is HTTP/1.1, uses If-Unmodified-Since
+ * header; guaranteed failure if resource is modified after 'modtime'.
+ * If server is HTTP/1.0, HEAD's the resource first to fetch current
+ * modtime; race condition if resource is modified between HEAD and PUT.
+ */
+int http_put_if_unmodified( http_session *sess,
+ const char *uri, FILE *stream, time_t modtime );
+
+/* GET resource at uri, writing response body into f */
+int http_get( http_session *sess, const char *uri, FILE *f );
+
+/* GET resource at uri, passing response body blocks to 'reader' */
+int http_read_file( http_session *sess, const char *uri,
+ http_block_reader reader, void *userdata );
+
+/* Retrieve modification time of resource at uri, place in *modtime.
+ * (uses HEAD) */
+int http_getmodtime( http_session *sess, const char *uri, time_t *modtime );
+
+typedef struct {
+ const char *type, *subtype;
+ const char *charset;
+ char *value;
+} http_content_type;
+
+/* Sets (*http_content_type)userdata appropriately.
+ * Caller must free ->value after use */
+void http_content_type_handler( void *userdata, const char *value );
+
+/* Server capabilities: */
+typedef struct {
+ unsigned int broken_expect100:1; /* True if the server is known to
+ * have broken Expect:
+ * 100-continue support; Apache
+ * 1.3.6 and earlier. */
+
+ unsigned int dav_class1; /* True if Class 1 WebDAV server */
+ unsigned int dav_class2; /* True if Class 2 WebDAV server */
+ unsigned int dav_executable; /* True if supports the 'executable'
+ * property a. la. mod_dav */
+} http_server_capabilities;
+
+/* Determines server capabilities (using OPTIONS).
+ * Pass uri="*" to determine proxy server capabilities if using
+ * a proxy server. */
+int http_options( http_session *sess, const char *uri,
+ http_server_capabilities *caps );
+
+#if 0 /* TODO: unimplemented */
+
+typedef http_content_range {
+ long start, end, total;
+} http_content_range;
+
+/* This will write to the CURRENT position of f; so if you want
+ * to do a resume download, use:
+ * struct http_content_range range;
+ * range.start = resume_from;
+ * range.end = range.total = 1000;
+ * fseek( myfile, resume_from, SEEK_SET );
+ * http_get_range( sess, uri, &range, myfile );
+ */
+int http_get_range( http_session *sess, const char *uri,
+ http_content_range *range, FILE *f );
+
+#endif
+
+
+#endif
diff --git a/neon/src/http_cookies.c b/neon/src/http_cookies.c
new file mode 100644
index 000000000..6a8562895
--- /dev/null
+++ b/neon/src/http_cookies.c
@@ -0,0 +1,143 @@
+/*
+ Basic cookie support for neon
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: http_cookies.c,v 1.5 2000/07/16 16:26:46 joe Exp
+*/
+
+/* A nice demo of hooks, since it doesn't need any external
+ * interface to muck with the stored states.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#include "http_request.h"
+#include "string_utils.h"
+#include "http_cookies.h"
+#include "xalloc.h"
+
+static void set_cookie_hdl(void *userdata, const char *value)
+{
+ char **pairs = pair_string(value, ';', '=', HTTP_QUOTES, HTTP_WHITESPACE);
+ http_cookie *cook;
+ http_cookie_cache *cache = userdata;
+ int n;
+
+ /* Check sanity */
+ if (pairs[0] == NULL || pairs[1] == NULL) {
+ /* yagaboo */
+ return;
+ }
+
+ DEBUG(DEBUG_HTTP, "Got cookie name=%s\n", pairs[0]);
+
+ /* Search for a cookie of this name */
+ DEBUG(DEBUG_HTTP, "Searching for existing cookie...\n");
+ for (cook = cache->cookies; cook != NULL; cook = cook->next) {
+ if (strcasecmp(cook->name, pairs[0]) == 0) {
+ break;
+ }
+ }
+
+ if (cook == NULL) {
+ DEBUG(DEBUG_HTTP, "New cookie.\n");
+ cook = xmalloc(sizeof(http_cookie));
+ memset(cook, 0, sizeof(http_cookie));
+ cook->name = pairs[0];
+ cook->next = cache->cookies;
+ cache->cookies = cook;
+ } else {
+ /* Free the old value */
+ free(cook->value);
+ }
+
+ cook->value = pairs[1];
+
+ for (n = 2; pairs[n] != NULL; n+=2) {
+ DEBUG(DEBUG_HTTP, "Cookie parm %s=%s\n", pairs[n], pairs[n+1]);
+ if (strcasecmp(pairs[n], "path") == 0) {
+ cook->path = pairs[n+1];
+ pairs[n+1] = NULL;
+ } else if (strcasecmp(pairs[n], "max-age") == 0) {
+ int t = atoi(pairs[n+1]);
+ cook->expiry = time(NULL) + (time_t)t;
+ } else if (strcasecmp(pairs[n], "domain") == 0) {
+ cook->domain = pairs[n+1];
+ pairs[n+1] = NULL;
+ }
+ }
+
+ DEBUG(DEBUG_HTTP, "End of parms.\n");
+
+ pair_string_free(pairs);
+}
+
+static void *create(void *session, http_req *req, const char *method, const char *uri)
+{
+ http_cookie_cache *cache = session;
+ http_add_response_header_handler(req, "Set-Cookie", set_cookie_hdl, cache);
+ return cache;
+}
+
+/* Just before sending the request: let them add headers if they want */
+static void pre_send(void *private, sbuffer request)
+{
+ http_cookie_cache *cache = private;
+ http_cookie *cook;
+
+ if (cache->cookies == NULL) {
+ return;
+ }
+
+ sbuffer_zappend(request, "Cookie: ");
+
+ for (cook = cache->cookies; cook != NULL; cook=cook->next) {
+ sbuffer_concat(request, cook->name, "=", cook->value, NULL);
+ if (cook->next != NULL) {
+ sbuffer_zappend(request, "; ");
+ }
+ }
+
+ sbuffer_zappend(request, EOL);
+
+}
+
+static void destroy(void *private)
+{
+ /* FIXME */
+ return;
+}
+
+http_request_hooks http_cookie_hooks = {
+ "http://www.webdav.org/neon/hooks/cookies",
+ create,
+ NULL,
+ pre_send,
+ NULL,
+ destroy
+};
diff --git a/neon/src/http_cookies.h b/neon/src/http_cookies.h
new file mode 100644
index 000000000..eb9274601
--- /dev/null
+++ b/neon/src/http_cookies.h
@@ -0,0 +1,43 @@
+/*
+ HTTP Request Handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef COOKIES_H
+#define COOKIES_H
+
+struct http_cookie_s;
+typedef struct http_cookie_s http_cookie;
+
+struct http_cookie_s {
+ char *name, *value;
+ unsigned int secure:1;
+ unsigned int discard:1;
+ char *domain, *path;
+ time_t expiry; /* time at which the cookie expires */
+ http_cookie *next;
+};
+
+typedef struct {
+ http_cookie *cookies;
+} http_cookie_cache;
+
+extern http_request_hooks http_cookie_hooks;
+
+#endif /* COOKIES_H */
diff --git a/neon/src/http_private.h b/neon/src/http_private.h
new file mode 100644
index 000000000..bea8e51eb
--- /dev/null
+++ b/neon/src/http_private.h
@@ -0,0 +1,176 @@
+/*
+ HTTP Request Handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* THIS IS NOT A PUBLIC INTERFACE. You CANNOT include this header file
+ * from an application. */
+
+#ifndef HTTP_PRIVATE_H
+#define HTTP_PRIVATE_H
+
+#include "http_auth.h"
+
+struct host_info {
+ const char *hostname;
+ int port;
+ struct in_addr addr;
+ char *hostport; /* URI hostport segment */
+ http_auth_session auth;
+ http_request_auth auth_callback;
+ void *auth_userdata;
+};
+
+typedef enum {
+ body_buffer,
+ body_stream,
+ body_none
+} request_body;
+
+/* This is called with each of the headers in the response */
+struct header_handler {
+ const char *name;
+ http_header_handler handler;
+ void *userdata;
+ struct header_handler *next;
+};
+
+/* TODO: could unify these all into a generic callback list */
+
+struct body_reader {
+ http_block_reader handler;
+ http_accept_response accept_response;
+ unsigned int use:1;
+ void *userdata;
+ struct body_reader *next;
+};
+
+/* Store hook information */
+struct hook {
+ http_request_hooks *hooks;
+ void *private;
+ struct hook *next;
+};
+
+/* Per-request store for hooks.
+ * This is a bit noddy really. */
+struct hook_request {
+ struct hook *hook;
+ void *cookie;
+ struct hook_request *next;
+};
+
+#define HAVE_HOOK(st,func) (st->hook->hooks->func != NULL)
+#define HOOK_FUNC(st, func) (*st->hook->hooks->func)
+
+/* Session support. */
+struct http_session_s {
+ /* Connection information */
+ nsocket *socket;
+
+ struct host_info server, proxy;
+
+ /* Connection states:
+ * 0: Not connected at all.
+ * 1: We have a TCP connection to the next-hop server.
+ * 2: We have a negotiated an SSL connection over the proxy's
+ * TCP tunnel.
+ *
+ * Note, 1 is all we need if we don't have a proxy server, or
+ * if we do have a proxy server and we're not using SSL.
+ */
+ unsigned int connected:2;
+
+ /* Settings */
+ unsigned int have_proxy:1; /* do we have a proxy server? */
+ unsigned int no_persist:1; /* set to disable persistent connections */
+ unsigned int use_secure:1; /* whether a secure connection is required */
+ int expect100_works:2; /* known state of 100-continue support */
+ unsigned int in_connect:1; /* doing a proxy CONNECT */
+ unsigned int request_secure_upgrade:1;
+ unsigned int accept_secure_upgrade:1;
+
+ http_use_proxy proxy_decider;
+ void *proxy_decider_udata;
+
+ nssl_context *ssl_context;
+
+ struct hook *hooks;
+
+ char *location; /* 302 redirect location of last request */
+
+ char *user_agent; /* full User-Agent string */
+
+ /* The last HTTP-Version returned by the server */
+ int version_major;
+ int version_minor;
+
+ /* Error string */
+ char error[BUFSIZ];
+};
+
+struct http_req_s {
+ const char *method;
+ char *uri, *abs_path;
+
+ /*** Request ***/
+
+ sbuffer headers;
+ request_body body;
+ FILE *body_stream;
+ const char *body_buffer;
+ size_t body_size;
+
+ /**** Response ***/
+
+ /* The transfer encoding types */
+ struct http_response {
+ unsigned int is_chunked; /* Are we using chunked TE? */
+ int length; /* Response entity-body content-length */
+ int left; /* Bytes left to read */
+ long int chunk_left; /* Bytes of chunk left to read */
+ } resp;
+
+ /* List of callbacks which are passed response headers */
+ struct header_handler *header_handlers;
+ /* List of callbacks which are passed response body blocks */
+ struct body_reader *body_readers;
+
+ /*** Miscellaneous ***/
+ unsigned int method_is_head:1;
+ unsigned int use_proxy:1;
+ unsigned int use_expect100:1;
+ unsigned int can_persist:1;
+ unsigned int forced_close:1;
+ unsigned int upgrade_to_tls:1;
+
+ http_session *session;
+ http_status *status; /* TODO: get rid of this */
+
+ /* stores request-private hook info */
+ struct hook_request *hook_store;
+
+#ifdef USE_DAV_LOCKS
+ /* TODO: move this to hooks... list of locks to submit */
+ struct dav_submit_locks *if_locks;
+#endif
+
+};
+
+#endif /* HTTP_PRIVATE_H */
diff --git a/neon/src/http_redirect.c b/neon/src/http_redirect.c
new file mode 100644
index 000000000..97745da69
--- /dev/null
+++ b/neon/src/http_redirect.c
@@ -0,0 +1,147 @@
+/*
+ HTTP-redirect support
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: http_redirect.c,v 1.4 2000/08/12 15:34:40 joe Exp
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "http_request.h"
+#include "ne_alloc.h"
+#include "http_private.h"
+#include "http_redirect.h"
+#include "uri.h"
+#include "neon_i18n.h"
+
+struct redirect {
+ char *location;
+ http_req *req;
+ http_redirect_confirm confirm;
+ http_redirect_notify notify;
+ void *userdata;
+};
+
+static void *create(void *session, http_req *req,
+ const char *method, const char *uri);
+static int post_send(void *private, http_status *status);
+static void destroy(void *private);
+
+http_request_hooks redirect_hooks = {
+ "http://www.webdav.org/neon/hooks/http-redirect",
+ create,
+ NULL,
+ NULL,
+ post_send,
+ destroy
+};
+
+static void *
+create(void *session, http_req *req, const char *method, const char *uri)
+{
+ struct redirect *red = session;
+
+ /* for handling 3xx redirects */
+ http_add_response_header_handler(req, "Location",
+ http_duplicate_header, &red->location);
+
+ red->req = req;
+
+ return red;
+}
+
+/* 2616 says we can't auto-redirect if the method is not GET or HEAD.
+ * We extend this to PROPFIND too, which violates a 2616 MUST, but
+ * is following the spirit of the spec, I think. */
+static int auto_redirect(struct redirect *red)
+{
+ return (red->req->method_is_head ||
+ strcasecmp(red->req->method, "GET") == 0 ||
+ strcasecmp(red->req->method, "PROPFIND") == 0);
+}
+
+static int post_send(void *private, http_status *status)
+{
+ struct redirect *red = private;
+ struct uri uri;
+
+ if ((status->code != 302 && status->code != 301) ||
+ red->location == NULL) {
+ /* Nothing to do. */
+ return HTTP_OK;
+ }
+
+ if (uri_parse(red->location, &uri, NULL)) {
+ /* Couldn't parse the URI */
+ http_set_error(red->req->session,
+ _("Could not parse redirect location."));
+ return HTTP_ERROR;
+ }
+
+ DEBUG(DEBUG_HTTP, "Redirect to hostname: %s, absPath: %s\n",
+ uri.host, uri.path);
+
+ if (strcasecmp(uri.host, red->req->session->server.hostname) != 0) {
+ /* Don't allow redirects to a different server */
+ return HTTP_OK;
+ }
+
+ if (auto_redirect(red)) {
+ if (red->notify != NULL) {
+ (*red->notify)(red->userdata, red->req->abs_path, uri.path);
+ }
+ } else {
+ /* Need user-confirmation to follow the redirect */
+ if (red->confirm == NULL ||
+ !(*red->confirm)(red->userdata, red->req->abs_path, uri.path)) {
+ return HTTP_OK;
+ }
+ }
+
+ /* FIXME FIXME: handle red->req->uri too */
+ red->req->abs_path = ne_strdup(uri.path);
+ return HTTP_RETRY;
+}
+
+static void destroy(void *private)
+{
+ struct redirect *red = private;
+ HTTP_FREE(red->location);
+ free(red);
+}
+
+void http_redirect_register(http_session *sess,
+ http_redirect_confirm confirm,
+ http_redirect_notify notify,
+ void *userdata)
+{
+ struct redirect *red = ne_calloc(sizeof *red);
+
+ red->confirm = confirm;
+ red->notify = notify;
+ red->userdata = userdata;
+
+ http_add_hooks(sess, &redirect_hooks, red);
+}
diff --git a/neon/src/http_redirect.h b/neon/src/http_redirect.h
new file mode 100644
index 000000000..688d2753e
--- /dev/null
+++ b/neon/src/http_redirect.h
@@ -0,0 +1,60 @@
+/*
+ HTTP-redirect support
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: http_redirect.h,v 1.4 2000/08/12 16:04:10 joe Exp
+*/
+
+#ifndef HTTP_REDIRECT_H
+#define HTTP_REDIRECT_H
+
+#include <http_request.h>
+
+/* Get confirmation from the user that a redirect from
+ * URI 'src' to URI 'dest' is acceptable. Should return:
+ * Non-Zero to FOLLOW the redirect
+ * Zero to NOT follow the redirect
+ */
+typedef int (*http_redirect_confirm)(void *userdata,
+ const char *src, const char *dest);
+
+/* Notify the user that a redirect has been automatically
+ * followed from URI 'src' to URI 'dest' */
+typedef void (*http_redirect_notify)(void *userdata,
+ const char *src, const char *dest);
+
+/* Register redirect handling for the given session.
+ * Some redirect responses will be automatically followed.
+ * If the redirect is automatically followed, the 'notify' callback
+ * is called.
+ * For redirects which are NOT automatically followed, the
+ * 'confirm' callback is called: if this returns zero, the redirect
+ * is ignored.
+ *
+ * 'confirm' may be passed as NULL: in this case, only automatic
+ * redirects are followed. 'notify' may also be passed as NULL,
+ * automatic redirects are still followed.
+ *
+ * 'userdata' is passed as the first argument to the confirm and
+ * notify callbacks. */
+void http_redirect_register(http_session *sess,
+ http_redirect_confirm confirm,
+ http_redirect_notify notify,
+ void *userdata);
+
+#endif /* HTTP_REDIRECT_H */
diff --git a/neon/src/http_request.c b/neon/src/http_request.c
new file mode 100644
index 000000000..b14c6cde0
--- /dev/null
+++ b/neon/src/http_request.c
@@ -0,0 +1,1306 @@
+/*
+ HTTP request/response handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: http_request.c,v 1.24 2000/05/10 13:24:08 joe Exp
+*/
+
+/* This is the HTTP client request/response implementation.
+ * The goal of this code is to be modular and simple.
+ */
+
+/* TODO:
+ * - Implement request hooks
+ * - Move DAV locks into a hook
+ * - Move authentication into a hook
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __EMX__
+#include <sys/select.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifndef HAVE_SNPRINTF
+#include <snprintf.h>
+#endif
+
+#include <netinet/in.h>
+
+#include "neon_i18n.h"
+
+#include "xalloc.h"
+#include "http_request.h"
+#include "http_auth.h"
+#include "socket.h"
+#include "string_utils.h" /* for sbuffer */
+#include "http_utils.h"
+#include "uri.h"
+#include "neon.h" /* for NEON_VERSION */
+
+struct host_info {
+ const char *hostname;
+ int port;
+ struct in_addr addr;
+ char *hostport; /* URI hostport segment */
+ http_auth_session auth;
+ http_request_auth auth_callback;
+ void *auth_userdata;
+};
+
+typedef enum {
+ body_buffer,
+ body_stream,
+ body_none
+} request_body;
+
+/* This is called with each of the headers in the response */
+struct header_handler {
+ const char *name;
+ http_header_handler handler;
+ void *userdata;
+ struct header_handler *next;
+};
+
+/* TODO: could unify these all into a generic callback list */
+
+struct body_reader {
+ http_block_reader handler;
+ http_accept_response accept_response;
+ unsigned int use:1;
+ void *userdata;
+ struct body_reader *next;
+};
+
+/* Session support. */
+struct http_session_s {
+ /* Connection information */
+ int socket;
+
+ struct host_info server, proxy;
+
+ unsigned int connected:1;
+
+ /* Settings */
+ unsigned int have_proxy:1; /* do we have a proxy server? */
+ unsigned int no_persist:1; /* set to disable persistent connections */
+ int expect100_works:2; /* known state of 100-continue support */
+
+ char *user_agent; /* full User-Agent string */
+
+ /* The last HTTP-Version returned by the server */
+ int version_major;
+ int version_minor;
+
+ /* Error string */
+ char error[BUFSIZ];
+};
+
+struct hook {
+ http_request_hooks *handler;
+ void *private;
+ struct hook *next;
+};
+
+struct http_req_s {
+ /* Fill in these with http_req_init */
+ const char *method;
+ char *uri;
+ char *abs_path;
+
+ /*** Request ***/
+
+ sbuffer headers;
+ /* Set these if you want to send a request body */
+ request_body body;
+ FILE *body_stream;
+ const char *body_buffer;
+ size_t body_size;
+
+ /**** Response ***/
+
+ /* The transfer encoding types */
+ struct http_response {
+ enum {
+ te_none,
+ te_chunked,
+ te_unknown
+ } te;
+ int length; /* Response entity-body content-length */
+ int left; /* Bytes left to read */
+ long int chunk_left; /* Bytes of chunk left to read */
+ } resp;
+
+ struct header_handler *header_handlers;
+ struct body_reader *body_readers;
+
+ /*** Miscellaneous ***/
+ unsigned int method_is_head:1;
+ unsigned int use_proxy:1;
+ unsigned int use_expect100:1;
+ unsigned int can_persist:1;
+ unsigned int forced_close:1;
+
+ http_session *session;
+ http_status *status; /* TODO: get rid of this */
+
+#ifdef USE_DAV_LOCKS
+ /* TODO: move this to hooks... list of locks to submit */
+ struct dav_submit_locks *if_locks;
+#endif
+
+};
+#define HTTP_PORT 80
+
+#define HTTP_EXPECT_TIMEOUT 15
+/* 100-continue only used if size > HTTP_EXPECT_MINSIZ */
+#define HTTP_EXPECT_MINSIZE 1024
+
+#define HTTP_VERSION_PRE11(s) \
+((s)->version_major<1 || ((s)->version_major==1 && (s)->version_minor<1))
+
+#define HTTP_MAXIMUM_HEADER_LENGTH 8192
+
+#define NEON_USERAGENT "neon/" NEON_VERSION;
+
+static void te_hdr_handler( void *userdata, const char *value );
+static void connection_hdr_handler( void *userdata, const char *value );
+
+static void set_hostinfo( struct host_info *info, const char *hostname, int port );
+static int lookup_host( struct host_info *info );
+
+static int open_connection( http_req *req );
+static int close_connection( http_session *req );
+
+static int set_sockerr( http_req *req, const char *doing, int sockerr );
+
+static char *get_hostport( struct host_info *host);
+static void add_fixed_headers( http_req *req );
+static int get_request_bodysize( http_req *req );
+
+static int send_request_body( http_req *req );
+static void build_request( http_req *req, sbuffer buf );
+static int read_message_header( http_req *req, sbuffer buf );
+static int read_response_block( http_req *req, struct http_response *resp,
+ char *buffer, size_t *buflen );
+static int read_response_body( http_req *req, http_status *status );
+
+/* Initializes an HTTP session */
+http_session *http_session_init( void )
+{
+ http_session *sess = xmalloc(sizeof(http_session));
+ DEBUG( DEBUG_HTTP, "HTTP session begins.\n" );
+ strcpy( sess->error, "Unknown error." );
+ memset( sess, 0, sizeof(struct http_session_s) );
+ sess->version_major = -1;
+ sess->version_minor = -1;
+ return sess;
+}
+
+static void
+set_hostinfo( struct host_info *info, const char *hostname, int port )
+{
+ HTTP_FREE( info->hostport );
+ info->hostname = hostname;
+ info->port = port;
+ info->hostport = get_hostport( info );
+ http_auth_init( &info->auth );
+}
+
+static int lookup_host( struct host_info *info )
+{
+ if( host_lookup( info->hostname, &info->addr ) ) {
+ return HTTP_LOOKUP;
+ } else {
+ return HTTP_OK;
+ }
+}
+
+int http_version_pre_http11( http_session *sess )
+{
+ return HTTP_VERSION_PRE11(sess);
+}
+
+int http_session_server( http_session *sess, const char *hostname, int port )
+{
+ set_hostinfo( &sess->server, hostname, port );
+ if( !sess->have_proxy ) {
+ return lookup_host( &sess->server );
+ } else {
+ return HTTP_OK;
+ }
+}
+
+int http_session_proxy( http_session *sess, const char *hostname, int port )
+{
+ sess->have_proxy = 1;
+ set_hostinfo( &sess->proxy, hostname, port );
+ return lookup_host( &sess->proxy );
+}
+
+void http_set_server_auth( http_session *sess,
+ http_request_auth callback, void *userdata )
+{
+ sess->server.auth_callback = callback;
+ sess->server.auth_userdata = userdata;
+}
+
+/* Set callback to handle proxy authentication */
+void http_set_proxy_auth( http_session *sess,
+ http_request_auth callback, void *userdata )
+{
+ sess->proxy.auth_callback = callback;
+ sess->proxy.auth_userdata = userdata;
+}
+
+void http_set_error( http_session *sess, const char *errstring )
+{
+ strncpy( sess->error, errstring, BUFSIZ );
+ sess->error[BUFSIZ-1] = '\0';
+ STRIP_EOL( sess->error );
+}
+
+const char *http_get_error( http_session *sess ) {
+ return sess->error;
+}
+
+/* Give authentication credentials */
+static int give_creds( void *udata, const char *realm,
+ char **username, char **password )
+{
+ http_req *req = udata;
+ http_session *sess = req->session;
+ if( req->status->code == 407 && req->use_proxy && sess->proxy.auth_callback ) {
+ return (*sess->proxy.auth_callback)(
+ sess->proxy.auth_userdata, realm, sess->proxy.hostname,
+ username, password );
+ } else if( req->status->code == 401 && sess->server.auth_callback ) {
+ return (*sess->server.auth_callback)(
+ sess->server.auth_userdata, realm, sess->server.hostname,
+ username, password );
+ }
+ return -1;
+}
+
+void http_duplicate_header( void *userdata, const char *value )
+{
+ char **location = userdata;
+ *location = xstrdup( value );
+}
+
+void http_handle_numeric_header( void *userdata, const char *value )
+{
+ int *location = userdata;
+ *location = atoi( value );
+}
+
+/* The body reader callback */
+static void auth_body_reader( void *userdata, const char *block, size_t length )
+{
+ http_auth_session *sess = userdata;
+ http_auth_response_body( sess, block, length );
+}
+
+int http_session_finish( http_session *sess ) {
+ DEBUG( DEBUG_HTTP, "http_session_finish called.\n" );
+ http_auth_finish( &sess->server.auth );
+ if( sess->have_proxy ) {
+ http_auth_finish( &sess->proxy.auth );
+ }
+ HTTP_FREE( sess->server.hostport );
+ HTTP_FREE( sess->proxy.hostport );
+ HTTP_FREE( sess->user_agent );
+ if( sess->connected ) close_connection(sess);
+ free( sess );
+ return HTTP_OK;
+}
+
+/* Sends the body down the socket.
+ * Returns HTTP_* code */
+int send_request_body( http_req *req ) {
+ int ret;
+ switch( req->body ) {
+ case body_stream:
+ ret = sock_transfer( fileno(req->body_stream), req->session->socket,
+ req->body_size );
+ DEBUG( DEBUG_HTTP, "Sent %d bytes.\n", ret );
+ rewind( req->body_stream ); /* since we may have to send it again */
+ break;
+ case body_buffer:
+ DEBUG( DEBUG_HTTP, "Sending body:\n%s\n", req->body_buffer );
+ ret = sock_send_string( req->session->socket, req->body_buffer );
+ DEBUG( DEBUG_HTTP, "sock_send_string returns: %d\n", ret );
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if( ret < 0 ) {
+ /* transfer failed */
+ req->forced_close = 1;
+ return set_sockerr( req, _("Could not send request body"), ret );
+ } else {
+ return HTTP_OK;
+ }
+}
+
+/* Deal with the body size.
+ * Returns 0 on success or non-zero on error. */
+static int get_request_bodysize( http_req *req )
+{
+ struct stat bodyst;
+ /* Do extra stuff if we have a body */
+ switch( req->body ) {
+ case body_stream:
+ /* Get file length */
+ if( fstat( fileno(req->body_stream), &bodyst ) < 0 ) {
+ /* Stat failed */
+ DEBUG( DEBUG_HTTP, "Stat failed: %s\n", strerror( errno ) );
+ return -1;
+ }
+ req->body_size = bodyst.st_size;
+ break;
+ case body_buffer:
+ req->body_size = strlen( req->body_buffer );
+ break;
+ default:
+ /* No body, so no size. */
+ break;
+ }
+ if( req->body != body_none) {
+ char tmp[BUFSIZ];
+ /* Add the body length header */
+ snprintf( tmp, BUFSIZ, "Content-Length: %d" EOL, req->body_size );
+ sbuffer_zappend( req->headers, tmp );
+ } else {
+ sbuffer_zappend( req->headers, "Content-Length: 0" EOL );
+ }
+ return 0;
+}
+
+static char *get_hostport( struct host_info *host)
+{
+ size_t len = strlen( host->hostname );
+ char *ret = xmalloc( len + 10 );
+ strcpy( ret, host->hostname );
+ if( host->port != HTTP_PORT ) {
+ snprintf( ret + len, 9, ":%d", host->port );
+ }
+ return ret;
+}
+
+const char *http_get_server_hostport( http_session *sess ) {
+ return sess->server.hostport;
+}
+
+
+/* Lob the User-Agent, connection and host headers in to the request
+ * headers */
+static void add_fixed_headers( http_req *req ) {
+ if( req->session->user_agent ) {
+ sbuffer_concat( req->headers,
+ "User-Agent: ", req->session->user_agent, EOL, NULL );
+ }
+ /* Send Connection: Keep-Alive for pre-1.1 origin servers, so we
+ * might get a persistent connection. 2068 sec 19.7.1 says we
+ * MUST NOT do this for proxies, though. So we don't. */
+ if( HTTP_VERSION_PRE11(req->session) && !req->use_proxy ) {
+ sbuffer_zappend( req->headers, "Connection: Keep-Alive, TE" EOL );
+ sbuffer_zappend( req->headers, "Keep-Alive: " EOL );
+ } else {
+ sbuffer_zappend( req->headers, "Connection: TE" EOL );
+ }
+ /* We send TE: trailers since we understand trailers in the chunked
+ * response. */
+ sbuffer_concat( req->headers, "TE: trailers" EOL
+ "Host: ", req->session->server.hostport, EOL, NULL );
+}
+
+static int always_accept_response( void *userdata, http_req *req, http_status *st )
+{
+ return 1;
+}
+
+int http_accept_2xx( void *userdata, http_req *req, http_status *st )
+{
+ return (st->class == 2);
+}
+
+/* Initializes the request with given method and URI.
+ * URI must be abs_path - i.e., NO scheme+hostname. It will BREAK
+ * otherwise. */
+http_req *http_request_create( http_session *sess,
+ const char *method, const char *uri ) {
+ sbuffer real_uri;
+ http_req *req = xmalloc( sizeof(http_req) );
+
+ /* Clear it out */
+ memset( req, 0, sizeof( http_req ) );
+
+ DEBUG( DEBUG_HTTP, "Creating request...\n" );
+
+ req->session = sess;
+ req->headers = sbuffer_create();
+
+ /* Add in the fixed headers */
+ add_fixed_headers( req );
+
+ /* Set the standard stuff */
+ req->method = method;
+ req->method_is_head = (strcmp( req->method, "HEAD" ) == 0);
+ req->body = body_none;
+
+
+ /* TODO: Can do clever "should we use the proxy" discrimination
+ * here, e.g. by server hostname used. TODO: when we do that,
+ * then we might need to do a name lookup on the server here,
+ * since we omit that if we are using a proxy, normally. */
+ req->use_proxy = sess->have_proxy;
+
+ /* Add in standard callbacks */
+
+ http_auth_set_creds_cb( &sess->server.auth, give_creds, req );
+ http_add_response_body_reader( req, always_accept_response,
+ auth_body_reader, &req->session->server.auth );
+ if( req->use_proxy ) {
+ http_auth_set_creds_cb( &sess->proxy.auth, give_creds, req );
+ http_add_response_body_reader( req, always_accept_response,
+ auth_body_reader, &req->session->proxy.auth );
+ }
+
+ /* Add in handlers for all the standard HTTP headers. */
+
+ http_add_response_header_handler( req, "Content-Length",
+ http_handle_numeric_header, &req->resp.length );
+ http_add_response_header_handler( req, "Transfer-Encoding",
+ te_hdr_handler, &req->resp );
+ http_add_response_header_handler( req, "Connection",
+ connection_hdr_handler, req );
+
+ req->abs_path = uri_abspath_escape( uri );
+
+ real_uri = sbuffer_create();
+ if( req->use_proxy )
+ sbuffer_concat( real_uri, "http://",
+ req->session->server.hostport, NULL );
+ sbuffer_zappend( real_uri, req->abs_path );
+ req->uri = sbuffer_finish(real_uri);
+
+ DEBUG( DEBUG_HTTP, "Request created.\n" );
+
+ return req;
+}
+
+void http_set_request_body_buffer( http_req *req, const char *buffer )
+{
+ req->body = body_buffer;
+ req->body_buffer = buffer;
+ req->body_stream = NULL;
+}
+
+void http_set_request_body_stream( http_req *req, FILE *stream )
+{
+ req->body = body_stream;
+ req->body_stream = stream;
+ req->body_buffer = NULL;
+}
+
+void http_set_expect100( http_session *sess, int use_expect100 )
+{
+ if( use_expect100 ) {
+ sess->expect100_works = 1;
+ } else {
+ sess->expect100_works = -1;
+ }
+}
+
+void http_set_persist( http_session *sess, int persist )
+{
+ sess->no_persist = !persist;
+}
+
+void http_set_useragent( http_session *sess, const char *token )
+{
+ static const char *fixed = " " NEON_USERAGENT;
+ HTTP_FREE( sess->user_agent );
+ CONCAT2( sess->user_agent, token, fixed );
+}
+
+sbuffer http_get_request_header( http_req *req )
+{
+ return req->headers;
+}
+
+void
+http_add_response_header_handler( http_req *req, const char *name,
+ http_header_handler hdl, void *userdata )
+{
+ struct header_handler *new = xmalloc( sizeof(struct header_handler) );
+ new->name = name;
+ new->handler = hdl;
+ new->userdata = userdata;
+ new->next = req->header_handlers;
+ req->header_handlers = new;
+}
+
+void
+http_add_response_body_reader( http_req *req, http_accept_response acpt,
+ http_block_reader rdr, void *userdata )
+{
+ struct body_reader *new = xmalloc( sizeof(struct body_reader) );
+ new->accept_response = acpt;
+ new->handler = rdr;
+ new->userdata = userdata;
+ new->next = req->body_readers;
+ req->body_readers = new;
+}
+
+void http_request_destroy( http_req *req )
+{
+
+ HTTP_FREE( req->uri );
+ HTTP_FREE( req->abs_path );
+
+ sbuffer_destroy( req->headers );
+
+ DEBUG( DEBUG_HTTP, "Request ends.\n" );
+ free( req );
+}
+
+
+/* Reads a block of the response into buffer, which is of size buflen.
+ * Returns number of bytes read, 0 on end-of-response, or HTTP_* on error.
+ * TODO?: only make one actual read() call in here...
+ */
+static int read_response_block( http_req *req, struct http_response *resp,
+ char *buffer, size_t *buflen )
+{
+ int willread, readlen, socket = req->session->socket;
+ if( resp->te==te_chunked ) {
+ /* We are doing a chunked transfer-encoding.
+ * It goes: `SIZE CRLF CHUNK CRLF SIZE CRLF CHUNK CRLF ...'
+ * ended by a `CHUNK CRLF 0 CRLF', a 0-sized chunk.
+ * The slight complication is that we have to cope with
+ * partial reads of chunks.
+ * For this reason, resp.chunk_left contains the number of
+ * bytes left to read in the current chunk.
+ */
+ if( resp->chunk_left == 0 ) {
+ long int chunk_len;
+ /* We are at the start of a new chunk. */
+ DEBUG( DEBUG_HTTP, "New chunk.\n" );
+ readlen = sock_readline( socket, buffer, *buflen );
+ if( readlen <= 0 ) {
+ return set_sockerr( req, _("Could not read chunk size"), readlen );
+ }
+ DEBUG( DEBUG_HTTP, "[Chunk Size] < %s", buffer );
+ chunk_len = strtol( buffer, NULL, 16 );
+ if( chunk_len == LONG_MIN || chunk_len == LONG_MAX ) {
+ DEBUG( DEBUG_HTTP, "Couldn't read chunk size.\n" );
+ return -1;
+ }
+ DEBUG( DEBUG_HTTP, "Got chunk size: %ld\n", chunk_len );
+ if( chunk_len == 0 ) {
+ /* Zero-size chunk */
+ DEBUG( DEBUG_HTTP, "Zero-size chunk.\n" );
+ *buflen = 0;
+ return HTTP_OK;
+ }
+ resp->chunk_left = chunk_len;
+ }
+ willread = min( *buflen - 1, resp->chunk_left );
+ } else if( resp->length > 0 ) {
+ /* Have we finished reading the body? */
+ if( resp->left == 0 ) {
+ *buflen = 0;
+ return HTTP_OK;
+ }
+ willread = min( *buflen - 1, resp->left );
+ } else {
+ /* Read until socket-close */
+ willread = *buflen - 1;
+ }
+ DEBUG( DEBUG_HTTP, "Reading %d bytes of response body.\n", willread );
+ readlen = sock_read( socket, buffer, willread );
+ DEBUG( DEBUG_HTTP, "Got %d bytes.\n", readlen );
+ if( readlen < 0 ||
+ (readlen == 0 && (resp->length > 0 || resp->te==te_chunked)) ) {
+ return set_sockerr( req, _("Could not read response body"), readlen );
+ }
+ buffer[readlen] = '\0';
+ *buflen = readlen;
+ DEBUG( DEBUG_HTTPBODY, "Read block:\n%s\n", buffer );
+ if( resp->te==te_chunked ) {
+ resp->chunk_left -= readlen;
+ if( resp->chunk_left == 0 ) {
+ char crlfbuf[2];
+ /* If we've read a whole chunk, read a CRLF */
+ readlen = sock_fullread( socket, crlfbuf, 2 );
+ if( readlen < 0 || strncmp( crlfbuf, EOL, 2 ) != 0 ) {
+ return set_sockerr( req, _("Error reading chunked response body"),
+ readlen );
+ }
+ }
+ } else if( resp->length > 0 ) {
+ resp->left -= readlen;
+ }
+ return HTTP_OK;
+}
+
+/* Build a request string into the buffer.
+ * If we sent the data as we generated it, it's possible that multiple
+ * packets could go out on the wire, which is less efficient. */
+static void build_request( http_req *req, sbuffer buf )
+{
+ const char *uri;
+ char *tmp;
+
+ /* If we are talking to a proxy, we send them the absoluteURI
+ * as the Request-URI. If we are talking to a server, we just
+ * send abs_path. */
+ if( req->use_proxy )
+ uri = req->uri;
+ else
+ uri = req->abs_path;
+
+ sbuffer_clear( buf );
+
+ /* Add in the request and the user-supplied headers */
+ sbuffer_concat( buf, req->method, " ", uri, " HTTP/1.1" EOL,
+ sbuffer_data(req->headers), NULL );
+
+ /* Add the authorization headers in */
+ tmp = http_auth_request_header( &req->session->server.auth );
+ if( tmp != NULL ) {
+ sbuffer_concat( buf, "Authorization: ", tmp, NULL );
+ free( tmp );
+ }
+
+ if( req->use_proxy ) {
+ tmp = http_auth_request_header( &req->session->proxy.auth );
+ if( tmp != NULL ) {
+ sbuffer_concat( buf, "Proxy-Authorization: ", tmp, NULL );
+ free( tmp );
+ }
+ }
+
+ /* Now handle the body. */
+ req->use_expect100 = 0;
+ if( req->body!=body_none &&
+ (req->session->expect100_works > -1) &&
+ (req->body_size > HTTP_EXPECT_MINSIZE) &&
+ !HTTP_VERSION_PRE11(req->session) ) {
+ /* Add Expect: 100-continue. */
+ sbuffer_zappend( buf, "Expect: 100-continue" EOL );
+ req->use_expect100 = 1;
+ }
+
+}
+
+static int set_sockerr( http_req *req, const char *doing, int code )
+{
+ switch( code ) {
+ case 0:
+ if( req->use_proxy ) {
+ snprintf( req->session->error, BUFSIZ,
+ _("%s: connection was closed by proxy server."), doing );
+ } else {
+ snprintf( req->session->error, BUFSIZ,
+ _("%s: connection was closed by server."), doing );
+ }
+ return HTTP_ERROR;
+ case SOCK_TIMEOUT:
+ snprintf( req->session->error, BUFSIZ,
+ _("%s: connection timed out."), doing );
+ return HTTP_TIMEOUT;
+ default:
+ snprintf( req->session->error, BUFSIZ,
+ "%s: %s", doing, strerror(errno) );
+ return HTTP_ERROR;
+ }
+}
+
+/* TODO: The retry loop may be overkill. It may only be necessary to
+ * try sending the request >1 time; making the loop shorter. */
+static int send_request( http_req *req, const char *request,
+ sbuffer buf, http_status *status )
+{
+ http_session *sess = req->session;
+ int ret, try_again;
+
+ do {
+
+ try_again = 0;
+
+ /* Open the connection if necessary */
+ if( !sess->connected ) {
+ ret = open_connection( req );
+ if( ret != HTTP_OK ) {
+ return ret;
+ }
+ }
+
+#ifdef DEBUGGING
+ if( (DEBUG_HTTPPLAIN&debug_mask) == DEBUG_HTTPPLAIN ) {
+ /* Display everything mode */
+ DEBUG( DEBUG_HTTP, "Sending request headers:\n%s", request );
+ } else {
+ /* Blank out the Authorization paramaters */
+ char *reqdebug = xstrdup(request), *pnt = reqdebug;
+ while( (pnt = strstr( pnt, "Authorization: ")) != NULL ) {
+ for( pnt += 15; *pnt != '\r' && *pnt != '\0'; pnt++ ) {
+ *pnt = 'x';
+ }
+ }
+ DEBUG( DEBUG_HTTP, "Sending request headers:\n%s", reqdebug );
+ free( reqdebug );
+ }
+#endif /* DEBUGGING */
+
+ DEBUG( DEBUG_HTTP, "Request size: %d\n", strlen(request) );
+ /* Send the Request-Line and headers */
+ ret = sock_send_string( req->session->socket, request );
+ if( ret < 0 ) {
+ return set_sockerr( req, _("Could not send request"), ret );
+ }
+
+ DEBUG( DEBUG_HTTP, "Request sent\n" );
+
+ /* Now, if we are doing a Expect: 100, hang around for a short
+ * amount of time, to see if the server actually cares about the
+ * Expect and sends us a 100 Continue response if the request
+ * is valid, else an error code if it's not. This saves sending
+ * big files to the server when they will be rejected.
+ */
+
+ if( req->use_expect100 ) {
+ DEBUG( DEBUG_HTTP, "Waiting for response...\n" );
+ ret = sock_block( sess->socket, HTTP_EXPECT_TIMEOUT );
+ switch( ret ) {
+ case SOCK_TIMEOUT:
+ /* Timed out - i.e. Expect: ignored. There is a danger
+ * here that the server DOES respect the Expect: header,
+ * but was going SO slowly that it didn't get time to
+ * respond within HTTP_EXPECT_TIMEOUT.
+ * TODO: while sending the body, check to see if the
+ * server has sent anything back - if it HAS, then
+ * stop sending - this is a spec compliance SHOULD */
+ DEBUG( DEBUG_HTTP, "Wait timed out.\n" );
+ sess->expect100_works = -1; /* don't try that again */
+ /* Try sending the request again without using 100-continue */
+ try_again = 1;
+ break;
+ case SOCK_ERROR: /* error */
+ return set_sockerr( req, _("Error waiting for response"), ret );
+ default:
+ DEBUG( DEBUG_HTTP, "Wait got data.\n" );
+ sess->expect100_works = 1; /* it works - use it again */
+ break;
+ }
+ } else if( req->body != body_none ) {
+ /* Just chuck the file down the socket */
+ DEBUG( DEBUG_HTTP, "Sending body...\n" );
+ ret = send_request_body( req );
+ if( ret != HTTP_OK ) return ret;
+ DEBUG( DEBUG_HTTP, "Body sent.\n" );
+ }
+
+ /* Now, we have either:
+ * - Sent the header and body, or
+ * - Sent the header incl. Expect: line, and got some response.
+ * In any case, we get the status line of the response.
+ */
+
+ /* HTTP/1.1 says that the server MAY emit any number of
+ * interim 100 (Continue) responses prior to the normal
+ * response. So loop while we get them. */
+
+ do {
+ if( sock_readline( sess->socket, sbuffer_data(buf), BUFSIZ ) <= 0 ) {
+ if( try_again ) {
+ return set_sockerr( req, _("Could not read status line"), ret );
+ }
+ try_again = 1;
+ break;
+ }
+
+ DEBUG( DEBUG_HTTP, "[Status Line] < %s", sbuffer_data(buf) );
+
+ /* Got the status line - parse it */
+ if( http_parse_statusline( sbuffer_data(buf), status ) ) {
+ http_set_error( sess, "Could not parse response status line." );
+ return -1;
+ }
+
+ sess->version_major = status->major_version;
+ sess->version_minor = status->minor_version;
+ snprintf( sess->error, BUFSIZ, "%d %s",
+ status->code, status->reason_phrase );
+ STRIP_EOL( sess->error );
+
+ if( status->class == 1 ) {
+ DEBUG( DEBUG_HTTP, "Got 1xx-class.\n" );
+ /* Skip any headers, we don't need them */
+ do {
+ ret = sock_readline( sess->socket, sbuffer_data(buf), BUFSIZ );
+ if( ret <= 0 ) {
+ return set_sockerr(
+ req, _("Error reading response headers"), ret );
+ }
+ DEBUG( DEBUG_HTTP, "[Ignored header] < %s",
+ sbuffer_data(buf) );
+ } while( strcmp( sbuffer_data(buf), EOL ) != 0 );
+
+ if( req->use_expect100 && (status->code == 100) ) {
+ /* We are using Expect: 100, and we got a 100-continue
+ * return code... send the request body */
+ DEBUG( DEBUG_HTTP, "Got continue... sending body now.\n" );
+ if( send_request_body( req ) != HTTP_OK )
+ return HTTP_ERROR;
+
+ DEBUG( DEBUG_HTTP, "Body sent.\n" );
+ }
+ }
+ } while( status->class == 1 );
+
+ if( try_again ) {
+ /* If we're trying again, close the conn first */
+ DEBUG( DEBUG_HTTP, "Retrying request, closing connection first.\n" );
+ close_connection( sess );
+ }
+
+ } while( try_again );
+
+ return HTTP_OK;
+}
+
+/* Read a message header from sock into buf.
+ * Returns an HTTP_* code.
+ */
+static int read_message_header( http_req *req, sbuffer buf )
+{
+ char extra[BUFSIZ], *pnt;
+ int ret, sock = req->session->socket;
+
+ sbuffer_clear(buf);
+
+ ret = sock_readline( sock, sbuffer_data(buf), BUFSIZ );
+ if( ret <= 0 )
+ return set_sockerr( req, _("Error reading response headers"), ret );
+ DEBUG( DEBUG_HTTP, "[Header:%d] < %s",
+ strlen(sbuffer_data(buf)), sbuffer_data(buf) );
+ if( strcmp( sbuffer_data(buf), EOL ) == 0 ) {
+ DEBUG( DEBUG_HTTP, "CRLF: End of headers.\n" );
+ return HTTP_OK;
+ }
+ while(sbuffer_size(buf) < HTTP_MAXIMUM_HEADER_LENGTH ) {
+ /* Collect any extra lines into buffer */
+ ret = sock_recv( sock, extra, 1, MSG_PEEK);
+ if( ret <= 0 ) {
+ return set_sockerr( req, _("Error reading response headers"), ret );
+ }
+ if( extra[0] != ' ' && extra[0] != '\t' ) {
+ /* No more headers */
+ return HTTP_OK;
+ }
+ ret = sock_readline( sock, extra, BUFSIZ );
+ if( ret <= 0 ) {
+ return set_sockerr( req, _("Error reading response headers"), ret);
+ }
+ DEBUG( DEBUG_HTTP, "[Cont:%d] < %s", strlen(extra), extra);
+ /* Append a space to the end of the last header, in
+ * place of the CRLF. */
+ pnt = strchr( sbuffer_data(buf), '\r' );
+ pnt[0] = ' '; pnt[1] = '\0';
+ /* Skip leading whitespace off next line */
+ for( pnt = extra; *pnt!='\0' &&
+ ( *pnt == ' ' || *pnt =='\t' ); pnt++ ) /*oneliner*/;
+ DEBUG( DEBUG_HTTP, "[Continued] < %s", pnt );
+ sbuffer_altered( buf );
+ sbuffer_zappend( buf, pnt );
+ }
+ return HTTP_ERROR;
+}
+
+static void normalize_response_length( http_req *req, http_status *status )
+{
+ /* Response entity-body length calculation, bit icky.
+ * Here, we set:
+ * length==-1 if we DO NOT know the exact body length
+ * length>=0 if we DO know the body length.
+ *
+ * RFC2616, section 4.4:
+ * NO body is returned if the method is HEAD, or the resp status
+ * is 204 or 304
+ */
+ if( req->method_is_head || status->code==204 || status->code==304 ) {
+ req->resp.length = 0;
+ } else {
+ /* RFC2616, section 4.4: if we have a transfer encoding
+ * and a content-length, then ignore the content-length. */
+ if( (req->resp.length>-1) &&
+ (req->resp.te!=te_unknown) ) {
+ req->resp.length = -1;
+ }
+ }
+}
+
+/* Read response headers, using buffer buffer.
+ * Returns HTTP_* code. */
+static int read_response_headers( http_req *req, sbuffer buf )
+{
+ http_session *sess = req->session;
+ struct header_handler *hdl;
+ char *name, *value, *pnt, *hdr;
+ int ret;
+
+ /* Read response headers */
+ while(1) {
+ ret = read_message_header( req, buf );
+ if( ret != HTTP_OK ) return ret;
+
+ hdr = sbuffer_data(buf);
+
+ if( strcmp( hdr, EOL ) == 0 ) {
+ return HTTP_OK;
+ }
+
+ /* Now parse the header line. This is all a bit noddy. */
+ pnt = strchr( hdr, ':' );
+ if( pnt == NULL ) {
+ http_set_error( sess, "Malformed header line." );
+ return HTTP_ERROR;
+ }
+
+ /* Null-term name at the : */
+ *pnt = '\0';
+ name = hdr;
+ /* Strip leading whitespace from the value */
+ for( value = pnt+1; *value!='\0' && *value==' '; value++ )
+ /* nullop */;
+ STRIP_EOL( value );
+ DEBUG( DEBUG_HTTP, "Header Name: [%s], Value: [%s]\n",
+ name, value );
+ /* Iterate through the header handlers */
+ for( hdl = req->header_handlers; hdl != NULL; hdl = hdl->next ) {
+ if( strcasecmp( name, hdl->name ) == 0 ) {
+ (*hdl->handler)( hdl->userdata, value );
+ }
+ }
+ }
+
+ return HTTP_ERROR;
+}
+
+/* Read the response message body */
+static int read_response_body( http_req *req, http_status *status )
+{
+ char buffer[BUFSIZ];
+ int readlen, ret = HTTP_OK;
+ struct body_reader *rdr;
+
+ /* If there is nothing to do... */
+ if( req->resp.length == 0 ) {
+ /* Do nothing */
+ return HTTP_OK;
+ }
+
+ /* First off, tell all of the response body handlers that they are
+ * going to get a body, and let them work out whether they want to
+ * handle it or not */
+ for( rdr = req->body_readers; rdr != NULL; rdr=rdr->next ) {
+ rdr->use = (*rdr->accept_response)( rdr->userdata, req, status );
+ }
+
+ req->resp.left = req->resp.length;
+ req->resp.chunk_left = 0;
+
+ /* Now actually read the thing */
+
+ do {
+ /* Read a block */
+ readlen = BUFSIZ;
+ ret = read_response_block( req, &req->resp, buffer, &readlen );
+
+ /* TODO: Do we need to call them if readlen==0, or if
+ * readlen == -1, to tell them something has gone wrong? */
+
+ if( ret == HTTP_OK ) {
+ for( rdr = req->body_readers; rdr!=NULL; rdr=rdr->next ) {
+ if( rdr->use )
+ (*rdr->handler)( rdr->userdata, buffer, readlen );
+ }
+ }
+
+ } while( ret == HTTP_OK && readlen > 0 );
+
+ if( ret != HTTP_OK )
+ req->forced_close = 1;
+
+ return ret;
+}
+
+/* Handler for the "Transfer-Encoding" response header */
+static void te_hdr_handler( void *userdata, const char *value )
+{
+ struct http_response *resp = userdata;
+ if( strcasecmp( value, "chunked" ) == 0 ) {
+ resp->te = te_chunked;
+ } else {
+ resp->te = te_unknown;
+ }
+}
+
+/* Handler for the "Connection" response header */
+static void connection_hdr_handler( void *userdata, const char *value )
+{
+ http_req *req = userdata;
+ if( strcasecmp( value, "close" ) == 0 ) {
+ req->forced_close = 1;
+ } else if( strcasecmp( value, "Keep-Alive" ) == 0 ) {
+ req->can_persist = 1;
+ }
+}
+
+
+/* HTTP/1.x request/response mechanism
+ *
+ * Returns an HTTP_* return code.
+ *
+ * The status information is placed in status. The error string is
+ * placed in req->session->error
+ *
+ */
+int http_request_dispatch( http_req *req, http_status *status )
+{
+ http_session *sess = req->session;
+ sbuffer buf, request;
+ int ret, attempt, proxy_attempt, con_attempt, can_retry;
+ /* Response header storage */
+ char *www_auth, *proxy_auth, *authinfo, *proxy_authinfo;
+
+ /* Initialization... */
+ DEBUG( DEBUG_HTTP, "Request started...\n" );
+ http_set_error( sess, "Unknown error." );
+ ret = HTTP_OK;
+
+ if( get_request_bodysize( req ) )
+ return HTTP_ERROR;
+
+ buf = sbuffer_create_sized( BUFSIZ );
+
+ http_add_response_header_handler( req, "WWW-Authenticate",
+ http_duplicate_header, &www_auth );
+ http_add_response_header_handler( req, "Proxy-Authenticate",
+ http_duplicate_header, &proxy_auth );
+ http_add_response_header_handler( req, "Authentication-Info",
+ http_duplicate_header, &authinfo );
+ http_add_response_header_handler( req, "Proxy-Authentication-Info",
+ http_duplicate_header, &proxy_authinfo );
+ req->status = status;
+
+ request = sbuffer_create();
+ proxy_attempt = con_attempt = attempt = 1;
+ www_auth = proxy_auth = authinfo = proxy_authinfo = NULL;
+
+ /* Loop sending the request:
+ * Retry whilst authentication fails and we supply it. */
+
+ do {
+
+ can_retry = 0;
+ req->can_persist = 0;
+ req->forced_close = 0;
+
+ /* Note that we pass the abs_path here... */
+ http_auth_new_request( &sess->server.auth, req->method, req->uri,
+ req->body_buffer, req->body_stream );
+ if( req->use_proxy ) {
+ /* ...and absoluteURI here. */
+ http_auth_new_request( &sess->proxy.auth, req->method, req->uri,
+ req->body_buffer, req->body_stream );
+ }
+
+ build_request( req, request );
+
+#ifdef USE_DAV_LOCKS
+ /* TODO: Move this into a hook structure */
+ {
+ char *tmp = dav_lock_ifheader( req );
+ if( tmp != NULL ) {
+ sbuffer_zappend( request, tmp );
+ free( tmp );
+ if( HTTP_VERSION_PRE11(sess) ) {
+ /* HTTP/1.0 */
+ sbuffer_zappend( request, "Pragma: no-cache" EOL );
+ } else {
+ /* HTTP/1.1 and above */
+ sbuffer_zappend( request, "Cache-Control: no-cache" EOL );
+ }
+ }
+ }
+#endif /* USE_DAV_LOCKS */
+
+ /* Final CRLF */
+ sbuffer_zappend( request, EOL );
+
+ /* Now send the request, and read the Status-Line */
+ ret = send_request( req, sbuffer_data(request), buf, status );
+ if( ret != HTTP_OK ) goto dispatch_error;
+
+ req->resp.length = -1;
+ req->resp.te = te_unknown;
+
+ /* Read the headers */
+ if( read_response_headers( req, buf ) != HTTP_OK ) {
+ ret = HTTP_ERROR;
+ goto dispatch_error;
+ }
+
+ normalize_response_length( req, status );
+
+ ret = read_response_body( req, status );
+ if( ret != HTTP_OK ) goto dispatch_error;
+
+ /* Read headers in chunked trailers */
+ if( req->resp.te == te_chunked ) {
+ ret = read_response_headers( req, buf );
+ if( ret != HTTP_OK ) goto dispatch_error;
+ }
+
+ if( proxy_authinfo != NULL &&
+ http_auth_verify_response( &sess->proxy.auth, proxy_authinfo ) ) {
+ DEBUG( DEBUG_HTTP, "Proxy response authentication invalid.\n" );
+ ret = HTTP_SERVERAUTH;
+ http_set_error( sess, _("Proxy server was not authenticated correctly.") );
+ } else if( authinfo != NULL &&
+ http_auth_verify_response( &sess->server.auth, authinfo ) ) {
+ DEBUG( DEBUG_HTTP, "Response authenticated as invalid.\n" );
+ ret = HTTP_PROXYAUTH;
+ http_set_error( sess, _("Server was not authenticated correctly.") );
+ } else if( status->code == 401 && www_auth != NULL && attempt++ == 1) {
+ if( !http_auth_challenge( &sess->server.auth, www_auth ) ) {
+ can_retry = 1;
+ }
+ } else if( status->code == 407 && proxy_auth != NULL && proxy_attempt++ == 1 ) {
+ if( !http_auth_challenge( &sess->proxy.auth, proxy_auth ) ) {
+ can_retry = 1;
+ }
+ }
+
+ HTTP_FREE( www_auth );
+ HTTP_FREE( proxy_auth );
+ HTTP_FREE( authinfo );
+ HTTP_FREE( proxy_authinfo );
+
+ /* Close the connection if one of the following is true:
+ * - We have a forced close (e.g. "Connection: close" header)
+ * - We are not using persistent connections for this session
+ * - this is HTTP/1.0, and they haven't said they can do
+ * persistent connections
+ */
+ if( req->forced_close || sess->no_persist ||
+ (HTTP_VERSION_PRE11(sess) && !req->can_persist ) ) {
+ close_connection(sess);
+ }
+
+ /* Retry it if we had an auth challenge */
+
+ } while( can_retry );
+
+ DEBUG( DEBUG_HTTP | DEBUG_FLUSH,
+ "Request ends, status %d class %dxx, error line:\n%s\n",
+ status->code, status->class, sess->error );
+ DEBUG( DEBUG_HTTPBASIC, "Response: %d %s", status->code, sess->error );
+
+ switch( status->code ) {
+ case 401:
+ ret = HTTP_AUTH;
+ break;
+ case 407:
+ ret = HTTP_AUTHPROXY;
+ break;
+ default:
+ break;
+ }
+
+dispatch_error:
+
+ sbuffer_destroy(request);
+
+ HTTP_FREE( www_auth );
+ HTTP_FREE( proxy_auth );
+ HTTP_FREE( authinfo );
+ HTTP_FREE( proxy_authinfo );
+
+ return ret;
+}
+
+static int open_connection( http_req *req ) {
+ http_session *sess = req->session;
+ if( req->use_proxy ) {
+ DEBUG( DEBUG_SOCKET, "Connecting to proxy at %s:%d...\n",
+ sess->proxy.hostname, sess->proxy.port );
+ sess->socket = sock_connect( sess->proxy.addr, sess->proxy.port );
+ if( sess->socket < 0 ) {
+ (void) set_sockerr( req, _("Could not connect to proxy server"), -1 );
+ return HTTP_CONNECT;
+ }
+ } else {
+ DEBUG( DEBUG_SOCKET, "Connecting to server at %s:%d...\n",
+ sess->server.hostname, sess->server.port );
+ sess->socket = sock_connect( sess->server.addr, sess->server.port );
+ if( sess->socket < 0 ) {
+ (void) set_sockerr( req, _("Could not connect to server"), -1 );
+ return HTTP_CONNECT;
+ }
+ }
+ DEBUG( DEBUG_SOCKET, "Connected.\n" );
+ sess->connected = 1;
+ return HTTP_OK;
+}
+
+static int close_connection( http_session *sess ) {
+ DEBUG( DEBUG_SOCKET, "Closing socket.\n" );
+ sock_close( sess->socket );
+ sess->connected = 0;
+ DEBUG( DEBUG_SOCKET, "Socket closed.\n" );
+ return 0;
+}
+
diff --git a/neon/src/http_request.h b/neon/src/http_request.h
new file mode 100644
index 000000000..6059f8a47
--- /dev/null
+++ b/neon/src/http_request.h
@@ -0,0 +1,261 @@
+/*
+ HTTP Request Handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef HTTP_REQUEST_H
+#define HTTP_REQUEST_H
+
+#include <stdio.h> /* For FILE * */
+#include "http_utils.h" /* For http_status */
+#include "string_utils.h" /* For sbuffer */
+
+#define HTTP_OK (0)
+#define HTTP_ERROR (1) /* Generic error */
+#define HTTP_LOOKUP (3) /* Name lookup failed */
+#define HTTP_AUTH (4) /* User authentication failed on server */
+#define HTTP_AUTHPROXY (5) /* User authentication failed on proxy */
+#define HTTP_SERVERAUTH (6) /* Server authentication failed */
+#define HTTP_PROXYAUTH (7) /* Proxy authentication failed */
+#define HTTP_CONNECT (8) /* Could not connect to server */
+#define HTTP_TIMEOUT (9) /* Connection timed out */
+#define HTTP_FAILED (10) /* The precondition failed */
+
+/* Will be used by hooks: */
+#define HTTP_RETRY (101)
+
+#define EOL "\r\n"
+
+/****** Session Handling ******/
+
+typedef struct http_session_s http_session;
+typedef struct http_req_s http_req;
+
+/*
+ * Session handling:
+ * Call order:
+ * 1. http_session_init MANDATORY
+ * 2. http_session_proxy OPTIONAL
+ * 3. http_session_server MANDATORY
+ * 4. http_set_* OPTIONAL
+ * ... --- Any HTTP request method ---
+ * n. http_session_finish MANDATORY
+ */
+
+/* Create a new HTTP session */
+http_session *http_session_init( void );
+
+
+/* Finish an HTTP session */
+int http_session_finish( http_session *sess );
+
+/* Set the server or proxy server to be used for the given session.
+ * Returns:
+ * HTTP_LOOKUP if the DNS lookup for hostname failed.
+ * HTTP_OK otherwise.
+ *
+ * Note that if a proxy is being used, http_session_proxy should be
+ * called BEFORE http_session_server, so the DNS lookup can be skipped
+ * on the server. */
+int http_session_server( http_session *sess, const char *hostname, int port );
+int http_session_proxy( http_session *sess, const char *hostname, int port );
+
+/* Set protocol options for session:
+ * expect100: Defaults to OFF
+ * persist: Defaults to ON
+ */
+void http_set_expect100( http_session *sess, int use_expect100 );
+void http_set_persist( http_session *sess, int persist );
+/* Sets the user-agent string. neon/VERSION will be appended, to make
+ * the full header "User-Agent: token neon/VERSION" */
+void http_set_useragent( http_session *sess, const char *token );
+
+/* Determine if next-hop server claims HTTP/1.1 compliance. Returns:
+ * 0 if next-hop server does NOT claim HTTP/1.1 compliance
+ * non-zero if next-hop serer DOES claim HTTP/1.1 compliance
+ */
+int http_version_pre_http11( http_session *sess );
+
+/* Returns the 'hostport' URI segment for the end-server, e.g.
+ * "my.server.com:8080" or "www.server.com"
+ * (port segment is ommitted if == 80)
+ */
+const char *http_get_server_hostport( http_session *sess );
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocated
+ * memory.
+ * Must return:
+ * 0 on success: *username and *password must be non-NULL, and will
+ * be free'd by the HTTP layer when necessary
+ * -1 to cancel (*username and *password are ignored.)
+ */
+typedef int (*http_request_auth)(
+ void *userdata, const char *realm, const char *hostname,
+ char **username, char **password );
+
+/* Set callbacks to handle server and proxy authentication.
+ * userdata is passed as the first argument to the callback. */
+void http_set_server_auth( http_session *sess, http_request_auth callback,
+ void *userdata );
+void http_set_proxy_auth( http_session *sess, http_request_auth callback,
+ void *userdata );
+
+/* Set the error string for the session */
+void http_set_error( http_session *sess, const char *errstring );
+/* Retrieve the error string for the session */
+const char *http_get_error( http_session *sess );
+
+/**** Request hooks handling *****/
+
+/* TODO: Unimplemented */
+
+/* The aim of hooks is to support HTTP authentication, SSL/TLS, and possibly
+ * DAV locking (though this is harder), in a modular fashion - i.e., without
+ * requiring any code in http_request.c.
+ *
+ * DAV locking is harder, as it is not transparent to the caller: they
+ * specify what locks are needed (i.e. MKCOL modifies the *parent* collection
+ * of the resource at Request-URI as well as the resource itself.).
+ * This will probably require some way of retrieving the private data of
+ * the hook externally, hence the handler_id hack.
+ */
+typedef struct {
+ /* A slight hack? Allows access to the hook private information,
+ * externally... */
+ const char *id; /* id could be a URI to be globally unique */
+ /* e.g. "http://webdav.org/neon/hooks/davlock" */
+
+ /* Register a new request */
+ void (*create)( void **private,
+ http_req *req, const char *method, const char *uri );
+ /* Tell them what request body we are using: either buffer or stream
+ * will be non-NULL. */
+ void (*use_body)( void *private, const char *buffer, FILE *stream );
+ /* Just before sending the request: let them add headers if they want */
+ void (*pre_send)( void *private, sbuffer request );
+ /* After sending the request. May return:
+ * HTTP_OK everything is okay
+ * HTTP_RETRY try sending the request again */
+ int (*post_send)( void *private, http_status *stat );
+ /* Clean up after yourself */
+ void (*destroy)( void *private );
+} http_request_hooks;
+
+/* Add in hooks */
+void http_add_hooks( http_session *sess, http_request_hooks hooks );
+
+/* Return private data for a new request */
+void *http_get_hook_private( http_req *req, const char *id );
+
+/***** Request Handling *****/
+
+/* Create a new request, with given method and URI.
+ * Returns request pointer. */
+http_req *
+http_request_create( http_session *sess, const char *method, const char *uri );
+
+/* 'buffer' will be sent as the request body with given request. */
+void http_set_request_body_buffer( http_req *req, const char *buffer );
+/* Contents of stream will be sent as the request body with the given
+ * request */
+void http_set_request_body_stream( http_req *req, FILE *stream );
+
+/* Handling response bodies... you provide TWO callbacks:
+ *
+ * 1) 'acceptance' callback: determines whether you want to handle the
+ * response body given the response-status information, e.g., if you
+ * only want 2xx responses, say so here.
+ *
+ * 2) 'reader' callback: passed blocks of the response-body as they
+ * arrive, if the acceptance callback returned non-zero. */
+
+/* 'acceptance' callback type. */
+typedef int (*http_accept_response)(
+ void *userdata, http_req *req, http_status *st );
+
+/* An 'acceptance' callback which only accepts 2xx-style responses.
+ * Ignores userdata. */
+int http_accept_2xx( void *userdata, http_req *req, http_status *st );
+
+/* The 'reader' callback type */
+typedef void (*http_block_reader)(
+ void *userdata, const char *buf, size_t len );
+
+/* Add a response reader for the given request, with the given
+ * acceptance function. userdata is passed as the first argument to
+ * the acceptance + reader callbacks. */
+void http_add_response_body_reader( http_req *req, http_accept_response accpt,
+ http_block_reader rdr, void *userdata );
+
+/* Handle response headers. Each handler is associated with a specific
+ * header field (indicated by name). The handler is then passed the
+ * value of this header field. */
+
+/* The header handler callback type */
+typedef void (*http_header_handler)( void *userdata, const char *value );
+
+/* Adds a response header handler for the given request. userdata is passed
+ * as the first argument to the header handler. */
+void http_add_response_header_handler(
+ http_req *req, const char *name, http_header_handler hdl, void *userdata );
+
+/* Stock header handlers:
+ * 'duplicate': *(char *)userdata = strdup(value)
+ * 'numeric': *(int *)userdata = atoi(value)
+ * e.g.
+ * int mynum;
+ * http_add_response_header_handler( myreq, "Content-Length",
+ * http_handle_numeric_handler, &mynum );
+ * ... arranges mynum to be set to the value of the Content-Length header.
+ */
+void http_duplicate_header( void *userdata, const char *value );
+void http_handle_numeric_header( void *userdata, const char *value );
+
+/* Returns the sbuffer which is the request header string, allowing
+ * extra headers to be added to the request. */
+/* TODO: This is a bit noddy. */
+sbuffer http_get_request_header( http_req *req );
+
+/* http_request_dispatch: Sends the given request, and reads the
+ * response. Response-Status information is written into 'status'.
+ *
+ * Returns:
+ * HTTP_OK if request sent + response read okay.
+ * HTTP_AUTH if user authentication failed on origin server
+ * HTTP_AUTHPROXY if user authentication failed on proxy server
+ * HTTP_SERVERAUTH server authentication failed
+ * HTTP_PROXYAUTH proxy authentication failed
+ * HTTP_CONNECT could not connect to server/proxy server
+ * HTTP_TIMEOUT connection timed out mid-request
+ * HTTP_ERROR for other errors, and http_get_error() should
+ * return a meaningful error string
+ *
+ * NB: HTTP_AUTH and HTTP_AUTHPROXY mean that the USER supplied the
+ * wrong username/password. SERVER/PROXYAUTH mean that, after the
+ * server has accepted a valid username/password, the server/proxy did
+ * not authenticate the response message correctly.
+ * */
+int http_request_dispatch( http_req *req, http_status *status );
+
+/* Destroy memory associated with request pointer */
+void http_request_destroy( http_req *req );
+
+#endif /* HTTP_REQUEST_H */
+
diff --git a/neon/src/http_utils.c b/neon/src/http_utils.c
new file mode 100644
index 000000000..f4548ac51
--- /dev/null
+++ b/neon/src/http_utils.c
@@ -0,0 +1,116 @@
+/*
+ HTTP utility functions
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: http_utils.c,v 1.4 2000/05/10 13:26:07 joe Exp
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <ctype.h> /* isdigit() for http_parse_statusline */
+
+#include "dates.h"
+
+#include "http_utils.h"
+
+int http_debug_mask;
+FILE *http_debug_stream;
+
+void http_debug( int ch, char *template, ...)
+{
+#ifdef DEBUGGING
+ va_list params;
+ if( (ch&http_debug_mask) != ch ) return;
+ fflush( stdout );
+ va_start( params, template );
+ vfprintf( http_debug_stream, template, params );
+ va_end( params );
+ if( (ch&DEBUG_FLUSH) == DEBUG_FLUSH ) {
+ fflush( http_debug_stream );
+ }
+#else
+ /* No debugging here */
+#endif
+}
+
+/* HTTP-date parser */
+time_t http_dateparse( const char *date ) {
+ time_t tmp;
+ tmp = rfc1123_parse( date );
+ if( tmp == -1 ) {
+ tmp = rfc1036_parse( date );
+ if( tmp == -1 )
+ tmp = asctime_parse( date );
+ }
+ return tmp;
+}
+
+int http_parse_statusline( const char *status_line, http_status *st ) {
+ const char *part;
+ int major, minor, status_code, class;
+ /* Check they're speaking the right language */
+ if( strncmp( status_line, "HTTP/", 5 ) != 0 ) {
+ return -1;
+ }
+ /* And find out which dialect of this peculiar language
+ * they can talk... */
+ major = 0;
+ minor = 0;
+ /* Note, we're good children, and accept leading zero's on the
+ * version numbers */
+ for( part = status_line + 5; *part != '\0' && isdigit(*part); part++ ) {
+ major = major*10 + (*part-'0');
+ }
+ if( *part != '.' ) {
+ return -1;
+ }
+ for( part++ ; *part != '\0' && isdigit(*part); part++ ) {
+ minor = minor*10 + (*part-'0');
+ }
+ if( *part != ' ' ) {
+ return -1;
+ }
+ /* Skip any spaces */
+ for( ; *part == ' ' ; part++ ) /* noop */;
+ /* Now for the Status-Code. part now points at the first Y in
+ * "HTTP/x.x YYY". We want value of YYY... could use atoi, but
+ * probably quicker this way. */
+ if( !isdigit(part[0]) || !isdigit(part[1]) || !isdigit(part[2]) ) {
+ return -1;
+ }
+ status_code = 100*(part[0]-'0') + 10*(part[1]-'0') + (part[2]-'0');
+ class = part[0]-'0';
+ /* Skip whitespace between status-code and reason-phrase */
+ for( part+=3; *part == ' ' || *part == '\t'; part++ ) /* noop */;
+ if( *part == '\0' ) {
+ return -1;
+ }
+ /* Fill in the results */
+ st->major_version = major;
+ st->minor_version = minor;
+ st->reason_phrase = part;
+ st->code = status_code;
+ st->class = class;
+ return 0;
+}
diff --git a/neon/src/http_utils.h b/neon/src/http_utils.h
new file mode 100644
index 000000000..d18ac6f72
--- /dev/null
+++ b/neon/src/http_utils.h
@@ -0,0 +1,105 @@
+/*
+ HTTP utility functions
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef HTTP_UTILS_H
+#define HTTP_UTILS_H
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_DEBUG_FUNC
+#include "common.h"
+#endif
+
+#define HTTP_QUOTES "\"'"
+#define HTTP_WHITESPACE " \r\n\t"
+
+/* Handy macro to free things. */
+#define HTTP_FREE(x) \
+do { if( (x)!=NULL ) free( (x) ); (x) = NULL; } while(0)
+
+#define HTTP_PORT 80
+
+time_t http_dateparse( const char *date );
+
+#undef min
+#define min(a,b) ((a)<(b)?(a):(b))
+
+#ifndef HAVE_DEBUG_FUNC
+/* CONSIDER: mutt has a nicer way of way of doing debugging output... maybe
+ * switch to like that. */
+#ifdef DEBUGGING
+#define DEBUG if(0) http_debug
+#else /* !DEBUGGING */
+#define DEBUG http_debug
+#endif /* DEBUGGING */
+
+#define DEBUG_SOCKET (1<<0)
+#define DEBUG_HTTP (1<<3)
+#define DEBUG_XML (1<<5)
+#define DEBUG_HTTPAUTH (1<<7)
+#define DEBUG_HTTPPLAIN (1<<8)
+#define DEBUG_LOCKS (1<<10)
+#define DEBUG_XMLPARSE (1<<11)
+#define DEBUG_HTTPBODY (1<<12)
+#define DEBUG_HTTPBASIC (1<<13)
+#define DEBUG_FLUSH (1<<22)
+
+extern FILE *http_debug_stream;
+extern int http_debug_mask;
+
+void http_debug( int ch, char *, ... )
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif /* __GNUC__ */
+;
+
+#endif /* HAVE_DEBUG_FUNC */
+
+/* Storing an HTTP status result */
+typedef struct {
+ int major_version;
+ int minor_version;
+ int code; /* Status-Code value */
+ int class; /* Class of Status-Code (1-5) */
+ const char *reason_phrase;
+} http_status;
+
+/* Parser for strings which follow the Status-Line grammar from
+ * RFC2616.
+ * Returns:
+ * 0 on success, *s will be filled in.
+ * -1 on parse error.
+ */
+int http_parse_statusline( const char *status_line, http_status *s );
+
+#endif /* HTTP_UTILS_H */
diff --git a/neon/src/md5.c b/neon/src/md5.c
new file mode 100644
index 000000000..00aab1582
--- /dev/null
+++ b/neon/src/md5.c
@@ -0,0 +1,447 @@
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+ according to the definition of MD5 in RFC 1321 from April 1992.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include "md5.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define WORDS_BIGENDIAN 1
+# endif
+/* We need to keep the namespace clean so define the MD5 function
+ protected using leading __ and use weak aliases. */
+# define md5_init_ctx __md5_init_ctx
+# define md5_process_block __md5_process_block
+# define md5_process_bytes __md5_process_bytes
+# define md5_finish_ctx __md5_finish_ctx
+# define md5_read_ctx __md5_read_ctx
+# define md5_stream __md5_stream
+# define md5_buffer __md5_buffer
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+void
+md5_init_ctx (ctx)
+ struct md5_ctx *ctx;
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result
+ must be in little endian byte order.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_read_ctx (ctx, resbuf)
+ const struct md5_ctx *ctx;
+ void *resbuf;
+{
+ ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+ ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+ ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+ ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+ return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_finish_ctx (ctx, resbuf)
+ struct md5_ctx *ctx;
+ void *resbuf;
+{
+ /* Take yet unprocessed bytes into account. */
+ md5_uint32 bytes = ctx->buflen;
+ size_t pad;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+ memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+ *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+ (ctx->total[0] >> 29));
+
+ /* Process last bytes. */
+ md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+ return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+int
+md5_stream (stream, resblock)
+ FILE *stream;
+ void *resblock;
+{
+ /* Important: BLOCKSIZE must be a multiple of 64. */
+#define BLOCKSIZE 4096
+ struct md5_ctx ctx;
+ char buffer[BLOCKSIZE + 72];
+ size_t sum;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ do
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+ }
+ while (sum < BLOCKSIZE && n != 0);
+ if (n == 0 && ferror (stream))
+ return 1;
+
+ /* If end of file is reached, end the loop. */
+ if (n == 0)
+ break;
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ md5_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+ /* Add the last bytes if necessary. */
+ if (sum > 0)
+ md5_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ md5_finish_ctx (&ctx, resblock);
+ return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+void *
+md5_buffer (buffer, len, resblock)
+ const char *buffer;
+ size_t len;
+ void *resblock;
+{
+ struct md5_ctx ctx;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Process whole buffer but last len % 64 bytes. */
+ md5_process_bytes (buffer, len, &ctx);
+
+ /* Put result in desired memory area. */
+ return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0)
+ {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy (&ctx->buffer[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (left_over + add > 64)
+ {
+ md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+ (left_over + add) & 63);
+ ctx->buflen = (left_over + add) & 63;
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len > 64)
+ {
+ md5_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0)
+ {
+ memcpy (ctx->buffer, buffer, len);
+ ctx->buflen = len;
+ }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+
+void
+md5_process_block (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ md5_uint32 correct_words[16];
+ const md5_uint32 *words = buffer;
+ size_t nwords = len / sizeof (md5_uint32);
+ const md5_uint32 *endp = words + nwords;
+ md5_uint32 A = ctx->A;
+ md5_uint32 B = ctx->B;
+ md5_uint32 C = ctx->C;
+ md5_uint32 D = ctx->D;
+
+ /* First increment the byte count. RFC 1321 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += len;
+ if (ctx->total[0] < len)
+ ++ctx->total[1];
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (words < endp)
+ {
+ md5_uint32 *cwp = correct_words;
+ md5_uint32 A_save = A;
+ md5_uint32 B_save = B;
+ md5_uint32 C_save = C;
+ md5_uint32 D_save = D;
+
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
+ ++words; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
+
+
+#ifdef _LIBC
+/* Define weak aliases. */
+# undef md5_init_ctx
+weak_alias (__md5_init_ctx, md5_init_ctx)
+# undef md5_process_block
+weak_alias (__md5_process_block, md5_process_block)
+# undef md5_process_bytes
+weak_alias (__md5_process_bytes, md5_process_bytes)
+# undef md5_finish_ctx
+weak_alias (__md5_finish_ctx, md5_finish_ctx)
+# undef md5_read_ctx
+weak_alias (__md5_read_ctx, md5_read_ctx)
+# undef md5_stream
+weak_alias (__md5_stream, md5_stream)
+# undef md5_buffer
+weak_alias (__md5_buffer, md5_buffer)
+#endif
diff --git a/neon/src/md5.h b/neon/src/md5.h
new file mode 100644
index 000000000..ce4091dea
--- /dev/null
+++ b/neon/src/md5.h
@@ -0,0 +1,157 @@
+/* Declaration of functions and data types used for MD5 sum computing
+ library functions.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _MD5_H
+#define _MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+ to determine an unsigned integral type that is 32 bits wide. An
+ alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+ doing that would require that the configure script compile and *run*
+ the resulting executable. Locally running cross-compiled executables
+ is usually not possible. */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+# define UINT_MAX_32_BITS 4294967295U
+# else
+# define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+ This should be valid for all systems GNU cares about because
+ that doesn't include 16-bit systems, and only modern systems
+ (that certainly have <limits.h>) have 64+-bit integral types. */
+
+# ifndef UINT_MAX
+# define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+ typedef unsigned int md5_uint32;
+# else
+# if USHRT_MAX == UINT_MAX_32_BITS
+ typedef unsigned short md5_uint32;
+# else
+# if ULONG_MAX == UINT_MAX_32_BITS
+ typedef unsigned long md5_uint32;
+# else
+ /* The following line is intended to evoke an error.
+ Using #error is not portable enough. */
+ "Cannot determine unsigned 32-bit data type."
+# endif
+# endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+# define __P(x) x
+#else
+# define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps. */
+struct md5_ctx
+{
+ md5_uint32 A;
+ md5_uint32 B;
+ md5_uint32 C;
+ md5_uint32 D;
+
+ md5_uint32 total[2];
+ md5_uint32 buflen;
+ char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+extern void __md5_init_ctx __P ((struct md5_ctx *ctx));
+extern void md5_init_ctx __P ((struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void __md5_process_block __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+extern void md5_process_block __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void __md5_process_bytes __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+extern void md5_process_bytes __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 16 bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *__md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *__md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+extern int __md5_stream __P ((FILE *stream, void *resblock));
+extern int md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+extern void *__md5_buffer __P ((const char *buffer, size_t len,
+ void *resblock));
+extern void *md5_buffer __P ((const char *buffer, size_t len,
+ void *resblock));
+
+#endif /* md5.h */
diff --git a/neon/src/ne_207.c b/neon/src/ne_207.c
new file mode 100644
index 000000000..ac6065b70
--- /dev/null
+++ b/neon/src/ne_207.c
@@ -0,0 +1,378 @@
+/*
+ WebDAV 207 multi-status response handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* Generic handling for WebDAV 207 Multi-Status responses. */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_utils.h"
+#include "ne_xml.h"
+#include "ne_207.h"
+#include "ne_uri.h"
+#include "ne_basic.h"
+
+#include "ne_i18n.h"
+
+struct ne_207_parser_s {
+ ne_207_start_response start_response;
+ ne_207_end_response end_response;
+ ne_207_start_propstat start_propstat;
+ ne_207_end_propstat end_propstat;
+ ne_xml_parser *parser;
+ void *userdata;
+ /* current position */
+ void *response, *propstat;
+ /* caching */
+ ne_status status;
+ char *description, *href, *status_line;
+};
+
+static const struct ne_xml_elm elements[] = {
+ { "DAV:", "multistatus", NE_ELM_multistatus, 0 },
+ { "DAV:", "response", NE_ELM_response, 0 },
+ { "DAV:", "responsedescription", NE_ELM_responsedescription,
+ NE_XML_CDATA },
+ { "DAV:", "href", NE_ELM_href, NE_XML_CDATA },
+ { "DAV:", "propstat", NE_ELM_propstat, 0 },
+ { "DAV:", "prop", NE_ELM_prop, 0 },
+ { "DAV:", "status", NE_ELM_status, NE_XML_CDATA },
+ { NULL }
+};
+
+/* Set the callbacks for the parser */
+void ne_207_set_response_handlers(ne_207_parser *p,
+ ne_207_start_response start,
+ ne_207_end_response end)
+{
+ p->start_response = start;
+ p->end_response = end;
+}
+
+void ne_207_set_propstat_handlers(ne_207_parser *p,
+ ne_207_start_propstat start,
+ ne_207_end_propstat end)
+{
+ p->start_propstat = start;
+ p->end_propstat = end;
+}
+
+void *ne_207_get_current_response(ne_207_parser *p)
+{
+ return p->response;
+}
+
+void *ne_207_get_current_propstat(ne_207_parser *p)
+{
+ return p->propstat;
+}
+
+static int
+start_element(void *userdata, const struct ne_xml_elm *elm,
+ const char **atts)
+{
+ ne_207_parser *p = userdata;
+
+ switch (elm->id) {
+ case NE_ELM_response:
+ /* Create new response delayed until we get HREF */
+ break;
+ case NE_ELM_propstat:
+ if (p->start_propstat) {
+ p->propstat = (*p->start_propstat)(p->userdata, p->response);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int
+end_element(void *userdata, const struct ne_xml_elm *elm, const char *cdata)
+{
+ ne_207_parser *p = userdata;
+
+ switch (elm->id) {
+ case NE_ELM_responsedescription:
+ if (cdata != NULL) {
+ NE_FREE(p->description);
+ p->description = ne_strdup(cdata);
+ }
+ break;
+ case NE_ELM_href:
+ /* Now we have the href, begin the response */
+ if (p->start_response && cdata != NULL) {
+ p->response = (*p->start_response)(p->userdata, cdata);
+ }
+ break;
+ case NE_ELM_status:
+ if (cdata) {
+ NE_FREE(p->status_line);
+ p->status_line = ne_strdup(cdata);
+ if (ne_parse_statusline(p->status_line, &p->status)) {
+ char buf[500];
+ NE_DEBUG(NE_DBG_HTTP, "Status line: %s\n", cdata);
+ snprintf(buf, 500,
+ _("Invalid HTTP status line in status element at line %d of response:\nStatus line was: %s"),
+ ne_xml_currentline(p->parser), p->status_line);
+ ne_xml_set_error(p->parser, buf);
+ NE_FREE(p->status_line);
+ return -1;
+ } else {
+ NE_DEBUG(NE_DBG_XML, "Decoded status line: %s\n", p->status_line);
+ }
+ }
+ break;
+ case NE_ELM_propstat:
+ if (p->end_propstat) {
+ (*p->end_propstat)(p->userdata, p->propstat, p->status_line,
+ p->status_line?&p->status:NULL, p->description);
+ }
+ p->propstat = NULL;
+ NE_FREE(p->description);
+ NE_FREE(p->status_line);
+ break;
+ case NE_ELM_response:
+ if (p->end_response) {
+ (*p->end_response)(p->userdata, p->response, p->status_line,
+ p->status_line?&p->status:NULL, p->description);
+ }
+ p->response = NULL;
+ NE_FREE(p->status_line);
+ NE_FREE(p->description);
+ break;
+ }
+ return 0;
+}
+
+/* This should map directly from the DTD... with the addition of
+ * ignoring anything we don't understand, being liberal in what we
+ * accept. */
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child)
+{
+ NE_DEBUG(NE_DBG_XML, "207cc: %d in %d\n", child, parent);
+ switch (parent) {
+ case NE_ELM_root:
+ switch (child) {
+ case NE_ELM_multistatus:
+ case NE_ELM_response: /* not sure why this is here... */
+ return NE_XML_VALID;
+ default:
+ break;
+ }
+ break;
+ case NE_ELM_multistatus:
+ /* <!ELEMENT multistatus (response+, responsedescription?) > */
+ switch (child) {
+ case NE_ELM_response:
+ case NE_ELM_responsedescription:
+ return NE_XML_VALID;
+ default:
+ break;
+ }
+ break;
+ case NE_ELM_response:
+ /* <!ELEMENT response (href, ((href*, status)|(propstat+)),
+ responsedescription?) > */
+ switch (child) {
+ case NE_ELM_href:
+ case NE_ELM_status:
+ case NE_ELM_propstat:
+ case NE_ELM_responsedescription:
+ return NE_XML_VALID;
+ default:
+ break;
+ }
+ break;
+ case NE_ELM_propstat:
+ /* <!ELEMENT propstat (prop, status, responsedescription?) > */
+ switch (child) {
+ case NE_ELM_prop:
+ case NE_ELM_status:
+ case NE_ELM_responsedescription:
+ return NE_XML_VALID;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NE_XML_DECLINE;
+}
+
+static int ignore_cc(ne_xml_elmid parent, ne_xml_elmid child)
+{
+ if (child == NE_ELM_unknown || parent == NE_ELM_unknown) {
+ NE_DEBUG(NE_DBG_XML, "207 catch-all caught %d in %d\n", child, parent);
+ return NE_XML_VALID;
+ }
+
+ return NE_XML_DECLINE;
+}
+
+void ne_207_ignore_unknown(ne_207_parser *p)
+{
+ static const struct ne_xml_elm any_elms[] = {
+ { "", "", NE_ELM_unknown, NE_XML_COLLECT },
+ { NULL }
+ };
+
+ ne_xml_push_handler(p->parser, any_elms,
+ ignore_cc, NULL, NULL, NULL);
+
+}
+
+ne_207_parser *ne_207_create(ne_xml_parser *parser, void *userdata)
+{
+ ne_207_parser *p = ne_calloc(sizeof *p);
+
+ p->parser = parser;
+ p->userdata = userdata;
+
+ /* Add handler for the standard 207 elements */
+ ne_xml_push_handler(parser, elements, check_context,
+ start_element, end_element, p);
+
+ return p;
+}
+
+void ne_207_destroy(ne_207_parser *p)
+{
+ free(p);
+}
+
+int ne_accept_207(void *userdata, ne_request *req, ne_status *status)
+{
+ return (status->code == 207);
+}
+
+/* Handling of 207 errors: we keep a string buffer, and append
+ * messages to it as they come down.
+ *
+ * Note, 424 means it would have worked but something else went wrong.
+ * We will have had the error for "something else", so we display
+ * that, and skip 424 errors. */
+
+/* This is passed as userdata to the 207 code. */
+struct context {
+ char *href;
+ ne_buffer *buf;
+ unsigned int is_error;
+};
+
+static void *start_response(void *userdata, const char *href)
+{
+ struct context *ctx = userdata;
+ NE_FREE(ctx->href);
+ ctx->href = ne_strdup(href);
+ return NULL;
+}
+
+static void handle_error(struct context *ctx,
+ const char *status_line, const ne_status *status,
+ const char *description)
+{
+ if (status && status->klass != 2 && status->code != 424) {
+ ctx->is_error = 1;
+ ne_buffer_concat(ctx->buf, ctx->href, ": ", status_line, "\n", NULL);
+ if (description != NULL) {
+ /* TODO: these can be multi-line. Would be good to
+ * word-wrap this at col 80. */
+ ne_buffer_concat(ctx->buf, " -> ", description, "\n", NULL);
+ }
+ }
+
+}
+
+static void end_response(void *userdata, void *response, const char *status_line,
+ const ne_status *status, const char *description)
+{
+ struct context *ctx = userdata;
+ handle_error(ctx, status_line, status, description);
+}
+
+static void
+end_propstat(void *userdata, void *propstat, const char *status_line,
+ const ne_status *status, const char *description)
+{
+ struct context *ctx = userdata;
+ handle_error(ctx, status_line, status, description);
+}
+
+/* Dispatch a DAV request and handle a 207 error response appropriately */
+int ne_simple_request(ne_session *sess, ne_request *req)
+{
+ int ret;
+ ne_content_type ctype = {0};
+ struct context ctx = {0};
+ ne_207_parser *p207;
+ ne_xml_parser *p;
+
+ p = ne_xml_create();
+ p207 = ne_207_create(p, &ctx);
+ /* The error string is progressively written into the
+ * ne_buffer by the element callbacks */
+ ctx.buf = ne_buffer_create();
+
+ ne_207_set_response_handlers(p207, start_response, end_response);
+ ne_207_set_propstat_handlers(p207, NULL, end_propstat);
+
+ ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
+ ne_add_response_header_handler(req, "Content-Type",
+ ne_content_type_handler, &ctype);
+
+ ne_207_ignore_unknown(p207);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK) {
+ if (ne_get_status(req)->code == 207) {
+ if (!ne_xml_valid(p)) {
+ /* The parse was invalid */
+ ne_set_error(sess, ne_xml_get_error(p));
+ ret = NE_ERROR;
+ } else if (ctx.is_error) {
+ /* If we've actually got any error information
+ * from the 207, then set that as the error */
+ ne_set_error(sess, ctx.buf->data);
+ ret = NE_ERROR;
+ }
+ } else if (ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+ }
+
+ NE_FREE(ctype.value);
+ ne_207_destroy(p207);
+ ne_xml_destroy(p);
+ ne_buffer_destroy(ctx.buf);
+ NE_FREE(ctx.href);
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
diff --git a/neon/src/ne_207.h b/neon/src/ne_207.h
new file mode 100644
index 000000000..4b748800e
--- /dev/null
+++ b/neon/src/ne_207.h
@@ -0,0 +1,100 @@
+/*
+ WebDAV 207 multi-status response handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DAV207_H
+#define DAV207_H
+
+#include "ne_xml.h"
+#include "ne_request.h" /* for http_req */
+
+BEGIN_NEON_DECLS
+
+#define NE_ELM_207_first (NE_ELM_UNUSED)
+
+#define NE_ELM_multistatus (NE_ELM_207_first)
+#define NE_ELM_response (NE_ELM_207_first + 1)
+#define NE_ELM_responsedescription (NE_ELM_207_first + 2)
+#define NE_ELM_href (NE_ELM_207_first + 3)
+#define NE_ELM_propstat (NE_ELM_207_first + 4)
+#define NE_ELM_prop (NE_ELM_207_first + 5)
+#define NE_ELM_status (NE_ELM_207_first + 6)
+
+#define NE_ELM_207_UNUSED (NE_ELM_UNUSED + 100)
+
+struct ne_207_parser_s;
+typedef struct ne_207_parser_s ne_207_parser;
+
+/* The name of a WebDAV property. */
+typedef struct {
+ const char *nspace, *name;
+} ne_propname;
+
+/* The handler structure: you provide a set of callbacks.
+ * They are called in the order they are listed... start/end_prop
+ * multiple times before end_prop, start/end_propstat multiple times
+ * before an end_response, start/end_response multiple times.
+ */
+
+/* TODO: do we need to pass userdata to ALL of these? We could get away with
+ * only passing the userdata to the start_'s and relying on the caller
+ * to send it through as the _start return value if they need it. */
+
+typedef void *(*ne_207_start_response)(void *userdata, const char *href);
+typedef void (*ne_207_end_response)(
+ void *userdata, void *response, const char *status_line,
+ const ne_status *status, const char *description);
+
+typedef void *(*ne_207_start_propstat)(void *userdata, void *response);
+typedef void (*ne_207_end_propstat)(
+ void *userdata, void *propstat, const char *status_line,
+ const ne_status *status, const char *description);
+
+/* Create a 207 parser */
+
+ne_207_parser *ne_207_create(ne_xml_parser *parser, void *userdata);
+
+/* Set the callbacks for the parser */
+
+void ne_207_set_response_handlers(
+ ne_207_parser *p, ne_207_start_response start, ne_207_end_response end);
+
+void ne_207_set_propstat_handlers(
+ ne_207_parser *p, ne_207_start_propstat start, ne_207_end_propstat end);
+
+void ne_207_destroy(ne_207_parser *p);
+
+/* An acceptance function which only accepts 207 responses */
+int ne_accept_207(void *userdata, ne_request *req, ne_status *status);
+
+void *ne_207_get_current_propstat(ne_207_parser *p);
+void *ne_207_get_current_response(ne_207_parser *p);
+
+/* Call this as the LAST thing before beginning parsing, to install a
+ * catch-all handler which means all unknown XML returned in the 207
+ * response is ignored gracefully. */
+void ne_207_ignore_unknown(ne_207_parser *p);
+
+/* Dispatch a DAV request and handle a 207 error response appropriately */
+int ne_simple_request(ne_session *sess, ne_request *req);
+
+END_NEON_DECLS
+
+#endif /* DAV207_H */
diff --git a/neon/src/ne_acl.c b/neon/src/ne_acl.c
new file mode 100644
index 000000000..888cb752e
--- /dev/null
+++ b/neon/src/ne_acl.c
@@ -0,0 +1,129 @@
+/*
+ Access control
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* Contributed by Arun Garg <arung@pspl.co.in> */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_locks.h"
+#include "ne_alloc.h"
+#include "ne_string.h"
+#include "ne_acl.h"
+#include "ne_uri.h"
+
+static ne_buffer *acl_body(ne_acl_entry *right, int count)
+{
+ ne_buffer *body = ne_buffer_create();
+ int m;
+
+ ne_buffer_zappend(body,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<acl xmlns='DAV:'>" EOL);
+
+ for (m = 0; m < count; m++) {
+ const char *type;
+
+ type = (right[m].type == ne_acl_grant ? "grant" : "deny");
+
+ ne_buffer_concat(body, "<ace>" EOL "<principal>", NULL);
+
+ switch (right[m].apply) {
+ case ne_acl_all:
+ ne_buffer_zappend(body, "<all/>" EOL);
+ break;
+ case ne_acl_property:
+ ne_buffer_concat(body, "<property><", right[m].principal,
+ "/></property>" EOL, NULL);
+ break;
+ case ne_acl_href:
+ ne_buffer_concat(body, "<href>", right[m].principal,
+ "</href>" EOL, NULL);
+ break;
+ }
+
+ ne_buffer_concat(body, "</principal>" EOL "<", type, ">" EOL, NULL);
+
+ if (right[m].read == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<read/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].read_acl == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<read-acl/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].write == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<write/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].write_acl == 0)
+ ne_buffer_concat(body,
+ "<privilege>" "<write-acl/>" "</privilege>" EOL,
+ NULL);
+ if (right[m].read_cuprivset == 0)
+ ne_buffer_concat(body,
+ "<privilege>"
+ "<read-current-user-privilege-set/>"
+ "</privilege>" EOL, NULL);
+ ne_buffer_concat(body, "</", type, ">" EOL, NULL);
+ ne_buffer_zappend(body, "</ace>" EOL);
+ }
+ ne_buffer_zappend(body, "</acl>" EOL);
+
+ return body;
+}
+
+int ne_acl_set(ne_session *sess, const char *uri,
+ ne_acl_entry *entries, int numentries)
+{
+ int ret;
+ ne_request *req = ne_request_create(sess, "ACL", uri);
+ ne_buffer *body = acl_body(entries, numentries);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, 0);
+#endif
+
+ ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+ ne_add_request_header(req, "Content-Type", "text/xml");
+ ret = ne_request_dispatch(req);
+
+ ne_buffer_destroy(body);
+
+ if (ret == NE_OK && ne_get_status(req)->code == 207) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+ return ret;
+}
diff --git a/neon/src/ne_acl.h b/neon/src/ne_acl.h
new file mode 100644
index 000000000..bf1a1ece4
--- /dev/null
+++ b/neon/src/ne_acl.h
@@ -0,0 +1,56 @@
+/*
+ Access control
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_ACL_H
+#define NE_ACL_H
+
+#include "ne_session.h"
+
+BEGIN_NEON_DECLS
+
+typedef struct
+{
+ enum {
+ ne_acl_href,
+ ne_acl_property,
+ ne_acl_all
+ } apply;
+
+ enum {
+ ne_acl_grant,
+ ne_acl_deny
+ } type;
+
+ char *principal;
+ int read;
+ int read_acl;
+ int write;
+ int write_acl;
+ int read_cuprivset;
+} ne_acl_entry;
+
+/* Set the ACL for the given resource to the list of ACL entries. */
+int ne_acl_set(ne_session *sess, const char *uri,
+ ne_acl_entry entries[], int numentries);
+
+END_NEON_DECLS
+
+#endif /* NE_ACL_H */
diff --git a/neon/src/ne_alloc.c b/neon/src/ne_alloc.c
new file mode 100644
index 000000000..f37662100
--- /dev/null
+++ b/neon/src/ne_alloc.c
@@ -0,0 +1,61 @@
+/*
+ Replacement memory allocation handling etc.
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: ne_alloc.c,v 1.2 2000/08/02 10:23:03 joe Exp
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_alloc.h"
+
+void *ne_malloc(size_t len)
+{
+ void *ptr = malloc(len);
+ if (!ptr) {
+ /* uh-oh */
+ abort();
+ }
+ return ptr;
+}
+
+void *ne_calloc(size_t len)
+{
+ return memset(ne_malloc(len), 0, len);
+}
+
+char *ne_strdup(const char *s)
+{
+ return strcpy(ne_malloc(strlen(s) + 1), s);
+}
+
+char *ne_strndup(const char *s, size_t n)
+{
+ char *new = ne_malloc(n + 1);
+ new[n] = '\0';
+ memcpy(new, s, n);
+ return new;
+}
diff --git a/neon/src/ne_alloc.h b/neon/src/ne_alloc.h
new file mode 100644
index 000000000..e120c6b53
--- /dev/null
+++ b/neon/src/ne_alloc.h
@@ -0,0 +1,34 @@
+/*
+ Replacement memory allocation handling etc.
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_ALLOC_H
+#define NE_ALLOC_H
+
+#include <sys/types.h>
+
+void *ne_malloc(size_t len);
+void *ne_calloc(size_t len);
+
+char *ne_strdup(const char *s);
+
+char *ne_strndup(const char *s, size_t n);
+
+#endif /* NE_ALLOC_H */
diff --git a/neon/src/ne_auth.c b/neon/src/ne_auth.c
new file mode 100644
index 000000000..e613726e9
--- /dev/null
+++ b/neon/src/ne_auth.c
@@ -0,0 +1,1095 @@
+/*
+ HTTP Authentication routines
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+
+/* HTTP Authentication, as per RFC2617.
+ *
+ * TODO:
+ * - Improve cnonce? Make it really random using /dev/random or whatever?
+ * - Test auth-int support
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#ifdef HAVE_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include "base64.h"
+
+#include "ne_md5.h"
+#include "ne_dates.h"
+#include "ne_request.h"
+#include "ne_auth.h"
+#include "ne_string.h"
+#include "ne_utils.h"
+#include "ne_alloc.h"
+#include "ne_uri.h"
+#include "ne_i18n.h"
+
+/* TODO: should remove this eventually. Need it for
+ * ne_pull_request_body. */
+#include "ne_private.h"
+
+/* The MD5 digest of a zero-length entity-body */
+#define DIGEST_MD5_EMPTY "d41d8cd98f00b204e9800998ecf8427e"
+
+#define HOOK_SERVER_ID "http://webdav.org/neon/hooks/server-auth"
+#define HOOK_PROXY_ID "http://webdav.org/neon/hooks/proxy-auth"
+
+/* The authentication scheme we are using */
+typedef enum {
+ auth_scheme_basic,
+ auth_scheme_digest
+} auth_scheme;
+
+typedef enum {
+ auth_alg_md5,
+ auth_alg_md5_sess,
+ auth_alg_unknown
+} auth_algorithm;
+
+/* Selected method of qop which the client is using */
+typedef enum {
+ auth_qop_none,
+ auth_qop_auth,
+ auth_qop_auth_int
+} auth_qop;
+
+/* A challenge */
+struct auth_challenge {
+ auth_scheme scheme;
+ char *realm;
+ char *domain;
+ char *nonce;
+ char *opaque;
+ unsigned int stale:1; /* if stale=true */
+ unsigned int got_qop:1; /* we were given a qop directive */
+ unsigned int qop_auth:1; /* "auth" token in qop attrib */
+ unsigned int qop_auth_int:1; /* "auth-int" token in qop attrib */
+ auth_algorithm alg;
+ struct auth_challenge *next;
+};
+
+static const char *qop_values[] = {
+ NULL,
+ "auth",
+ "auth-int"
+};
+static const char *algorithm_names[] = {
+ "MD5",
+ "MD5-sess",
+ NULL
+};
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocate
+ * memory.
+ * Must return:
+ * 0 on success,
+ * -1 to cancel.
+ */
+
+/* Authentication session state. */
+typedef struct {
+ ne_session *sess;
+
+ /* for making this proxy/server generic. */
+ const char *req_hdr, *resp_hdr, *resp_info_hdr, *fail_msg;
+ int status_code;
+ int fail_code;
+
+ /* The scheme used for this authentication session */
+ auth_scheme scheme;
+ /* The callback used to request new username+password */
+ ne_request_auth reqcreds;
+ void *reqcreds_udata;
+
+ /*** Session details ***/
+
+ /* The username and password we are using to authenticate with */
+ char *username;
+ /* Whether we CAN supply authentication at the moment */
+ unsigned int can_handle:1;
+ /* This used for Basic auth */
+ char *basic;
+ /* These all used for Digest auth */
+ char *unq_realm;
+ char *unq_nonce;
+ char *unq_cnonce;
+ char *opaque;
+ /* A list of domain strings */
+ unsigned int domain_count;
+ char **domain;
+ auth_qop qop;
+ auth_algorithm alg;
+ int nonce_count;
+ /* The ASCII representation of the session's H(A1) value */
+ char h_a1[33];
+
+ /* Temporary store for half of the Request-Digest
+ * (an optimisation - used in the response-digest calculation) */
+ struct ne_md5_ctx stored_rdig;
+
+ /* Details of server... needed to reconstruct absoluteURI's when
+ * necessary */
+ const char *host;
+ const char *uri_scheme;
+ unsigned int port;
+
+} auth_session;
+
+struct auth_request {
+ auth_session *session;
+
+ /*** Per-request details. ***/
+ ne_request *request; /* the request object. */
+
+ /* The method and URI we are using for the current request */
+ const char *uri;
+ const char *method;
+ /* Whether we WILL supply authentication for this request or not */
+ unsigned int will_handle:1;
+
+ /* Used for calculation of H(entity-body) of the response */
+ struct ne_md5_ctx response_body;
+
+ int tries;
+ /* Results of response-header callbacks */
+ char *auth_hdr, *auth_info_hdr;
+};
+
+/* Private prototypes which used to be public. */
+
+static auth_session *auth_create(ne_session *sess,
+ ne_request_auth callback,
+ void *userdata);
+
+/* Pass this the value of the "(Proxy,WWW)-Authenticate: " header field.
+ * Returns:
+ * 0 if we can now authenticate ourselves with the server.
+ * non-zero if we can't
+ */
+static int auth_challenge(auth_session *sess, const char *value);
+
+/* If you receive a "(Proxy-)Authentication-Info:" header, pass its value to
+ * this function. Returns zero if this successfully authenticates
+ * the response as coming from the server, and false if it hasn't. */
+static int verify_response(struct auth_request *req,
+ auth_session *sess, const char *value);
+
+/* Private prototypes */
+static char *get_cnonce(void);
+static void clean_session(auth_session *sess);
+static int digest_challenge(auth_session *, struct auth_challenge *);
+static int basic_challenge(auth_session *, struct auth_challenge *);
+static char *request_digest(auth_session *sess, struct auth_request *req);
+static char *request_basic(auth_session *);
+
+/* Domain handling */
+static int is_in_domain(auth_session *sess, const char *uri);
+static int parse_domain(auth_session *sess, const char *domain);
+
+/* Get the credentials, passing a temporary store for the password value */
+static int get_credentials(auth_session *sess, char **password);
+
+auth_session *auth_create(ne_session *sess,
+ ne_request_auth callback,
+ void *userdata)
+{
+ auth_session *ret = ne_calloc(sizeof(auth_session));
+
+ ret->reqcreds = callback;
+ ret->reqcreds_udata = userdata;
+ ret->sess = sess;
+
+ return ret;
+}
+
+#if 0
+void ne_auth_set_server(auth_session *sess,
+ const char *host, unsigned int port, const char *scheme)
+{
+ sess->host = host;
+ sess->port = port;
+ sess->req_scheme = scheme;
+}
+#endif
+
+static void clean_session(auth_session *sess)
+{
+ sess->can_handle = 0;
+ NE_FREE(sess->basic);
+ NE_FREE(sess->unq_realm);
+ NE_FREE(sess->unq_nonce);
+ NE_FREE(sess->unq_cnonce);
+ NE_FREE(sess->opaque);
+ NE_FREE(sess->username);
+ if (sess->domain_count > 0) {
+ split_string_free(sess->domain);
+ sess->domain_count = 0;
+ }
+}
+
+/* Returns cnonce-value. We just use base64(time).
+ * TODO: Could improve this? */
+static char *get_cnonce(void)
+{
+ char *ret, *tmp;
+ tmp = ne_rfc1123_date(time(NULL));
+ ret = ne_base64(tmp, strlen(tmp));
+ free(tmp);
+ return ret;
+}
+
+static int
+get_credentials(auth_session *sess, char **password)
+{
+ return (*sess->reqcreds)(sess->reqcreds_udata, sess->unq_realm,
+ &sess->username, password);
+}
+
+static int parse_domain(auth_session *sess, const char *domain) {
+ char *unq, **doms;
+ int count, ret;
+
+ unq = shave_string(domain, '"');
+ doms = split_string_c(unq, ' ', NULL, " \r\n\t", &count);
+ if (doms != NULL) {
+ if (count > 0) {
+ sess->domain = doms;
+ sess->domain_count = count;
+ ret = 0;
+ } else {
+ free(doms);
+ ret = -1;
+ }
+ } else {
+ ret = -1;
+ }
+ free(unq);
+ return ret;
+}
+
+/* Returns:
+ * 0: if uri is in NOT in domain of session
+ * else: uri IS in domain of session (or no domain known)
+ */
+static int is_in_domain(auth_session *sess, const char *uri)
+{
+#if 1
+ return 1;
+#else
+ /* TODO: Need proper URI comparison for this to work. */
+ int ret, dom;
+ const char *abs_path;
+ if (sess->domain_count == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "No domain, presuming okay.\n");
+ return 1;
+ }
+ ret = 0;
+ abs_path = uri_abspath(uri);
+ for (dom = 0; dom < sess->domain_count; dom++) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Checking domain: %s\n", sess->domain[dom]);
+ if (uri_childof(sess->domain[dom], abs_path)) {
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+#endif
+}
+
+/* Examine a Basic auth challenge.
+ * Returns 0 if an valid challenge, else non-zero. */
+static int
+basic_challenge(auth_session *sess, struct auth_challenge *parms)
+{
+ char *tmp, *password;
+
+ /* Verify challenge... must have a realm */
+ if (parms->realm == NULL) {
+ return -1;
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got Basic challenge with realm [%s]\n",
+ parms->realm);
+
+ clean_session(sess);
+
+ sess->unq_realm = shave_string(parms->realm, '"');
+
+ if (get_credentials(sess, &password)) {
+ /* Failed to get credentials */
+ NE_FREE(sess->unq_realm);
+ return -1;
+ }
+
+ sess->scheme = auth_scheme_basic;
+
+ CONCAT3(tmp, sess->username, ":", password?password:"");
+ sess->basic = ne_base64(tmp, strlen(tmp));
+ free(tmp);
+
+ NE_FREE(password);
+
+ return 0;
+}
+
+/* Add Basic authentication credentials to a request */
+static char *request_basic(auth_session *sess)
+{
+ char *buf;
+ CONCAT3(buf, "Basic ", sess->basic, "\r\n");
+ return buf;
+}
+
+/* Examine a digest challenge: return 0 if it is a valid Digest challenge,
+ * else non-zero. */
+static int digest_challenge(auth_session *sess,
+ struct auth_challenge *parms)
+{
+ struct ne_md5_ctx tmp;
+ unsigned char tmp_md5[16];
+ char *password;
+
+ /* Verify they've given us the right bits. */
+ if (parms->alg == auth_alg_unknown ||
+ ((parms->alg == auth_alg_md5_sess) &&
+ !(parms->qop_auth || parms->qop_auth_int)) ||
+ parms->realm==NULL || parms->nonce==NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Invalid challenge.");
+ return -1;
+ }
+
+ if (parms->stale) {
+ /* Just a stale response, don't need to get a new username/password */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Stale digest challenge.\n");
+ } else {
+ /* Forget the old session details */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "In digest challenge.\n");
+
+ clean_session(sess);
+ sess->unq_realm = shave_string(parms->realm, '"');
+
+ /* Not a stale response: really need user authentication */
+ if (get_credentials(sess, &password)) {
+ /* Failed to get credentials */
+ NE_FREE(sess->unq_realm);
+ return -1;
+ }
+ }
+ sess->alg = parms->alg;
+ sess->scheme = auth_scheme_digest;
+ sess->unq_nonce = shave_string(parms->nonce, '"');
+ sess->unq_cnonce = get_cnonce();
+ if (parms->domain) {
+ if (parse_domain(sess, parms->domain)) {
+ /* TODO: Handle the error? */
+ }
+ } else {
+ sess->domain = NULL;
+ sess->domain_count = 0;
+ }
+ if (parms->opaque != NULL) {
+ sess->opaque = ne_strdup(parms->opaque); /* don't strip the quotes */
+ }
+
+ if (parms->got_qop) {
+ /* What type of qop are we to apply to the message? */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got qop directive.\n");
+ sess->nonce_count = 0;
+ if (parms->qop_auth_int) {
+ sess->qop = auth_qop_auth_int;
+ } else {
+ sess->qop = auth_qop_auth;
+ }
+ } else {
+ /* No qop at all/ */
+ sess->qop = auth_qop_none;
+ }
+
+ if (!parms->stale) {
+ /* Calculate H(A1).
+ * tmp = H(unq(username-value) ":" unq(realm-value) ":" passwd)
+ */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating H(A1).\n");
+ ne_md5_init_ctx(&tmp);
+ ne_md5_process_bytes(sess->username, strlen(sess->username), &tmp);
+ ne_md5_process_bytes(":", 1, &tmp);
+ ne_md5_process_bytes(sess->unq_realm, strlen(sess->unq_realm), &tmp);
+ ne_md5_process_bytes(":", 1, &tmp);
+ if (password != NULL)
+ ne_md5_process_bytes(password, strlen(password), &tmp);
+ ne_md5_finish_ctx(&tmp, tmp_md5);
+ if (sess->alg == auth_alg_md5_sess) {
+ unsigned char a1_md5[16];
+ struct ne_md5_ctx a1;
+ char tmp_md5_ascii[33];
+ /* Now we calculate the SESSION H(A1)
+ * A1 = H(...above...) ":" unq(nonce-value) ":" unq(cnonce-value)
+ */
+ ne_md5_to_ascii(tmp_md5, tmp_md5_ascii);
+ ne_md5_init_ctx(&a1);
+ ne_md5_process_bytes(tmp_md5_ascii, 32, &a1);
+ ne_md5_process_bytes(":", 1, &a1);
+ ne_md5_process_bytes(sess->unq_nonce, strlen(sess->unq_nonce), &a1);
+ ne_md5_process_bytes(":", 1, &a1);
+ ne_md5_process_bytes(sess->unq_cnonce, strlen(sess->unq_cnonce), &a1);
+ ne_md5_finish_ctx(&a1, a1_md5);
+ ne_md5_to_ascii(a1_md5, sess->h_a1);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Session H(A1) is [%s]\n", sess->h_a1);
+ } else {
+ ne_md5_to_ascii(tmp_md5, sess->h_a1);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "H(A1) is [%s]\n", sess->h_a1);
+ }
+
+ NE_FREE(password);
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "I like this Digest challenge.\n");
+
+ return 0;
+}
+
+/* callback for ne_pull_request_body. */
+static int digest_body(void *userdata, const char *buf, size_t count)
+{
+ struct ne_md5_ctx *ctx = userdata;
+ ne_md5_process_bytes(buf, count, ctx);
+ return 0;
+}
+
+/* Return Digest authentication credentials header value for the given
+ * session. */
+static char *request_digest(auth_session *sess, struct auth_request *req)
+{
+ struct ne_md5_ctx a2, rdig;
+ unsigned char a2_md5[16], rdig_md5[16];
+ char a2_md5_ascii[33], rdig_md5_ascii[33];
+ char nc_value[9] = {0};
+ const char *qop_value; /* qop-value */
+ ne_buffer *ret;
+
+ /* Increase the nonce-count */
+ if (sess->qop != auth_qop_none) {
+ sess->nonce_count++;
+ snprintf(nc_value, 9, "%08x", sess->nonce_count);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Nonce count is %d, nc is [%s]\n",
+ sess->nonce_count, nc_value);
+ }
+ qop_value = qop_values[sess->qop];
+
+ /* Calculate H(A2). */
+ ne_md5_init_ctx(&a2);
+ ne_md5_process_bytes(req->method, strlen(req->method), &a2);
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(req->uri, strlen(req->uri), &a2);
+
+ if (sess->qop == auth_qop_auth_int) {
+ struct ne_md5_ctx body;
+ char tmp_md5_ascii[33];
+ unsigned char tmp_md5[16];
+
+ ne_md5_init_ctx(&body);
+
+ /* Calculate H(entity-body): pull in the request body from
+ * where-ever it is coming from, and calculate the digest. */
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body...\n");
+ ne_pull_request_body(req->request, digest_body, &body);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body done.\n");
+
+ ne_md5_finish_ctx(&body, tmp_md5);
+ ne_md5_to_ascii(tmp_md5, tmp_md5_ascii);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "H(entity-body) is [%s]\n", tmp_md5_ascii);
+
+ /* Append to A2 */
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(tmp_md5_ascii, 32, &a2);
+ }
+ ne_md5_finish_ctx(&a2, a2_md5);
+ ne_md5_to_ascii(a2_md5, a2_md5_ascii);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "H(A2): %s\n", a2_md5_ascii);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating Request-Digest.\n");
+ /* Now, calculation of the Request-Digest.
+ * The first section is the regardless of qop value
+ * H(A1) ":" unq(nonce-value) ":" */
+ ne_md5_init_ctx(&rdig);
+
+ /* Use the calculated H(A1) */
+ ne_md5_process_bytes(sess->h_a1, 32, &rdig);
+
+ ne_md5_process_bytes(":", 1, &rdig);
+ ne_md5_process_bytes(sess->unq_nonce, strlen(sess->unq_nonce), &rdig);
+ ne_md5_process_bytes(":", 1, &rdig);
+ if (sess->qop != auth_qop_none) {
+ /* Add on:
+ * nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":"
+ */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Have qop directive, digesting: [%s:%s:%s]\n",
+ nc_value, sess->unq_cnonce, qop_value);
+ ne_md5_process_bytes(nc_value, 8, &rdig);
+ ne_md5_process_bytes(":", 1, &rdig);
+ ne_md5_process_bytes(sess->unq_cnonce, strlen(sess->unq_cnonce), &rdig);
+ ne_md5_process_bytes(":", 1, &rdig);
+ /* Store a copy of this structure (see note below) */
+ sess->stored_rdig = rdig;
+ ne_md5_process_bytes(qop_value, strlen(qop_value), &rdig);
+ ne_md5_process_bytes(":", 1, &rdig);
+ } else {
+ /* Store a copy of this structure... we do this because the
+ * calculation of the rspauth= field in the Auth-Info header
+ * is the same as this digest, up to this point. */
+ sess->stored_rdig = rdig;
+ }
+ /* And finally, H(A2) */
+ ne_md5_process_bytes(a2_md5_ascii, 32, &rdig);
+ ne_md5_finish_ctx(&rdig, rdig_md5);
+ ne_md5_to_ascii(rdig_md5, rdig_md5_ascii);
+
+ ret = ne_buffer_create();
+
+ ne_buffer_concat(ret,
+ "Digest username=\"", sess->username, "\", "
+ "realm=\"", sess->unq_realm, "\", "
+ "nonce=\"", sess->unq_nonce, "\", "
+ "uri=\"", req->uri, "\", "
+ "response=\"", rdig_md5_ascii, "\", "
+ "algorithm=\"", algorithm_names[sess->alg], "\"", NULL);
+
+ if (sess->opaque != NULL) {
+ /* We never unquote it, so it's still quoted here */
+ ne_buffer_concat(ret, ", opaque=", sess->opaque, NULL);
+ }
+
+ if (sess->qop != auth_qop_none) {
+ /* Add in cnonce and nc-value fields */
+ ne_buffer_concat(ret,
+ ", cnonce=\"", sess->unq_cnonce, "\", "
+ "nc=", nc_value, ", "
+ "qop=\"", qop_values[sess->qop], "\"", NULL);
+ }
+
+ ne_buffer_zappend(ret, "\r\n");
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digest request header is %s\n", ret->data);
+
+ return ne_buffer_finish(ret);
+}
+
+/* Pass this the value of the 'Authentication-Info:' header field, if
+ * one is received.
+ * Returns:
+ * 0 if it gives a valid authentication for the server
+ * non-zero otherwise (don't believe the response in this case!).
+ */
+int verify_response(struct auth_request *req, auth_session *sess, const char *value)
+{
+ char **pairs;
+ auth_qop qop = auth_qop_none;
+ char *nextnonce = NULL, /* for the nextnonce= value */
+ *rspauth = NULL, /* for the rspauth= value */
+ *cnonce = NULL, /* for the cnonce= value */
+ *nc = NULL, /* for the nc= value */
+ *unquoted, *qop_value = NULL;
+ int n, nonce_count, okay;
+
+ if (!req->will_handle) {
+ /* Ignore it */
+ return 0;
+ }
+
+ if (sess->scheme != auth_scheme_digest) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Found Auth-Info header not in response to Digest credentials - dodgy.\n");
+ return -1;
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Auth-Info header: %s\n", value);
+
+ pairs = pair_string(value, ',', '=', "\"'", " \r\n\t");
+
+ for (n = 0; pairs[n]!=NULL; n+=2) {
+ unquoted = shave_string(pairs[n+1], '"');
+ if (strcasecmp(pairs[n], "qop") == 0) {
+ qop_value = ne_strdup(pairs[n+1]);
+ if (strcasecmp(pairs[n+1], "auth-int") == 0) {
+ qop = auth_qop_auth_int;
+ } else if (strcasecmp(pairs[n+1], "auth") == 0) {
+ qop = auth_qop_auth;
+ } else {
+ qop = auth_qop_none;
+ }
+ } else if (strcasecmp(pairs[n], "nextnonce") == 0) {
+ nextnonce = ne_strdup(unquoted);
+ } else if (strcasecmp(pairs[n], "rspauth") == 0) {
+ rspauth = ne_strdup(unquoted);
+ } else if (strcasecmp(pairs[n], "cnonce") == 0) {
+ cnonce = ne_strdup(unquoted);
+ } else if (strcasecmp(pairs[n], "nc") == 0) {
+ nc = ne_strdup(pairs[n]);
+ if (sscanf(pairs[n+1], "%x", &nonce_count) != 1) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Couldn't scan [%s] for nonce count.\n",
+ pairs[n+1]);
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got nonce_count: %d\n", nonce_count);
+ }
+ }
+ free(unquoted);
+ }
+ pair_string_free(pairs);
+
+ /* Presume the worst */
+ okay = -1;
+
+ if ((qop != auth_qop_none) && (qop_value != NULL)) {
+ if ((rspauth == NULL) || (cnonce == NULL) || (nc == NULL)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Missing rspauth, cnonce or nc with qop.\n");
+ } else { /* Have got rspauth, cnonce and nc */
+ if (strcmp(cnonce, sess->unq_cnonce) != 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Response cnonce doesn't match.\n");
+ } else if (nonce_count != sess->nonce_count) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Response nonce count doesn't match.\n");
+ } else {
+ /* Calculate and check the response-digest value.
+ * joe: IMO the spec is slightly ambiguous as to whether
+ * we use the qop which WE sent, or the qop which THEY
+ * sent... */
+ struct ne_md5_ctx a2;
+ unsigned char a2_md5[16], rdig_md5[16];
+ char a2_md5_ascii[33], rdig_md5_ascii[33];
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating response-digest.\n");
+
+ /* First off, H(A2) again. */
+ ne_md5_init_ctx(&a2);
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(req->uri, strlen(req->uri), &a2);
+ if (qop == auth_qop_auth_int) {
+ unsigned char heb_md5[16];
+ char heb_md5_ascii[33];
+ /* Add on ":" H(entity-body) */
+ ne_md5_finish_ctx(&req->response_body, heb_md5);
+ ne_md5_to_ascii(heb_md5, heb_md5_ascii);
+ ne_md5_process_bytes(":", 1, &a2);
+ ne_md5_process_bytes(heb_md5_ascii, 32, &a2);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digested [:%s]\n", heb_md5_ascii);
+ }
+ ne_md5_finish_ctx(&a2, a2_md5);
+ ne_md5_to_ascii(a2_md5, a2_md5_ascii);
+
+ /* We have the stored digest-so-far of
+ * H(A1) ":" unq(nonce-value)
+ * [ ":" nc-value ":" unq(cnonce-value) ] for qop
+ * in sess->stored_rdig, to save digesting them again.
+ *
+ */
+ if (qop != auth_qop_none) {
+ /* Add in qop-value */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting qop-value [%s:].\n",
+ qop_value);
+ ne_md5_process_bytes(qop_value, strlen(qop_value),
+ &sess->stored_rdig);
+ ne_md5_process_bytes(":", 1, &sess->stored_rdig);
+ }
+ /* Digest ":" H(A2) */
+ ne_md5_process_bytes(a2_md5_ascii, 32, &sess->stored_rdig);
+ /* All done */
+ ne_md5_finish_ctx(&sess->stored_rdig, rdig_md5);
+ ne_md5_to_ascii(rdig_md5, rdig_md5_ascii);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Calculated response-digest of: [%s]\n",
+ rdig_md5_ascii);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Given response-digest of: [%s]\n",
+ rspauth);
+
+ /* And... do they match? */
+ okay = (strcasecmp(rdig_md5_ascii, rspauth) == 0)?0:-1;
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Matched: %s\n", okay?"nope":"YES!");
+ }
+ free(rspauth);
+ free(cnonce);
+ free(nc);
+ }
+ free(qop_value);
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "No qop directive, auth okay.\n");
+ okay = 0;
+ }
+
+ /* Check for a nextnonce */
+ if (nextnonce != NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Found nextnonce of [%s].\n", nextnonce);
+ if (sess->unq_nonce != NULL)
+ free(sess->unq_nonce);
+ sess->unq_nonce = nextnonce;
+ }
+
+ return okay;
+}
+
+/* A new challenge presented by the server */
+int auth_challenge(auth_session *sess, const char *value)
+{
+ char **pairs, *pnt, *unquoted, *key;
+ struct auth_challenge *chall = NULL, *challenges = NULL;
+ int n, success;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got new auth challenge: %s\n", value);
+
+ /* The header value may be made up of one or more challenges.
+ * We split it down into attribute-value pairs, then search for
+ * schemes in the pair keys.
+ */
+ pairs = pair_string(value, ',', '=', "\"'", " \r\n\t");
+
+ for (n = 0; pairs[n]!=NULL; n+=2) {
+ /* Look for an auth-scheme in the key */
+ pnt = strchr(pairs[n], ' ');
+ if (pnt != NULL) {
+ /* We have a new challenge */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "New challenge.\n");
+ chall = ne_calloc(sizeof *chall);
+
+ chall->next = challenges;
+ challenges = chall;
+ /* Initialize the challenge parameters */
+ /* Which auth-scheme is it (case-insensitive matching) */
+ if (strncasecmp(pairs[n], "basic ", 6) == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Basic scheme.\n");
+ chall->scheme = auth_scheme_basic;
+ } else if (strncasecmp(pairs[n], "digest ", 7) == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n");
+ chall->scheme = auth_scheme_digest;
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n");
+ free(chall);
+ challenges = NULL;
+ break;
+ }
+ /* Now, the real key for this pair starts after the
+ * auth-scheme... skipping whitespace */
+ while (strchr(" \r\n\t", *(++pnt)) != NULL)
+ /* nullop */;
+ key = pnt;
+ } else if (chall == NULL) {
+ /* If we haven't got an auth-scheme, and we're
+ * haven't yet found a challenge, skip this pair.
+ */
+ continue;
+ } else {
+ key = pairs[n];
+ }
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, pairs[n+1]);
+ /* Most values are quoted, so unquote them here */
+ unquoted = shave_string(pairs[n+1], '"');
+ /* Now parse the attribute */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Unquoted pair is: [%s]\n", unquoted);
+ if (strcasecmp(key, "realm") == 0) {
+ chall->realm = pairs[n+1];
+ } else if (strcasecmp(key, "nonce") == 0) {
+ chall->nonce = pairs[n+1];
+ } else if (strcasecmp(key, "opaque") == 0) {
+ chall->opaque = pairs[n+1];
+ } else if (strcasecmp(key, "domain") == 0) {
+ chall->domain = pairs[n+1];
+ } else if (strcasecmp(key, "stale") == 0) {
+ /* Truth value */
+ chall->stale =
+ (strcasecmp(unquoted, "true") == 0);
+ } else if (strcasecmp(key, "algorithm") == 0) {
+ if (strcasecmp(unquoted, "md5") == 0) {
+ chall->alg = auth_alg_md5;
+ } else if (strcasecmp(unquoted, "md5-sess") == 0) {
+ chall->alg = auth_alg_md5_sess;
+ } else {
+ chall->alg = auth_alg_unknown;
+ }
+ } else if (strcasecmp(key, "qop") == 0) {
+ char **qops;
+ int qop;
+ qops = split_string(unquoted, ',', NULL, " \r\n\t");
+ chall->got_qop = 1;
+ for (qop = 0; qops[qop] != NULL; qop++) {
+ if (strcasecmp(qops[qop], "auth") == 0) {
+ chall->qop_auth = 1;
+ } else if (strcasecmp(qops[qop], "auth-int") == 0) {
+ chall->qop_auth_int = 1;
+ }
+ }
+ split_string_free(qops);
+ }
+ free(unquoted);
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Finished parsing parameters.\n");
+
+ /* Did we find any challenges */
+ if (challenges == NULL) {
+ pair_string_free(pairs);
+ return -1;
+ }
+
+ success = 0;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for Digest challenges.\n");
+
+ /* Try a digest challenge */
+ for (chall = challenges; chall != NULL; chall = chall->next) {
+ if (chall->scheme == auth_scheme_digest) {
+ if (!digest_challenge(sess, chall)) {
+ success = 1;
+ break;
+ }
+ }
+ }
+
+ if (!success) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "No good Digest challenges, looking for Basic.\n");
+ for (chall = challenges; chall != NULL; chall = chall->next) {
+ if (chall->scheme == auth_scheme_basic) {
+ if (!basic_challenge(sess, chall)) {
+ success = 1;
+ break;
+ }
+ }
+ }
+
+ if (!success) {
+ /* No good challenges - record this in the session state */
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Did not understand any challenges.\n");
+ }
+
+ }
+
+ /* Remember whether we can now supply the auth details */
+ sess->can_handle = success;
+
+ while (challenges != NULL) {
+ chall = challenges->next;
+ free(challenges);
+ challenges = chall;
+ }
+
+ /* Free up the parsed header values */
+ pair_string_free(pairs);
+
+ return !success;
+}
+
+/* The body reader callback. */
+static void auth_body_reader(void *cookie, const char *block, size_t length)
+{
+ struct ne_md5_ctx *ctx = cookie;
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting %d bytes of response body.\n", length);
+ ne_md5_process_bytes(block, length, ctx);
+}
+
+static void *ah_create(void *session, ne_request *request, const char *method,
+ const char *uri)
+{
+ auth_session *sess = session;
+ struct auth_request *areq = ne_calloc(sizeof *areq);
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "ah_create, for %s\n", sess->resp_hdr);
+
+ areq->session = sess;
+ areq->method = method;
+ areq->uri = uri;
+ areq->request = request;
+
+ ne_add_response_header_handler(request, sess->resp_hdr,
+ ne_duplicate_header, &areq->auth_hdr);
+
+
+ ne_add_response_header_handler(request, sess->resp_info_hdr,
+ ne_duplicate_header,
+ &areq->auth_info_hdr);
+
+ return areq;
+}
+
+
+static void ah_pre_send(void *cookie, ne_buffer *request)
+{
+ struct auth_request *req = cookie;
+ auth_session *sess = req->session;
+
+
+ if (!sess->can_handle) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Not handling session.\n");
+ } else if (!is_in_domain(sess, req->uri)) {
+ /* We have moved out of the authentication domain */
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "URI %s outside session domain, not handling.\n", req->uri);
+ req->will_handle = 0;
+ } else {
+ char *value;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Handling.");
+ req->will_handle = 1;
+
+ if (sess->qop == auth_qop_auth_int) {
+ /* Digest mode / qop=auth-int: take an MD5 digest of the
+ * response body. */
+ ne_md5_init_ctx(&req->response_body);
+ ne_add_response_body_reader(req->request, ne_accept_always,
+ auth_body_reader, &req->response_body);
+ }
+
+ switch(sess->scheme) {
+ case auth_scheme_basic:
+ value = request_basic(sess);
+ break;
+ case auth_scheme_digest:
+ value = request_digest(sess, req);
+ break;
+ default:
+ value = NULL;
+ break;
+ }
+
+ if (value != NULL) {
+ ne_buffer_concat(request, sess->req_hdr, ": ", value, NULL);
+ free(value);
+ }
+
+ /* increase counter so we don't retry this >1. */
+ req->tries++;
+
+ }
+
+}
+
+#define SAFELY(x) ((x) != NULL?(x):"null")
+
+static int ah_post_send(void *cookie, const ne_status *status)
+{
+ struct auth_request *areq = cookie;
+ auth_session *sess = areq->session;
+ int ret = NE_OK;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "ah_post_send (#%d), code is %d (want %d), %s is %s\n",
+ areq->tries, status->code, sess->status_code,
+ SAFELY(sess->resp_hdr), SAFELY(areq->auth_hdr));
+ if (areq->auth_info_hdr != NULL &&
+ verify_response(areq, sess, areq->auth_info_hdr)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n");
+ ne_set_error(sess->sess, sess->fail_msg);
+ ret = NE_ERROR;
+ } else if (status->code == sess->status_code &&
+ areq->auth_hdr != NULL && areq->tries == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge with code %d.\n", status->code);
+ if (!auth_challenge(sess, areq->auth_hdr)) {
+ ret = NE_RETRY;
+ }
+ } else if (areq->tries > 0 && sess->status_code == status->code) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Authentication failed - bad password?\n");
+ clean_session(sess);
+ ret = sess->fail_code;
+ }
+
+ NE_FREE(areq->auth_info_hdr);
+ NE_FREE(areq->auth_hdr);
+
+ return ret;
+}
+
+static void ah_destroy(void *cookie)
+{
+ free(cookie);
+}
+
+static const ne_request_hooks ah_server_hooks = {
+ HOOK_SERVER_ID,
+ ah_create,
+ ah_pre_send,
+ ah_post_send,
+ ah_destroy
+};
+
+static const ne_request_hooks ah_proxy_hooks = {
+ HOOK_PROXY_ID,
+ ah_create,
+ ah_pre_send,
+ ah_post_send,
+ ah_destroy
+};
+
+static void free_auth(void *cookie)
+{
+ auth_session *sess = cookie;
+
+ clean_session(sess);
+ free(sess);
+}
+
+void ne_set_server_auth(ne_session *sess, ne_request_auth callback,
+ void *userdata)
+{
+ auth_session *auth_sess = auth_create(sess, callback, userdata);
+
+ /* Server auth details */
+ auth_sess->status_code = 401;
+ auth_sess->fail_code = NE_AUTH;
+ auth_sess->resp_hdr = "WWW-Authenticate";
+ auth_sess->resp_info_hdr = "Authentication-Info";
+ auth_sess->req_hdr = "Authorization";
+ auth_sess->fail_msg = _("Server was not authenticated correctly.");
+
+ ne_add_hooks(sess, &ah_server_hooks, auth_sess, free_auth);
+}
+
+void ne_set_proxy_auth(ne_session *sess, ne_request_auth callback,
+ void *userdata)
+{
+ auth_session *auth_sess = auth_create(sess, callback, userdata);
+
+ /* Proxy auth details */
+ auth_sess->status_code = 407;
+ auth_sess->fail_code = NE_PROXYAUTH;
+ auth_sess->resp_hdr = "Proxy-Authenticate";
+ auth_sess->resp_info_hdr = "Proxy-Authentication-Info";
+ auth_sess->req_hdr = "Proxy-Authorization";
+ auth_sess->fail_msg = _("Proxy server was not authenticated correctly.");
+
+ ne_add_hooks(sess, &ah_proxy_hooks, auth_sess, free_auth);
+}
+
+void ne_forget_auth(ne_session *sess)
+{
+ clean_session(ne_session_hook_private(sess, HOOK_SERVER_ID));
+ clean_session(ne_session_hook_private(sess, HOOK_PROXY_ID));
+}
+
diff --git a/neon/src/ne_auth.h b/neon/src/ne_auth.h
new file mode 100644
index 000000000..12be1eff0
--- /dev/null
+++ b/neon/src/ne_auth.h
@@ -0,0 +1,53 @@
+/*
+ HTTP authentication routines
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_AUTH_H
+#define NE_AUTH_H
+
+#include "ne_session.h" /* for http_session */
+
+BEGIN_NEON_DECLS
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocated
+ * memory.
+ * Must return:
+ * 0 on success: *username and *password must be non-NULL, and will
+ * be free'd by the HTTP layer when necessary
+ * -1 to cancel (*username and *password are ignored.)
+ */
+typedef int (*ne_request_auth)(
+ void *userdata, const char *realm,
+ char **username, char **password);
+
+/* Set callbacks to handle server and proxy authentication.
+ * userdata is passed as the first argument to the callback. */
+void ne_set_server_auth(ne_session *sess, ne_request_auth callback,
+ void *userdata);
+void ne_set_proxy_auth(ne_session *sess, ne_request_auth callback,
+ void *userdata);
+
+/* Clear any stored authentication details for the given session. */
+void ne_forget_auth(ne_session *sess);
+
+END_NEON_DECLS
+
+#endif /* NE_AUTH_H */
diff --git a/neon/src/ne_basic.c b/neon/src/ne_basic.c
new file mode 100644
index 000000000..22ff2ee1b
--- /dev/null
+++ b/neon/src/ne_basic.c
@@ -0,0 +1,563 @@
+/*
+ Basic HTTP and WebDAV methods
+ Copyright (C) 1999-2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <errno.h>
+
+#include "ne_request.h"
+#include "ne_alloc.h"
+#include "ne_utils.h"
+#include "ne_basic.h"
+#include "ne_207.h"
+
+#ifndef NEON_NODAV
+#include "ne_uri.h"
+#endif
+
+#ifdef USE_DAV_LOCKS
+#include "ne_locks.h"
+#endif
+#include "ne_dates.h"
+#include "ne_i18n.h"
+
+/* Header parser to retrieve Last-Modified date */
+static void get_lastmodified(void *userdata, const char *value) {
+ time_t *modtime = userdata;
+ *modtime = ne_httpdate_parse(value);
+}
+
+int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime)
+{
+ ne_request *req = ne_request_create(sess, "HEAD", uri);
+ int ret;
+
+ ne_add_response_header_handler(req, "Last-Modified", get_lastmodified,
+ modtime);
+
+ *modtime = -1;
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ *modtime = -1;
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+/* PUT's from fd to URI */
+int ne_put(ne_session *sess, const char *uri, int fd)
+{
+ ne_request *req = ne_request_create(sess, "PUT", uri);
+ int ret;
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, 0);
+ ne_lock_using_parent(req, uri);
+#endif
+
+ ne_set_request_body_fd(req, fd);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2)
+ ret = NE_ERROR;
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+/* Conditional HTTP put.
+ * PUTs from fd to uri, returning NE_FAILED if resource as URI has
+ * been modified more recently than 'since'.
+ */
+int
+ne_put_if_unmodified(ne_session *sess, const char *uri, int fd,
+ time_t since)
+{
+ ne_request *req;
+ char *date;
+ int ret;
+
+ if (ne_version_pre_http11(sess)) {
+ time_t modtime;
+ /* Server is not minimally HTTP/1.1 compliant. Do a HEAD to
+ * check the remote mod time. Of course, this makes the
+ * operation very non-atomic, but better than nothing. */
+ ret = ne_getmodtime(sess, uri, &modtime);
+ if (ret != NE_OK) return ret;
+ if (modtime != since)
+ return NE_FAILED;
+ }
+
+ req = ne_request_create(sess, "PUT", uri);
+
+ date = ne_rfc1123_date(since);
+ /* Add in the conditionals */
+ ne_add_request_header(req, "If-Unmodified-Since", date);
+ free(date);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, 0);
+ /* FIXME: this will give 412 if the resource doesn't exist, since
+ * PUT may modify the parent... does that matter? */
+#endif
+
+ ne_set_request_body_fd(req, fd);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK) {
+ if (ne_get_status(req)->code == 412) {
+ ret = NE_FAILED;
+ } else if (ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+struct get_context {
+ int error;
+ size_t total, progress;
+ int fd; /* used in get_to_fd */
+ ne_content_range *range;
+};
+
+int ne_read_file(ne_session *sess, const char *uri,
+ ne_block_reader reader, void *userdata) {
+ struct get_context ctx;
+ ne_request *req = ne_request_create(sess, "GET", uri);
+ int ret;
+
+ /* Read the value of the Content-Length header into ctx.total */
+ ne_add_response_header_handler(req, "Content-Length",
+ ne_handle_numeric_header, &ctx.total);
+
+ ne_add_response_body_reader(req, ne_accept_2xx, reader, userdata);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2)
+ ret = NE_ERROR;
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+static void get_to_fd(void *userdata, const char *block, size_t length)
+{
+ struct get_context *ctx = userdata;
+ ssize_t ret;
+
+ if (!ctx->error) {
+ while (length > 0) {
+ ret = write(ctx->fd, block, length);
+ if (ret < 0) {
+ ctx->error = errno;
+ break;
+ } else {
+ length -= ret;
+ }
+ }
+ }
+}
+
+static int accept_206(void *ud, ne_request *req, ne_status *st)
+{
+ return (st->code == 206);
+}
+
+static void clength_hdr_handler(void *ud, const char *value)
+{
+ struct get_context *ctx = ud;
+ off_t len = strtol(value, NULL, 10);
+
+ if (ctx->range->end == -1) {
+ ctx->range->end = ctx->range->start + len - 1;
+ ctx->range->total = len;
+ }
+ else if (len != ctx->range->total) {
+ NE_DEBUG(NE_DBG_HTTP,
+ "Expecting %ld bytes, got entity of length %ld\n",
+ (long int) ctx->range->total, (long int) len);
+ ctx->error = 1;
+ }
+}
+
+static void content_range_hdr_handler(void *ud, const char *value)
+{
+ struct get_context *ctx = ud;
+
+ if (strncmp(value, "bytes ", 6) != 0) {
+ ctx->error = 1;
+ }
+
+ /* TODO: verify against requested range. */
+}
+
+int ne_get_range(ne_session *sess, const char *uri,
+ ne_content_range *range, int fd)
+{
+ ne_request *req = ne_request_create(sess, "GET", uri);
+ struct get_context ctx;
+ int ret;
+
+ if (range->end == -1) {
+ ctx.total = -1;
+ }
+ else {
+ ctx.total = (range->end - range->start) + 1;
+ }
+
+ ctx.fd = fd;
+ ctx.error = 0;
+ ctx.range = range;
+
+ ne_add_response_header_handler(req, "Content-Length",
+ clength_hdr_handler, &ctx);
+ ne_add_response_header_handler(req, "Content-Range",
+ content_range_hdr_handler,
+ &ctx);
+
+ ne_add_response_body_reader(req, accept_206, get_to_fd, &ctx);
+
+ /* icky casts to long int, which should be at least as large as the
+ * off_t's */
+ if (range->end == -1) {
+ ne_print_request_header(req, "Range", "bytes=%ld-",
+ (long int) range->start);
+ }
+ else {
+ ne_print_request_header(req, "Range", "bytes=%ld-%ld",
+ (long int) range->start,
+ (long int)range->end);
+ }
+ ne_add_request_header(req, "Accept-Ranges", "bytes");
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+ else if (ne_get_status(req)->code != 206) {
+ ne_set_error(sess, _("Server does not allow partial GETs."));
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+
+/* Get to given fd */
+int ne_get(ne_session *sess, const char *uri, int fd)
+{
+ ne_request *req = ne_request_create(sess, "GET", uri);
+ struct get_context ctx;
+ int ret;
+
+ ctx.total = -1;
+ ctx.progress = 0;
+ ctx.fd = fd;
+ ctx.error = 0;
+
+ /* Read the value of the Content-Length header into ctx.total */
+ ne_add_response_header_handler(req, "Content-Length",
+ ne_handle_numeric_header,
+ &ctx.total);
+
+ ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
+
+ ret = ne_request_dispatch(req);
+
+ if (ctx.error) {
+ char buf[BUFSIZ];
+ snprintf(buf, BUFSIZ,
+ _("Could not write to file: %s"), strerror(ctx.error));
+ ne_set_error(sess, buf);
+ ret = NE_ERROR;
+ }
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+
+/* Get to given fd */
+int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer)
+{
+ ne_request *req = ne_request_create(sess, "POST", uri);
+ struct get_context ctx;
+ int ret;
+
+ ctx.total = -1;
+ ctx.fd = fd;
+ ctx.error = 0;
+
+ /* Read the value of the Content-Length header into ctx.total */
+ ne_add_response_header_handler(req, "Content-Length",
+ ne_handle_numeric_header, &ctx.total);
+
+ ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
+
+ ne_set_request_body_buffer(req, buffer, strlen(buffer));
+
+ ret = ne_request_dispatch(req);
+
+ if (ctx.error) {
+ char buf[BUFSIZ];
+ snprintf(buf, BUFSIZ,
+ _("Could not write to file: %s"), strerror(ctx.error));
+ ne_set_error(sess, buf);
+ ret = NE_ERROR;
+ }
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+static void server_hdr_handler(void *userdata, const char *value)
+{
+ char **tokens = split_string(value, ' ', "\"'", NULL);
+ ne_server_capabilities *caps = userdata;
+ int n;
+
+ for (n = 0; tokens[n] != NULL; n++) {
+ if (strncasecmp(tokens[n], "Apache/", 7) == 0 &&
+ strlen(tokens[n]) > 11) { /* 12 == "Apache/1.3.0" */
+ const char *ver = tokens[n] + 7;
+ int count;
+ char **vers;
+ vers = split_string_c(ver, '.', NULL, NULL, &count);
+ /* Apache/1.3.6 and before have broken Expect: 100 support */
+ if (count > 1 && atoi(vers[0]) < 2 &&
+ atoi(vers[1]) < 4 && atoi(vers[2]) < 7) {
+ caps->broken_expect100 = 1;
+ }
+ split_string_free(vers);
+ }
+ }
+
+ split_string_free(tokens);
+}
+
+void ne_content_type_handler(void *userdata, const char *value)
+{
+ ne_content_type *ct = userdata;
+ char *sep, *parms;
+
+ ct->value = ne_strdup(value);
+
+ sep = strchr(ct->value, '/');
+ if (!sep) {
+ NE_FREE(ct->value);
+ return;
+ }
+
+ *++sep = '\0';
+ ct->type = ct->value;
+ ct->subtype = sep;
+
+ parms = strchr(ct->value, ';');
+
+ if (parms) {
+ *parms = '\0';
+ /* TODO: handle charset. */
+ }
+}
+
+static void dav_hdr_handler(void *userdata, const char *value)
+{
+ char **classes, **class;
+ ne_server_capabilities *caps = userdata;
+
+ classes = split_string(value, ',', "\"'", " \r\t\n");
+ for (class = classes; *class!=NULL; class++) {
+
+ if (strcmp(*class, "1") == 0) {
+ caps->dav_class1 = 1;
+ } else if (strcmp(*class, "2") == 0) {
+ caps->dav_class2 = 1;
+ } else if (strcmp(*class, "<http://apache.org/dav/propset/fs/1>") == 0) {
+ caps->dav_executable = 1;
+ }
+ }
+
+ split_string_free(classes);
+
+}
+
+int ne_options(ne_session *sess, const char *uri,
+ ne_server_capabilities *caps)
+{
+ ne_request *req = ne_request_create(sess, "OPTIONS", uri);
+
+ int ret;
+
+ ne_add_response_header_handler(req, "Server", server_hdr_handler, caps);
+ ne_add_response_header_handler(req, "DAV", dav_hdr_handler, caps);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+#ifndef NEON_NODAV
+
+void ne_add_depth_header(ne_request *req, int depth)
+{
+ const char *value;
+ switch(depth) {
+ case NE_DEPTH_ZERO:
+ value = "0";
+ break;
+ case NE_DEPTH_ONE:
+ value = "1";
+ break;
+ default:
+ value = "infinity";
+ break;
+ }
+ ne_add_request_header(req, "Depth", value);
+}
+
+static int copy_or_move(ne_session *sess, int is_move, int overwrite,
+ const char *src, const char *dest )
+{
+ ne_request *req = ne_request_create( sess, is_move?"MOVE":"COPY", src );
+
+#ifdef USE_DAV_LOCKS
+ if (is_move) {
+ ne_lock_using_resource(req, src, NE_DEPTH_INFINITE);
+ }
+ ne_lock_using_resource(req, dest, NE_DEPTH_INFINITE);
+ /* And we need to be able to add members to the destination's parent */
+ ne_lock_using_parent(req, dest);
+#endif
+
+ ne_print_request_header(req, "Destination", "%s://%s%s",
+ ne_get_scheme(sess),
+ ne_get_server_hostport(sess), dest);
+
+ ne_add_request_header(req, "Overwrite", overwrite?"T":"F");
+
+ return ne_simple_request(sess, req);
+}
+
+int ne_copy(ne_session *sess, int overwrite,
+ const char *src, const char *dest)
+{
+ return copy_or_move(sess, 0, overwrite, src, dest);
+}
+
+int ne_move(ne_session *sess, int overwrite,
+ const char *src, const char *dest)
+{
+ return copy_or_move(sess, 1, overwrite, src, dest);
+}
+
+/* Deletes the specified resource. (and in only two lines of code!) */
+int ne_delete(ne_session *sess, const char *uri)
+{
+ ne_request *req = ne_request_create(sess, "DELETE", uri);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, uri, NE_DEPTH_INFINITE);
+ ne_lock_using_parent(req, uri);
+#endif
+
+ /* joe: I asked on the DAV WG list about whether we might get a
+ * 207 error back from a DELETE... conclusion, you shouldn't if
+ * you don't send the Depth header, since we might be an HTTP/1.1
+ * client and a 2xx response indicates success to them. But
+ * it's all a bit unclear. In any case, DAV servers today do
+ * return 207 to DELETE even if we don't send the Depth header.
+ * So we handle 207 errors appropriately. */
+
+ return ne_simple_request(sess, req);
+}
+
+int ne_mkcol(ne_session *sess, const char *uri)
+{
+ ne_request *req;
+ char *real_uri;
+ int ret;
+
+ if (uri_has_trailing_slash(uri)) {
+ real_uri = ne_strdup(uri);
+ } else {
+ CONCAT2(real_uri, uri, "/");
+ }
+
+ req = ne_request_create(sess, "MKCOL", real_uri);
+
+#ifdef USE_DAV_LOCKS
+ ne_lock_using_resource(req, real_uri, 0);
+ ne_lock_using_parent(req, real_uri);
+#endif
+
+ ret = ne_simple_request(sess, req);
+
+ free(real_uri);
+
+ return ret;
+}
+
+#endif /* NEON_NODAV */
diff --git a/neon/src/ne_basic.h b/neon/src/ne_basic.h
new file mode 100644
index 000000000..2daf91960
--- /dev/null
+++ b/neon/src/ne_basic.h
@@ -0,0 +1,136 @@
+/*
+ HTTP/1.1 methods
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_BASIC_H
+#define NE_BASIC_H
+
+#include <sys/types.h> /* for time_t */
+
+#include "ne_request.h"
+
+BEGIN_NEON_DECLS
+
+/* PUT resource at uri, reading request body from f */
+int ne_put(ne_session *sess, const char *uri, int fd);
+
+/* GET resource at uri, writing response body into f */
+int ne_get(ne_session *sess, const char *uri, int fd);
+
+#ifndef NEON_NODAV
+
+/* Basic WebDAV methods:
+ * ne_copy: copy resoure from src to dest
+ * ne_move: move resource from src to dest
+ * -> if overwrite is non-zero, the destination resource
+ * will be overwritten if it exists.
+ * ne_delete: delete resource at uri
+ * ne_mkcol: create a collection at uri (uri MUST have a trailing slash).
+ */
+int ne_copy(ne_session *sess, int overwrite, const char *src, const char *dest);
+int ne_move(ne_session *sess, int overwrite, const char *src, const char *dest);
+int ne_delete(ne_session *sess, const char *uri);
+int ne_mkcol(ne_session *sess, const char *uri);
+
+#define NE_DEPTH_ZERO (0)
+#define NE_DEPTH_ONE (1)
+#define NE_DEPTH_INFINITE (2)
+
+/* Adds a Depth: header to a request */
+void ne_add_depth_header(ne_request *req, int depth);
+
+#endif /* NEON_NODAV */
+
+/* PUT resource at uri as above, only if it has not been modified
+ * since given modtime. If server is HTTP/1.1, uses If-Unmodified-Since
+ * header; guaranteed failure if resource is modified after 'modtime'.
+ * If server is HTTP/1.0, HEAD's the resource first to fetch current
+ * modtime; race condition if resource is modified between HEAD and PUT.
+ */
+int ne_put_if_unmodified(ne_session *sess,
+ const char *uri, int fd, time_t modtime);
+
+/* GET resource at uri, passing response body blocks to 'reader' */
+int ne_read_file(ne_session *sess, const char *uri,
+ ne_block_reader reader, void *userdata);
+
+/* Retrieve modification time of resource at uri, place in *modtime.
+ * (uses HEAD) */
+int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime);
+
+typedef struct {
+ const char *type, *subtype;
+ const char *charset;
+ char *value;
+} ne_content_type;
+
+/* Sets (*ne_content_type)userdata appropriately.
+ * Caller must free ->value after use */
+void ne_content_type_handler(void *userdata, const char *value);
+
+/* Server capabilities: */
+typedef struct {
+ unsigned int broken_expect100:1; /* True if the server is known to
+ * have broken Expect:
+ * 100-continue support; Apache
+ * 1.3.6 and earlier. */
+
+ unsigned int dav_class1; /* True if Class 1 WebDAV server */
+ unsigned int dav_class2; /* True if Class 2 WebDAV server */
+ unsigned int dav_executable; /* True if supports the 'executable'
+ * property a. la. mod_dav */
+} ne_server_capabilities;
+
+/* Determines server capabilities (using OPTIONS).
+ * Pass uri="*" to determine proxy server capabilities if using
+ * a proxy server. */
+int ne_options(ne_session *sess, const char *uri,
+ ne_server_capabilities *caps);
+
+/* Defines a range of bytes, starting at 'start' and ending
+ * at 'end'. 'total' is the number of bytes in the range.
+ */
+typedef struct {
+ off_t start, end, total;
+} ne_content_range;
+
+/* Partial GET. range->start must be >= 0. range->total is ignored.
+ *
+ * If range->end is -1, then the rest of the resource from start is
+ * requested, and range->total and end are filled in on success.
+ *
+ * Otherwise, bytes from range->start to range->end are requested.
+ *
+ * This will write to the CURRENT position of f; so if you want
+ * to do a resume download, use:
+ * struct ne_content_range range;
+ * range.start = resume_from;
+ * range.end = range.start + 999; (= 1000 bytes)
+ * fseek(myfile, resume_from, SEEK_SET);
+ * ne_get_range(sess, uri, &range, myfile); */
+int ne_get_range(ne_session *sess, const char *uri,
+ ne_content_range *range, int fd);
+
+/* Post using buffer as request-body: stream response into f */
+int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer);
+
+END_NEON_DECLS
+
+#endif /* NE_BASIC_H */
diff --git a/neon/src/ne_compress.c b/neon/src/ne_compress.c
new file mode 100644
index 000000000..6478fb9e5
--- /dev/null
+++ b/neon/src/ne_compress.c
@@ -0,0 +1,332 @@
+/*
+ Handling of compressed HTTP responses
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <assert.h>
+
+#include "ne_request.h"
+#include "ne_compress.h"
+#include "ne_utils.h"
+
+#include <zlib.h>
+
+/* Adds support for the 'gzip' Content-Encoding in HTTP. gzip is a
+ * file format which wraps the DEFLATE compression algorithm. zlib
+ * implements DEFLATE: we have to unwrap the gzip format as it comes
+ * off the wire, and hand off chunks of data to be inflated. */
+
+struct ne_decompress_s {
+ ne_session *session; /* associated session. */
+ /* temporary buffer for holding inflated data. */
+ char outbuf[BUFSIZ];
+ z_stream zstr;
+ char *enchdr; /* value of Content-Enconding response header. */
+
+ /* pass blocks back to this. */
+ ne_block_reader reader;
+ void *userdata;
+
+ /* buffer for gzip header bytes. */
+ union {
+ unsigned char buf[10];
+ struct header {
+ unsigned char id1;
+ unsigned char id2;
+ unsigned char cmeth; /* compression method. */
+ unsigned char flags;
+ unsigned int mtime;
+ unsigned char xflags;
+ unsigned char os;
+ } hdr;
+ } in;
+ size_t incount; /* bytes in in.buf */
+
+ /* current state. */
+ enum state {
+ BEFORE_DATA, /* not received any response blocks yet. */
+ PASSTHROUGH, /* response not compressed: passing through. */
+ IN_HEADER, /* received a few bytes of response data, but not
+ * got past the gzip header yet. */
+ POST_HEADER, /* waiting for the end of the NUL-terminated bits. */
+ INFLATING, /* inflating response bytes. */
+ FINISHED, /* inflate has returned Z_STREAM_END: not expecting
+ * any more response data. */
+ ERROR /* inflate bombed. */
+ } state;
+};
+
+#define ID1 0x1f
+#define ID2 0x8b
+
+#define HDR_DONE 0
+#define HDR_EXTENDED 1
+#define HDR_ERROR 2
+
+/* parse_header parses the gzip header, sets the next state and returns
+ * HDR_DONE: all done, bytes following are raw DEFLATE data.
+ * HDR_EXTENDED: all done, expect a NUL-termianted string
+ * before the DEFLATE data
+ * HDR_ERROR: invalid header, give up.
+ */
+static int parse_header(ne_decompress *ctx)
+{
+ struct header *h = &ctx->in.hdr;
+
+ NE_DEBUG(NE_DBG_HTTP, "ID1: %d ID2: %d, cmeth %d, flags %d\n",
+ h->id1, h->id2, h->cmeth, h->flags);
+
+ if (h->id1 != ID1 || h->id2 != ID2 || h->cmeth != 8) {
+ ctx->state = ERROR;
+ ne_set_error(ctx->session, "Compressed stream invalid");
+ return HDR_ERROR;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "mtime: %d, xflags: %d, os: %d\n",
+ h->mtime, h->xflags, h->os);
+
+ /* TODO: we can only handle one NUL-terminated extensions field
+ * currently. Really, we should count the number of bits set, and
+ * skip as many fields as bits set (bailing if any reserved bits
+ * are set. */
+ if (h->flags == 8) {
+ ctx->state = POST_HEADER;
+ return HDR_EXTENDED;
+ } else if (h->flags != 0) {
+ ctx->state = ERROR;
+ ne_set_error(ctx->session, "Compressed stream not supported");
+ return HDR_ERROR;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "compress: Good stream.\n");
+
+ ctx->state = INFLATING;
+ return HDR_DONE;
+}
+
+static int find_token(const char *value, const char *wanted)
+{
+ char *buf = ne_strdup(value), *pnt = buf, *tok;
+ int ret = 0;
+
+ while ((tok = ne_token(&pnt, ',', NULL)) != NULL)
+ {
+ if (strcasecmp(tok, wanted) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+
+ free(buf);
+
+ return ret;
+}
+
+/* inflate it baby. */
+static void do_inflate(ne_decompress *ctx, const char *buf, size_t len)
+{
+ int ret;
+
+ ctx->zstr.avail_in = len;
+ ctx->zstr.next_in = (char *)buf;
+ ctx->zstr.total_in = 0;
+
+ do {
+ ctx->zstr.avail_out = BUFSIZ;
+ ctx->zstr.next_out = ctx->outbuf;
+ ctx->zstr.total_out = 0;
+
+ ret = inflate(&ctx->zstr, Z_NO_FLUSH);
+
+ NE_DEBUG(NE_DBG_HTTP,
+ "compress: inflate %d, %ld bytes out, %d remaining\n",
+ ret, ctx->zstr.total_out, ctx->zstr.avail_in);
+#if 0
+ NE_DEBUG(NE_DBG_HTTPBODY,
+ "Inflated body block (%ld):\n[%.*s]\n",
+ ctx->zstr.total_out, (int)ctx->zstr.total_out,
+ ctx->outbuf);
+#endif
+
+ /* pass on the inflated data */
+ ctx->reader(ctx->userdata, ctx->outbuf, ctx->zstr.total_out);
+
+ } while (ret == Z_OK && ctx->zstr.avail_out == 0);
+
+ if (ret == Z_STREAM_END) {
+ NE_DEBUG(NE_DBG_HTTP, "compress: end of stream.\n");
+ ctx->state = FINISHED;
+ } else if (ret != Z_OK) {
+ ctx->state = ERROR;
+ ne_set_error(ctx->session, "Error reading compressed data.");
+ NE_DEBUG(NE_DBG_HTTP, "compress: inflate failed (%d): %s\n",
+ ret, ctx->zstr.msg);
+ }
+}
+
+static void gz_reader(void *ud, const char *buf, size_t len)
+{
+ ne_decompress *ctx = ud;
+ const char *zbuf;
+ size_t count;
+
+ switch (ctx->state) {
+ case PASSTHROUGH:
+ /* move along there. */
+ ctx->reader(ctx->userdata, buf, len);
+ return;
+
+ case ERROR:
+ /* beyond hope. */
+ return;
+
+ case FINISHED:
+ NE_DEBUG(NE_DBG_HTTP,
+ "compress: %d bytes in after end of stream.\n", len);
+ return;
+
+ case BEFORE_DATA:
+ /* work out whether this is a compressed response or not. */
+ if (ctx->enchdr != NULL && find_token(ctx->enchdr, "gzip")) {
+ NE_DEBUG(NE_DBG_HTTP, "compress: got gzipped stream.\n");
+
+ /* This is the magic bit: using plain inflateInit()
+ * doesn't work, and this does, but I have no idea why..
+ * Google showed me the way. */
+ if (inflateInit2(&ctx->zstr, -MAX_WBITS) != Z_OK) {
+ /* bugger. can't get to the session. */
+ ne_set_error(ctx->session, ctx->zstr.msg);
+ ctx->state = ERROR;
+ return;
+ }
+
+ } else {
+ /* No Content-Encoding header: pass it on. TODO: we could
+ * hack it and register the real callback now. But that
+ * would require add_resp_body_rdr to have defined
+ * ordering semantics etc etc */
+ ctx->state = PASSTHROUGH;
+ ctx->reader(ctx->userdata, buf, len);
+ return;
+ }
+
+ ctx->state = IN_HEADER;
+ /* FALLTHROUGH */
+
+ case IN_HEADER:
+ /* copy as many bytes as possible into the buffer. */
+ if (len + ctx->incount > 10) {
+ count = 10 - ctx->incount;
+ } else {
+ count = len;
+ }
+ memcpy(ctx->in.buf + ctx->incount, buf, count);
+ ctx->incount += count;
+ /* have we got the full header yet? */
+ if (ctx->incount != 10) {
+ return;
+ }
+
+ buf += count;
+ len -= count;
+
+ switch (parse_header(ctx)) {
+ case HDR_ERROR:
+ return;
+ case HDR_EXTENDED:
+ if (len == 0)
+ return;
+ break;
+ case HDR_DONE:
+ if (len > 0) {
+ do_inflate(ctx, buf, len);
+ }
+ return;
+ }
+
+ /* FALLTHROUGH */
+
+ case POST_HEADER:
+ /* eating the filename string. */
+ zbuf = memchr(buf, '\0', len);
+ if (zbuf == NULL) {
+ /* not found it yet. */
+ return;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "compresss: skipped %d header bytes.\n",
+ zbuf - buf);
+ /* found end of string. */
+ len -= (1 + zbuf - buf);
+ buf = zbuf + 1;
+ ctx->state = INFLATING;
+ if (len == 0) {
+ /* end of string was at end of buffer. */
+ return;
+ }
+
+ /* FALLTHROUGH */
+
+ case INFLATING:
+ do_inflate(ctx, buf, len);
+ break;
+ }
+
+}
+
+int ne_decompress_destroy(ne_decompress *ctx)
+{
+ if (ctx->state != PASSTHROUGH && ctx->state != BEFORE_DATA) {
+ /* stream must have been initialized */
+ inflateEnd(&ctx->zstr);
+ }
+ if (ctx->enchdr)
+ free(ctx->enchdr);
+ free(ctx);
+ return (ctx->state == ERROR);
+}
+
+ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt,
+ ne_block_reader rdr, void *userdata)
+{
+ ne_decompress *ctx = ne_calloc(sizeof *ctx);
+
+ ne_add_request_header(req, "Accept-Encoding", "gzip");
+
+ ne_add_response_header_handler(req, "Content-Encoding",
+ ne_duplicate_header, &ctx->enchdr);
+
+ ne_add_response_body_reader(req, acpt, gz_reader, ctx);
+
+ ctx->state = BEFORE_DATA;
+ ctx->reader = rdr;
+ ctx->userdata = userdata;
+ ctx->session = ne_get_session(req);
+
+ return ctx;
+}
diff --git a/neon/src/ne_compress.h b/neon/src/ne_compress.h
new file mode 100644
index 000000000..9fa6b73bb
--- /dev/null
+++ b/neon/src/ne_compress.h
@@ -0,0 +1,44 @@
+/*
+ Compressed HTPT request/response Handling
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_COMPRESS_H
+#define NE_COMPRESS_H
+
+#include "ne_request.h"
+
+typedef struct ne_decompress_s ne_decompress;
+
+/* Call this to register a 'reader' callback which will be passed
+ * blocks of response body (if the 'acceptance' callback is
+ * successful). If the response body is returned compressed by the
+ * server, this reader will receive UNCOMPRESSED blocks.
+ *
+ * Returns pointer to context object which must be passed to
+ * ne_decompress_destroy after the request has been dispatched, to
+ * free any internal state. */
+ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response accpt,
+ ne_block_reader rdr, void *userdata);
+
+/* Free's up internal state. Returns non-zero if errors occured during
+ * decompression: the session error string will have the error. */
+int ne_decompress_destroy(ne_decompress *ctx);
+
+#endif /* NE_COMPRESS_H */
diff --git a/neon/src/ne_cookies.c b/neon/src/ne_cookies.c
new file mode 100644
index 000000000..4d99ef60c
--- /dev/null
+++ b/neon/src/ne_cookies.c
@@ -0,0 +1,142 @@
+/*
+ Basic cookie support for neon
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* A nice demo of hooks, since it doesn't need any external
+ * interface to muck with the stored states.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#include "ne_cookies.h"
+
+#include "ne_request.h"
+#include "ne_string.h"
+#include "ne_alloc.h"
+
+static void set_cookie_hdl(void *userdata, const char *value)
+{
+ char **pairs = pair_string(value, ';', '=', HTTP_QUOTES, HTTP_WHITESPACE);
+ ne_cookie *cook;
+ ne_cookie_cache *cache = userdata;
+ int n;
+
+ /* Check sanity */
+ if (pairs[0] == NULL || pairs[1] == NULL) {
+ /* yagaboo */
+ return;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Got cookie name=%s\n", pairs[0]);
+
+ /* Search for a cookie of this name */
+ NE_DEBUG(NE_DBG_HTTP, "Searching for existing cookie...\n");
+ for (cook = cache->cookies; cook != NULL; cook = cook->next) {
+ if (strcasecmp(cook->name, pairs[0]) == 0) {
+ break;
+ }
+ }
+
+ if (cook == NULL) {
+ NE_DEBUG(NE_DBG_HTTP, "New cookie.\n");
+ cook = ne_malloc(sizeof(ne_cookie));
+ memset(cook, 0, sizeof(ne_cookie));
+ cook->name = pairs[0];
+ cook->next = cache->cookies;
+ cache->cookies = cook;
+ } else {
+ /* Free the old value */
+ free(cook->value);
+ }
+
+ cook->value = pairs[1];
+
+ for (n = 2; pairs[n] != NULL; n+=2) {
+ NE_DEBUG(NE_DBG_HTTP, "Cookie parm %s=%s\n", pairs[n], pairs[n+1]);
+ if (strcasecmp(pairs[n], "path") == 0) {
+ cook->path = pairs[n+1];
+ pairs[n+1] = NULL;
+ } else if (strcasecmp(pairs[n], "max-age") == 0) {
+ int t = atoi(pairs[n+1]);
+ cook->expiry = time(NULL) + (time_t)t;
+ } else if (strcasecmp(pairs[n], "domain") == 0) {
+ cook->domain = pairs[n+1];
+ pairs[n+1] = NULL;
+ }
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "End of parms.\n");
+
+ pair_string_free(pairs);
+}
+
+static void *create(void *session, ne_request *req, const char *method, const char *uri)
+{
+ ne_cookie_cache *cache = session;
+ ne_add_response_header_handler(req, "Set-Cookie", set_cookie_hdl, cache);
+ return cache;
+}
+
+/* Just before sending the request: let them add headers if they want */
+static void pre_send(void *private, ne_buffer *request)
+{
+ ne_cookie_cache *cache = private;
+ ne_cookie *cook;
+
+ if (cache->cookies == NULL) {
+ return;
+ }
+
+ ne_buffer_zappend(request, "Cookie: ");
+
+ for (cook = cache->cookies; cook != NULL; cook=cook->next) {
+ ne_buffer_concat(request, cook->name, "=", cook->value, NULL);
+ if (cook->next != NULL) {
+ ne_buffer_zappend(request, "; ");
+ }
+ }
+
+ ne_buffer_zappend(request, EOL);
+
+}
+
+static void destroy(void *private)
+{
+ /* FIXME */
+ return;
+}
+
+ne_request_hooks ne_cookie_hooks = {
+ "http://www.webdav.org/neon/hooks/cookies",
+ create,
+ pre_send,
+ NULL,
+ destroy
+};
diff --git a/neon/src/ne_cookies.h b/neon/src/ne_cookies.h
new file mode 100644
index 000000000..0025ac3df
--- /dev/null
+++ b/neon/src/ne_cookies.h
@@ -0,0 +1,50 @@
+/*
+ HTTP Request Handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_COOKIES_H
+#define NE_COOKIES_H
+
+#include "ne_request.h"
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+struct ne_cookie_s;
+typedef struct ne_cookie_s ne_cookie;
+
+struct ne_cookie_s {
+ char *name, *value;
+ unsigned int secure:1;
+ unsigned int discard:1;
+ char *domain, *path;
+ time_t expiry; /* time at which the cookie expires */
+ ne_cookie *next;
+};
+
+typedef struct {
+ ne_cookie *cookies;
+} ne_cookie_cache;
+
+extern ne_request_hooks ne_cookie_hooks;
+
+END_NEON_DECLS
+
+#endif /* NE_COOKIES_H */
diff --git a/neon/src/ne_dates.c b/neon/src/ne_dates.c
new file mode 100644
index 000000000..34f353970
--- /dev/null
+++ b/neon/src/ne_dates.c
@@ -0,0 +1,191 @@
+/*
+ Date manipulation routines
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <time.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include "ne_alloc.h"
+#include "ne_dates.h"
+
+/* Generic date manipulation routines. */
+
+/* RFC1123: Sun, 06 Nov 1994 08:49:37 GMT */
+#define RFC1123_FORMAT "%3s, %02d %3s %4d %02d:%02d:%02d GMT"
+/* RFC850: Sunday, 06-Nov-94 08:49:37 GMT */
+#define RFC1036_FORMAT "%s, %2d-%3s-%2d %2d:%2d:%2d GMT"
+/* asctime: Wed Jun 30 21:49:08 1993 */
+#define ASCTIME_FORMAT "%3s %3s %2d %2d:%2d:%2d %4d"
+
+static const char *rfc1123_weekdays[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+static const char *short_months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* Returns the time/date GMT, in RFC1123-type format: eg
+ * Sun, 06 Nov 1994 08:49:37 GMT. */
+char *ne_rfc1123_date(time_t anytime) {
+ struct tm *gmt;
+ char *ret;
+ gmt = gmtime(&anytime);
+ if (gmt == NULL)
+ return NULL;
+ ret = ne_malloc(29 + 1); /* dates are 29 chars long */
+/* it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+ snprintf(ret, 30, RFC1123_FORMAT,
+ rfc1123_weekdays[gmt->tm_wday], gmt->tm_mday,
+ short_months[gmt->tm_mon], 1900 + gmt->tm_year,
+ gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
+
+ return ret;
+}
+
+/* Takes an RFC1123-formatted date string and returns the time_t.
+ * Returns (time_t)-1 if the parse fails. */
+time_t ne_rfc1123_parse(const char *date)
+{
+ struct tm gmt = {0};
+ static char wkday[4], mon[4];
+ int n;
+/* it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+ n = sscanf(date, RFC1123_FORMAT,
+ wkday, &gmt.tm_mday, mon, &gmt.tm_year, &gmt.tm_hour,
+ &gmt.tm_min, &gmt.tm_sec);
+ /* Is it portable to check n==7 here? */
+ gmt.tm_year -= 1900;
+ for (n=0; n<12; n++)
+ if (strcmp(mon, short_months[n]) == 0)
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ gmt.tm_isdst = -1;
+ return mktime(&gmt);
+}
+
+/* Takes a string containing a RFC1036-style date and returns the time_t */
+time_t ne_rfc1036_parse(const char *date)
+{
+ struct tm gmt = {0};
+ int n;
+ static char wkday[10], mon[4];
+ /* RFC850/1036 style dates: Sunday, 06-Nov-94 08:49:37 GMT */
+ n = sscanf(date, RFC1036_FORMAT,
+ wkday, &gmt.tm_mday, mon, &gmt.tm_year,
+ &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec);
+ /* portable to check n here? */
+ for (n=0; n<12; n++)
+ if (strcmp(mon, short_months[n]) == 0)
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ gmt.tm_isdst = -1;
+ return mktime(&gmt);
+}
+
+
+/* (as)ctime dates are like:
+ * Wed Jun 30 21:49:08 1993
+ */
+time_t ne_asctime_parse(const char *date)
+{
+ struct tm gmt = {0};
+ int n;
+ static char wkday[4], mon[4];
+ n = sscanf(date, ASCTIME_FORMAT,
+ wkday, mon, &gmt.tm_mday,
+ &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec,
+ &gmt.tm_year);
+ /* portable to check n here? */
+ for (n=0; n<12; n++)
+ if (strcmp(mon, short_months[n]) == 0)
+ break;
+ /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+ * since the mktime will then fail */
+ gmt.tm_mon = n;
+ gmt.tm_isdst = -1;
+ return mktime(&gmt);
+}
+
+/* HTTP-date parser */
+time_t ne_httpdate_parse(const char *date)
+{
+ time_t tmp;
+ tmp = ne_rfc1123_parse(date);
+ if (tmp == -1) {
+ tmp = ne_rfc1036_parse(date);
+ if (tmp == -1)
+ tmp = ne_asctime_parse(date);
+ }
+ return tmp;
+}
+
+#undef RFC1036_FORMAT
+#undef ASCTIME_FORMAT
+#undef RFC1123_FORMAT
+
+#ifdef RFC1123_TEST
+
+int main(int argc, char **argv) {
+ time_t now, in;
+ char *out;
+ if (argc > 1) {
+ printf("Got: %s\n", argv[1]);
+ in = ne_rfc1123_parse(argv[1]);
+ printf("Parsed: %d\n", in);
+ out = ne_rfc1123_date(in);
+ printf("Back again: %s\n", out);
+ } else {
+ now = time(NULL);
+ out = ne_rfc1123_date(now);
+ in = ne_rfc1123_parse(out);
+ printf("time(NULL) = %d\n", now);
+ printf("RFC1123 Time: [%s]\n", out);
+ printf("Parsed = %d\n", in);
+ out = ne_rfc1123_date(in);
+ printf("Back again: [%s]\n", out);
+ }
+ return 0;
+}
+
+#endif
+
+
diff --git a/neon/src/ne_dates.h b/neon/src/ne_dates.h
new file mode 100644
index 000000000..70163003f
--- /dev/null
+++ b/neon/src/ne_dates.h
@@ -0,0 +1,49 @@
+/*
+ Date manipulation routines
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef DATES_H
+#define DATES_H
+
+#include <sys/types.h>
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Date manipulation routines as per RFC1123 and RFC1036 */
+
+/* Return current date/time in RFC1123 format */
+char *ne_rfc1123_date(time_t anytime);
+
+/* Returns time from date/time in RFC1123 format */
+time_t ne_rfc1123_parse(const char *date);
+
+time_t ne_rfc1036_parse(const char *date);
+
+/* Parses asctime date string */
+time_t ne_asctime_parse(const char *date);
+
+/* Parse an HTTP-date as per RFC2616 */
+time_t ne_httpdate_parse(const char *date);
+
+END_NEON_DECLS
+
+#endif /* DATES_H */
diff --git a/neon/src/ne_defs.h b/neon/src/ne_defs.h
new file mode 100644
index 000000000..f029edf28
--- /dev/null
+++ b/neon/src/ne_defs.h
@@ -0,0 +1,10 @@
+
+#undef BEGIN_NEON_DECLS
+#undef END_NEON_DECLS
+#ifdef __cplusplus
+# define BEGIN_NEON_DECLS extern "C" {
+# define END_NEON_DECLS }
+#else
+# define BEGIN_NEON_DECLS /* empty */
+# define END_NEON_DECLS /* empty */
+#endif
diff --git a/neon/src/ne_i18n.c b/neon/src/ne_i18n.c
new file mode 100644
index 000000000..a0df0ac07
--- /dev/null
+++ b/neon/src/ne_i18n.c
@@ -0,0 +1,32 @@
+/*
+ Internationalization of neon
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+void neon_i18n_init(void)
+{
+#if defined(ENABLE_NLS) && defined(NEON_IS_LIBRARY)
+ /* if neon is build bundled in (i.e., not as a standalone
+ * library), then there is probably no point in this, since the
+ * messages won't be pointing in the right direction.
+ * there's not really any point in doing this if neon is
+ * a library since the messages aren't i18n'ized, but... */
+ bindtextdomain("neon", LOCALEDIR);
+#endif
+}
diff --git a/neon/src/ne_i18n.h b/neon/src/ne_i18n.h
new file mode 100644
index 000000000..c1770e916
--- /dev/null
+++ b/neon/src/ne_i18n.h
@@ -0,0 +1,37 @@
+/*
+ Internationalization of neon
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NEON_I18N_H
+#define NEON_I18N_H
+
+#undef _
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(str) gettext(str)
+#else
+#define _(str) (str)
+#endif /* ENABLE_NLS */
+#define N_(str) (str)
+
+/* Initialize i18n in neon */
+void neon_i18n_init(void);
+
+#endif /* NEON_I18N_H */
diff --git a/neon/src/ne_locks.c b/neon/src/ne_locks.c
new file mode 100644
index 000000000..b6f4686e5
--- /dev/null
+++ b/neon/src/ne_locks.c
@@ -0,0 +1,618 @@
+/*
+ WebDAV Class 2 locking operations
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ctype.h> /* for isdigit() */
+
+#include "ne_alloc.h"
+
+#include "ne_request.h"
+#include "ne_xml.h"
+#include "ne_locks.h"
+#include "ne_uri.h"
+#include "ne_basic.h"
+#include "ne_props.h"
+#include "ne_207.h"
+#include "ne_i18n.h"
+
+#define HOOK_ID "http://webdav.org/neon/hooks/webdav-locking"
+
+/* The list of locks to submit in an If header
+ * for the current requests */
+struct submit_locks {
+ const struct ne_lock *lock;
+ const char *uri;
+ struct submit_locks *next;
+};
+
+struct ne_lock_session_s {
+ struct ne_lock *locks;
+};
+
+/* Per-request lock structure */
+struct request_locks {
+ struct submit_locks *locks; /* for the If header */
+ ne_lock_session *session;
+};
+
+/* Context for PROPFIND/lockdiscovery callbacks */
+struct discover_ctx {
+ ne_lock_result results;
+ void *userdata;
+};
+
+/* Hook callbacks */
+static void *create(void *session, ne_request *req,
+ const char *method, const char *uri);
+static void pre_send(void *private, ne_buffer *req);
+static void destroy(void *private);
+
+/* The hooks are needed for construction of the If header */
+static ne_request_hooks lock_hooks = {
+ HOOK_ID, /* unique id for the locking hooks */
+ create,
+ pre_send,
+ NULL, /* post_send not needed */
+ destroy
+};
+
+/* Element ID's start at HIP_ELM_UNUSED and work upwards */
+
+#define NE_ELM_LOCK_FIRST (NE_ELM_207_UNUSED)
+
+#define NE_ELM_lockdiscovery (NE_ELM_LOCK_FIRST)
+#define NE_ELM_activelock (NE_ELM_LOCK_FIRST + 1)
+#define NE_ELM_lockscope (NE_ELM_LOCK_FIRST + 2)
+#define NE_ELM_locktype (NE_ELM_LOCK_FIRST + 3)
+#define NE_ELM_depth (NE_ELM_LOCK_FIRST + 4)
+#define NE_ELM_owner (NE_ELM_LOCK_FIRST + 5)
+#define NE_ELM_timeout (NE_ELM_LOCK_FIRST + 6)
+#define NE_ELM_locktoken (NE_ELM_LOCK_FIRST + 7)
+#define NE_ELM_lockinfo (NE_ELM_LOCK_FIRST + 8)
+#define NE_ELM_write (NE_ELM_LOCK_FIRST + 9)
+#define NE_ELM_exclusive (NE_ELM_LOCK_FIRST + 10)
+#define NE_ELM_shared (NE_ELM_LOCK_FIRST + 11)
+
+static const struct ne_xml_elm lock_elms[] = {
+#define A(x) { "DAV:", #x, NE_ELM_ ## x, NE_XML_COLLECT } /* ANY */
+#define D(x) { "DAV:", #x, NE_ELM_ ## x, 0 } /* normal */
+#define C(x) { "DAV:", #x, NE_ELM_ ## x, NE_XML_CDATA } /* (#PCDATA) */
+#define E(x) { "DAV:", #x, NE_ELM_ ## x, 0 /* LEAF */ } /* EMPTY */
+ D(lockdiscovery), D(activelock),
+ D(prop),
+ D(lockscope), D(locktype), C(depth), A(owner), C(timeout), D(locktoken),
+ /* no lockentry */
+ D(lockinfo), D(lockscope), D(locktype),
+ E(write), E(exclusive), E(shared),
+ C(href),
+#undef A
+#undef D
+#undef C
+#undef E
+ { NULL, 0, 0 }
+};
+
+static const ne_propname lock_props[] = {
+ { "DAV:", "lockdiscovery" },
+ { NULL }
+};
+
+static void *create(void *session, ne_request *req,
+ const char *method, const char *uri)
+{
+ struct request_locks *rl = ne_calloc(sizeof *rl);
+ rl->session = session;
+ return rl;
+}
+
+static void pre_send(void *private, ne_buffer *req)
+{
+ struct request_locks *rl = private;
+
+ if (rl->locks != NULL) {
+ struct submit_locks *item;
+
+ /* Add in the If header */
+ ne_buffer_zappend(req, "If:");
+ for (item = rl->locks; item != NULL; item = item->next) {
+ ne_buffer_concat(req, " <", item->lock->uri, "> (<",
+ item->lock->token, ">)", NULL);
+ }
+ ne_buffer_zappend(req, EOL);
+ }
+}
+
+static void destroy(void *priv)
+{
+ struct request_locks *rl = priv;
+ struct submit_locks *lock, *next;
+
+ for (lock = rl->locks; lock != NULL; lock = next) {
+ next = lock->next;
+ free(lock);
+ }
+
+ free(rl);
+}
+
+static void free_locks(void *session)
+{
+ ne_lock_session *sess = session;
+ struct ne_lock *lk = sess->locks;
+
+ while (lk) {
+ struct ne_lock *nextlk = lk->next;
+ ne_lock_free(lk);
+ lk = nextlk;
+ }
+
+ free(sess);
+}
+
+ne_lock_session *ne_lock_register(ne_session *sess)
+{
+ ne_lock_session *locksess = ne_calloc(sizeof *locksess);
+
+ /* Register the hooks */
+ ne_add_hooks(sess, &lock_hooks, locksess, free_locks);
+
+ return locksess;
+}
+
+/* Submit the given lock for the given URI */
+static void submit_lock(struct request_locks *rl, struct ne_lock *lock,
+ const char *uri)
+{
+ struct submit_locks *slock;
+
+ /* Check for dups */
+ for (slock = rl->locks; slock != NULL; slock = slock->next) {
+ if (strcasecmp(slock->lock->token, lock->token) == 0)
+ return;
+ }
+
+ slock = ne_calloc(sizeof *slock);
+ slock->lock = lock;
+ slock->uri = uri;
+ slock->next = rl->locks;
+ rl->locks = slock;
+}
+
+struct ne_lock *ne_lock_find(ne_lock_session *sess, const char *uri)
+{
+ struct ne_lock *cur;
+ for (cur = sess->locks; cur != NULL; cur = cur->next) {
+ if (uri_compare(uri, cur->uri) == 0)
+ return cur;
+ }
+ return NULL;
+}
+
+void ne_lock_using_parent(ne_request *req, const char *uri)
+{
+ struct request_locks *rl = ne_request_hook_private(req, HOOK_ID);
+ char *parent;
+
+ if (rl == NULL)
+ return;
+
+ parent = uri_parent(uri);
+
+ if (parent != NULL) {
+ struct ne_lock *lock;
+ /* Find any locks on the parent resource.
+ * FIXME: should check for depth-infinity locks too. */
+ lock = ne_lock_find(rl->session, parent);
+ if (lock) {
+ NE_DEBUG(NE_DBG_LOCKS, "Locked parent, %s on %s\n", lock->token,
+ lock->uri);
+ submit_lock(rl, lock, uri);
+ }
+ free(parent);
+ }
+}
+
+int ne_lock_iterate(ne_lock_session *sess,
+ ne_lock_walkfunc func, void *userdata)
+{
+ struct ne_lock *lock;
+ int count = 0;
+
+ for (lock = sess->locks; lock != NULL; lock = lock->next) {
+ if (func != NULL) {
+ (*func)(lock, userdata);
+ }
+ count++;
+ }
+
+ return count;
+}
+
+void ne_lock_using_resource(ne_request *req, const char *uri, int depth)
+{
+ /* Grab the private cookie for this request */
+ struct request_locks *rl = ne_request_hook_private(req, HOOK_ID);
+ struct ne_lock *lock; /* all the known locks */
+ int match;
+
+ if (rl == NULL)
+ return;
+
+ /* Iterate over the list of session locks to see if any of
+ * them apply to this resource */
+ for (lock = rl->session->locks; lock != NULL; lock = lock->next) {
+
+ match = 0;
+
+ if (depth == NE_DEPTH_INFINITE && uri_childof(uri, lock->uri)) {
+ /* Case 1: this is a depth-infinity request which will
+ * modify a lock somewhere inside the collection. */
+ NE_DEBUG(NE_DBG_LOCKS, "Has child: %s\n", lock->token);
+ match = 1;
+ }
+ else if (uri_compare(uri, lock->uri) == 0) {
+ /* Case 2: this request is directly on a locked resource */
+ NE_DEBUG(NE_DBG_LOCKS, "Has direct lock: %s\n", lock->token);
+ match = 1;
+ }
+ else if (lock->depth == NE_DEPTH_INFINITE &&
+ uri_childof(lock->uri, uri)) {
+ /* Case 3: there is a higher-up infinite-depth lock which
+ * covers the resource that this request will modify. */
+ NE_DEBUG(NE_DBG_LOCKS, "Is child of: %s\n", lock->token);
+ match = 1;
+ }
+
+ if (match) {
+ submit_lock(rl, lock, uri);
+ }
+ }
+
+}
+
+void ne_lock_add(ne_lock_session *sess, struct ne_lock *lock)
+{
+ if (sess->locks != NULL) {
+ sess->locks->prev = lock;
+ }
+ lock->prev = NULL;
+ lock->next = sess->locks;
+ sess->locks = lock;
+}
+
+void ne_lock_remove(ne_lock_session *sess, struct ne_lock *lock)
+{
+ if (lock->prev != NULL) {
+ lock->prev->next = lock->next;
+ } else {
+ sess->locks = lock->next;
+ }
+ if (lock->next != NULL) {
+ lock->next->prev = lock->prev;
+ }
+}
+
+struct ne_lock *ne_lock_copy(const struct ne_lock *lock)
+{
+ struct ne_lock *ret = ne_calloc(sizeof *ret);
+
+ /* TODO: check whether uri or token are NULL? Or, maybe, might as
+ * well crash now if they are, since something else will later
+ * on. */
+ ret->uri = ne_strdup(lock->uri);
+ ret->depth = lock->depth;
+ ret->type = lock->type;
+ ret->scope = lock->scope;
+ ret->token = ne_strdup(lock->token);
+ if (lock->owner) ret->owner = ne_strdup(lock->owner);
+ ret->timeout = lock->timeout;
+
+ return ret;
+}
+
+void ne_lock_free(struct ne_lock *lock)
+{
+ NE_FREE(lock->uri);
+ NE_FREE(lock->owner);
+ NE_FREE(lock->token);
+ free(lock);
+}
+
+int ne_unlock(ne_session *sess, struct ne_lock *lock)
+{
+ ne_request *req = ne_request_create(sess, "UNLOCK", lock->uri);
+ int ret;
+
+ ne_print_request_header(req, "Lock-Token", "<%s>", lock->token);
+
+ /* TODO: need this or not?
+ * it definitely goes away if lock-null resources go away */
+ ne_lock_using_parent(req, lock->uri);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass == 2) {
+ ret = NE_OK;
+ } else {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child) {
+ NE_DEBUG(NE_DBG_XML, "ne_locks: check_context %d in %d\n", child, parent);
+ switch (parent) {
+ case NE_ELM_root:
+ /* TODO: for LOCK requests only...
+ * shouldn't allow this for PROPFIND really */
+ if (child == NE_ELM_prop)
+ return NE_XML_VALID;
+ break;
+ case NE_ELM_prop:
+ if (child == NE_ELM_lockdiscovery)
+ return NE_XML_VALID;
+ break;
+ case NE_ELM_lockdiscovery:
+ if (child == NE_ELM_activelock)
+ return NE_XML_VALID;
+ break;
+ case NE_ELM_activelock:
+ switch (child) {
+ case NE_ELM_lockscope:
+ case NE_ELM_locktype:
+ case NE_ELM_depth:
+ case NE_ELM_owner:
+ case NE_ELM_timeout:
+ case NE_ELM_locktoken:
+ return NE_XML_VALID;
+ default:
+ break;
+ }
+ break;
+ case NE_ELM_lockscope:
+ switch (child) {
+ case NE_ELM_exclusive:
+ case NE_ELM_shared:
+ return NE_XML_VALID;
+ default:
+ break;
+ }
+ case NE_ELM_locktype:
+ if (child == NE_ELM_write)
+ return NE_XML_VALID;
+ break;
+ /* ... depth is PCDATA, owner is COLLECT, timeout is PCDATA */
+ case NE_ELM_locktoken:
+ if (child == NE_ELM_href)
+ return NE_XML_VALID;
+ break;
+ }
+ return NE_XML_DECLINE;
+}
+
+static int parse_depth(const char *depth) {
+ if (strcasecmp(depth, "infinity") == 0) {
+ return NE_DEPTH_INFINITE;
+ } else if (isdigit(depth[0])) {
+ return atoi(depth);
+ } else {
+ return -1;
+ }
+}
+
+static long parse_timeout(const char *timeout) {
+ if (strcasecmp(timeout, "infinite") == 0) {
+ return NE_TIMEOUT_INFINITE;
+ } else if (strncasecmp(timeout, "Second-", 7) == 0) {
+ long to = strtol(timeout, NULL, 10);
+ if (to == LONG_MIN || to == LONG_MAX)
+ return NE_TIMEOUT_INVALID;
+ return to;
+ } else {
+ return NE_TIMEOUT_INVALID;
+ }
+}
+
+static void discover_results(void *userdata, const char *href,
+ const ne_prop_result_set *set)
+{
+ struct discover_ctx *ctx = userdata;
+ struct ne_lock *lock = ne_propset_private(set);
+
+ if (lock == NULL)
+ return;
+
+ lock->uri = ne_strdup(href);
+
+ ctx->results(ctx->userdata, lock,
+ href, ne_propset_status(set, &lock_props[0]));
+
+
+ ne_lock_free(lock);
+
+ NE_DEBUG(NE_DBG_LOCKS, "End of response for %s\n", href);
+}
+
+static int
+end_element_common(struct ne_lock *l, const struct ne_xml_elm *elm,
+ const char *cdata)
+{
+ switch (elm->id){
+ case NE_ELM_write:
+ l->type = ne_locktype_write;
+ break;
+ case NE_ELM_exclusive:
+ l->scope = ne_lockscope_exclusive;
+ break;
+ case NE_ELM_shared:
+ l->scope = ne_lockscope_shared;
+ break;
+ case NE_ELM_depth:
+ NE_DEBUG(NE_DBG_LOCKS, "Got depth: %s\n", cdata);
+ l->depth = parse_depth(cdata);
+ if (l->depth == -1) {
+ return -1;
+ }
+ break;
+ case NE_ELM_timeout:
+ NE_DEBUG(NE_DBG_LOCKS, "Got timeout: %s\n", cdata);
+ l->timeout = parse_timeout(cdata);
+ if (l->timeout == NE_TIMEOUT_INVALID) {
+ return -1;
+ }
+ break;
+ case NE_ELM_owner:
+ l->owner = strdup(cdata);
+ break;
+ case NE_ELM_href:
+ l->token = strdup(cdata);
+ break;
+ }
+ return 0;
+}
+
+/* End-element handler for lock discovery PROPFIND response */
+static int
+end_element_ldisc(void *userdata, const struct ne_xml_elm *elm,
+ const char *cdata)
+{
+ struct ne_lock *lock = ne_propfind_current_private(userdata);
+
+ return end_element_common(lock, elm, cdata);
+}
+
+/* End-element handler for LOCK response */
+static int
+end_element_lock(void *userdata, const struct ne_xml_elm *elm,
+ const char *cdata)
+{
+ struct ne_lock *lock = userdata;
+ return end_element_common(lock, elm, cdata);
+}
+
+static void *create_private(void *userdata, const char *uri)
+{
+ return ne_calloc(sizeof(struct ne_lock));
+}
+
+/* Discover all locks on URI */
+int ne_lock_discover(ne_session *sess, const char *uri,
+ ne_lock_result callback, void *userdata)
+{
+ ne_propfind_handler *handler;
+ struct discover_ctx ctx = {0};
+ int ret;
+
+ ctx.results = callback;
+ ctx.userdata = userdata;
+
+ handler = ne_propfind_create(sess, uri, NE_DEPTH_ZERO);
+
+ ne_propfind_set_private(handler, create_private, NULL);
+
+ ne_xml_push_handler(ne_propfind_get_parser(handler), lock_elms,
+ check_context, NULL, end_element_ldisc, handler);
+
+ ret = ne_propfind_named(handler, lock_props, discover_results, &ctx);
+
+ ne_propfind_destroy(handler);
+
+ return ret;
+}
+
+int ne_lock(ne_session *sess, struct ne_lock *lock)
+{
+ ne_request *req = ne_request_create(sess, "LOCK", lock->uri);
+ ne_buffer *body = ne_buffer_create();
+ ne_xml_parser *parser = ne_xml_create();
+ int ret, parse_failed;
+
+ ne_xml_push_handler(parser, lock_elms, check_context,
+ NULL, end_element_lock, lock);
+
+ /* Create the body */
+ ne_buffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<lockinfo xmlns='DAV:'>" EOL " <lockscope>",
+ lock->scope==ne_lockscope_exclusive?
+ "<exclusive/>":"<shared/>",
+ "</lockscope>" EOL
+ "<locktype><write/></locktype>", NULL);
+
+ if (lock->owner) {
+ ne_buffer_concat(body, "<owner>", lock->owner, "</owner>" EOL, NULL);
+ }
+ ne_buffer_zappend(body, "</lockinfo>" EOL);
+
+ ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+ ne_add_response_body_reader(req, ne_accept_2xx,
+ ne_xml_parse_v, parser);
+ ne_add_request_header(req, "Content-Type", "text/xml");
+ ne_add_depth_header(req, lock->depth);
+
+ /* TODO:
+ * By 2518, we need this only if we are creating a lock-null resource.
+ * Since we don't KNOW whether the lock we're given is a lock-null
+ * or not, we cover our bases.
+ */
+ ne_lock_using_parent(req, lock->uri);
+ /* This one is clearer from 2518 sec 8.10.4. */
+ ne_lock_using_resource(req, lock->uri, lock->depth);
+
+ ret = ne_request_dispatch(req);
+
+ ne_buffer_destroy(body);
+ parse_failed = !ne_xml_valid(parser);
+
+ if (ret == NE_OK && ne_get_status(req)->klass == 2) {
+ if (parse_failed) {
+ ret = NE_ERROR;
+ ne_set_error(sess, ne_xml_get_error(parser));
+ }
+ else if (ne_get_status(req)->code == 207) {
+ ret = NE_ERROR;
+ /* TODO: set the error string appropriately */
+ }
+ } else {
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+ ne_xml_destroy(parser);
+
+ /* TODO: free the list */
+ return ret;
+}
diff --git a/neon/src/ne_locks.h b/neon/src/ne_locks.h
new file mode 100644
index 000000000..12199cd3e
--- /dev/null
+++ b/neon/src/ne_locks.h
@@ -0,0 +1,126 @@
+/*
+ WebDAV Class 2 locking operations
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_LOCKS_H
+#define NE_LOCKS_H
+
+#include "ne_request.h" /* for ne_session + http_req */
+
+BEGIN_NEON_DECLS
+
+/* The scope of a lock */
+enum ne_lock_scope {
+ ne_lockscope_exclusive,
+ ne_lockscope_shared
+};
+
+/* Lock type. Only write locks are defined in RFC2518. */
+enum ne_lock_type {
+ ne_locktype_write
+};
+
+/* A lock object. Lock objects are kept on doubly-linked lists. */
+struct ne_lock {
+ char *uri; /* the URI which this lock covers. */
+ int depth; /* the depth of the lock (NE_DEPTH_*). */
+ enum ne_lock_type type;
+ enum ne_lock_scope scope;
+ char *token; /* the lock token: uniquely identifies this lock. */
+ char *owner; /* string describing the owner of the lock. */
+ long timeout; /* timeout in seconds. (or NE_TIMEOUT_*) */
+ struct ne_lock *next, *prev;
+};
+/* NB: struct ne_lock Would be typedef'ed to ne_lock except lock is
+ * a verb and a noun, so we already have ne_lock the function. Damn
+ * the English language. */
+
+#define NE_TIMEOUT_INFINITE -1
+#define NE_TIMEOUT_INVALID -2
+
+typedef struct ne_lock_session_s ne_lock_session;
+
+/* TODO:
+ * "session" is a bad word, and it's already used for ne_session,
+ * maybe think up a better name. lock_store is quite good.
+ */
+
+/* Register the locking hooks with an ne_session. Owned locks
+ * persist for the duration of this session. The lock session lasts
+ * exactly as long as the corresponding ne_session. Once you call
+ * ne_session_destroy(sess), any use of the lock session has
+ * undefined results. */
+ne_lock_session *ne_lock_register(ne_session *sess);
+
+/* Add a lock to the given session. The lock will subsequently be
+ * submitted as required in an If: header with any requests created
+ * using the ne_session which the lock session is tied to. Requests
+ * indicate to the locking layer which locks they might need using
+ * ne_lock_using_*, as described below. */
+void ne_lock_add(ne_lock_session *sess, struct ne_lock *lock);
+
+/* Remove lock, which must have been previously added to the
+ * session using 'ne_lock_add' above. */
+void ne_lock_remove(ne_lock_session *sess, struct ne_lock *lock);
+
+typedef void (*ne_lock_walkfunc)(struct ne_lock *lock, void *userdata);
+
+/* For each lock added to the session, call func, passing the lock
+ * and the given userdata. Returns the number of locks. func may be
+ * pass as NULL, in which case, can be used to simply return number
+ * of locks in the session. */
+int ne_lock_iterate(ne_lock_session *sess,
+ ne_lock_walkfunc func, void *userdata);
+
+/* Issue a LOCK request for the given lock. */
+int ne_lock(ne_session *sess, struct ne_lock *lock);
+/* Issue an UNLOCK request for the given lock */
+int ne_unlock(ne_session *sess, struct ne_lock *lock);
+
+/* Find a lock in the session with given URI */
+struct ne_lock *ne_lock_find(ne_lock_session *sess, const char *uri);
+
+/* Deep-copy a lock structure. */
+struct ne_lock *ne_lock_copy(const struct ne_lock *lock);
+
+/* Free a lock structure */
+void ne_lock_free(struct ne_lock *lock);
+
+/* Callback for lock discovery. If 'lock' is NULL,
+ * something went wrong retrieving lockdiscover for the resource,
+ * look at 'status' for the details. */
+typedef void (*ne_lock_result)(void *userdata, const struct ne_lock *lock,
+ const char *uri, const ne_status *status);
+
+/* Perform lock discovery on the given URI. 'result' is called
+ * with the results (possibly >1 times). */
+int ne_lock_discover(ne_session *sess, const char *uri,
+ ne_lock_result result, void *userdata);
+
+/*** For use by method functions */
+
+/* Indicate that this request is of depth n on given uri */
+void ne_lock_using_resource(ne_request *req, const char *uri, int depth);
+/* Indicate that this request will modify parent collection of given URI */
+void ne_lock_using_parent(ne_request *req, const char *uri);
+
+END_NEON_DECLS
+
+#endif /* NE_LOCKS_H */
diff --git a/neon/src/ne_md5.c b/neon/src/ne_md5.c
new file mode 100644
index 000000000..208d8312b
--- /dev/null
+++ b/neon/src/ne_md5.c
@@ -0,0 +1,460 @@
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+ according to the definition of MD5 in RFC 1321 from April 1992.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include <ctype.h> /* for tolower */
+
+#include "ne_md5.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define WORDS_BIGENDIAN 1
+# endif
+#endif
+
+#define md5_init_ctx ne_md5_init_ctx
+#define md5_process_block ne_md5_process_block
+#define md5_process_bytes ne_md5_process_bytes
+#define md5_finish_ctx ne_md5_finish_ctx
+#define md5_read_ctx ne_md5_read_ctx
+#define md5_stream ne_md5_stream
+#define md5_buffer ne_md5_buffer
+#define md5_ctx ne_md5_ctx
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+void
+md5_init_ctx (ctx)
+ struct md5_ctx *ctx;
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result
+ must be in little endian byte order.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_read_ctx (ctx, resbuf)
+ const struct md5_ctx *ctx;
+ void *resbuf;
+{
+ ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+ ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+ ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+ ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+ return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_finish_ctx (ctx, resbuf)
+ struct md5_ctx *ctx;
+ void *resbuf;
+{
+ /* Take yet unprocessed bytes into account. */
+ md5_uint32 bytes = ctx->buflen;
+ size_t pad;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+ memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+ *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+ (ctx->total[0] >> 29));
+
+ /* Process last bytes. */
+ md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+ return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+int
+md5_stream (stream, resblock)
+ FILE *stream;
+ void *resblock;
+{
+ /* Important: BLOCKSIZE must be a multiple of 64. */
+#define BLOCKSIZE 4096
+ struct md5_ctx ctx;
+ char buffer[BLOCKSIZE + 72];
+ size_t sum;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ do
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+ }
+ while (sum < BLOCKSIZE && n != 0);
+ if (n == 0 && ferror (stream))
+ return 1;
+
+ /* If end of file is reached, end the loop. */
+ if (n == 0)
+ break;
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ md5_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+ /* Add the last bytes if necessary. */
+ if (sum > 0)
+ md5_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ md5_finish_ctx (&ctx, resblock);
+ return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+void *
+md5_buffer (buffer, len, resblock)
+ const char *buffer;
+ size_t len;
+ void *resblock;
+{
+ struct md5_ctx ctx;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Process whole buffer but last len % 64 bytes. */
+ md5_process_bytes (buffer, len, &ctx);
+
+ /* Put result in desired memory area. */
+ return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0)
+ {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy (&ctx->buffer[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (left_over + add > 64)
+ {
+ md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+ (left_over + add) & 63);
+ ctx->buflen = (left_over + add) & 63;
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len > 64)
+ {
+ md5_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0)
+ {
+ memcpy (ctx->buffer, buffer, len);
+ ctx->buflen = len;
+ }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+
+void
+md5_process_block (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ md5_uint32 correct_words[16];
+ const unsigned char *words = buffer;
+ const unsigned char *endp = words + len;
+ md5_uint32 A = ctx->A;
+ md5_uint32 B = ctx->B;
+ md5_uint32 C = ctx->C;
+ md5_uint32 D = ctx->D;
+
+ /* First increment the byte count. RFC 1321 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += len;
+ if (ctx->total[0] < len)
+ ++ctx->total[1];
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (words < endp)
+ {
+ md5_uint32 *cwp = correct_words;
+ md5_uint32 A_save = A;
+ md5_uint32 B_save = B;
+ md5_uint32 C_save = C;
+ md5_uint32 D_save = D;
+
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ md5_uint32 WORD_ = (md5_uint32)words[0] | ((md5_uint32)words[1] << 8) \
+ | ((md5_uint32)words[2] << 16) | ((md5_uint32)words[3] << 24); \
+ a += FF (b, c, d) + (*cwp++ = WORD_) + T; \
+ words += 4; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
+
+#define ASC2HEX(x) (((x) <= '9') ? ((x) - '0') : (tolower((x)) + 10 - 'a'))
+#define HEX2ASC(x) ((x) > 9 ? ((x) - 10 + 'a') : ((x) + '0'))
+
+/* Writes the ASCII representation of the MD5 digest into the
+ * given buffer, which must be at least 33 characters long. */
+void ne_md5_to_ascii(const unsigned char md5_buf[16], char *buffer)
+{
+ int count;
+ for (count = 0; count<16; count++) {
+ buffer[count*2] = HEX2ASC(md5_buf[count] >> 4);
+ buffer[count*2+1] = HEX2ASC(md5_buf[count] & 0x0f);
+ }
+ buffer[32] = '\0';
+}
+
+/* Reads the ASCII representation of an MD5 digest. The buffer must
+ * be at least 32 characters long. */
+void ne_ascii_to_md5(const char *buffer, unsigned char md5_buf[16])
+{
+ int count;
+ for (count = 0; count<16; count++) {
+ md5_buf[count] = ((ASC2HEX(buffer[count*2])) << 4) |
+ ASC2HEX(buffer[count*2+1]);
+ }
+}
diff --git a/neon/src/ne_md5.h b/neon/src/ne_md5.h
new file mode 100644
index 000000000..7033b8b0e
--- /dev/null
+++ b/neon/src/ne_md5.h
@@ -0,0 +1,151 @@
+/* Declaration of functions and data types used for MD5 sum computing
+ library functions.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef NEON_MD5_H
+#define NEON_MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+ to determine an unsigned integral type that is 32 bits wide. An
+ alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+ doing that would require that the configure script compile and *run*
+ the resulting executable. Locally running cross-compiled executables
+ is usually not possible. */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+# define UINT_MAX_32_BITS 4294967295U
+# else
+# define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+ This should be valid for all systems GNU cares about because
+ that doesn't include 16-bit systems, and only modern systems
+ (that certainly have <limits.h>) have 64+-bit integral types. */
+
+# ifndef UINT_MAX
+# define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+ typedef unsigned int md5_uint32;
+# else
+# if USHRT_MAX == UINT_MAX_32_BITS
+ typedef unsigned short md5_uint32;
+# else
+# if ULONG_MAX == UINT_MAX_32_BITS
+ typedef unsigned long md5_uint32;
+# else
+ /* The following line is intended to evoke an error.
+ Using #error is not portable enough. */
+ "Cannot determine unsigned 32-bit data type."
+# endif
+# endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+# define __P(x) x
+#else
+# define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps. */
+struct ne_md5_ctx
+{
+ md5_uint32 A;
+ md5_uint32 B;
+ md5_uint32 C;
+ md5_uint32 D;
+
+ md5_uint32 total[2];
+ md5_uint32 buflen;
+ char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+extern void ne_md5_init_ctx __P ((struct ne_md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void ne_md5_process_block __P ((const void *buffer, size_t len,
+ struct ne_md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void ne_md5_process_bytes __P ((const void *buffer, size_t len,
+ struct ne_md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 16 bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *ne_md5_finish_ctx __P ((struct ne_md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *ne_md5_read_ctx __P ((const struct ne_md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+extern int ne_md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+extern void *ne_md5_buffer __P ((const char *buffer, size_t len,
+ void *resblock));
+
+/* MD5 ascii->binary conversion */
+void ne_md5_to_ascii(const unsigned char md5_buf[16], char *buffer);
+void ne_ascii_to_md5(const char *buffer, unsigned char md5_buf[16]);
+
+#endif /* NEON_MD5_H */
diff --git a/neon/src/ne_private.h b/neon/src/ne_private.h
new file mode 100644
index 000000000..38f2e3ab4
--- /dev/null
+++ b/neon/src/ne_private.h
@@ -0,0 +1,204 @@
+/*
+ HTTP Request Handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* THIS IS NOT A PUBLIC INTERFACE. You CANNOT include this header file
+ * from an application. */
+
+#ifndef NE_PRIVATE_H
+#define NE_PRIVATE_H
+
+#include "ne_request.h"
+
+struct host_info {
+ /* hostname is not const since it changes on redirects. */
+ char *hostname;
+ int port;
+ struct in_addr addr;
+ char *hostport; /* URI hostport segment */
+};
+
+/* This is called with each of the headers in the response */
+struct header_handler {
+ char *name;
+ ne_header_handler handler;
+ void *userdata;
+ struct header_handler *next;
+};
+
+/* TODO: could unify these all into a generic callback list */
+
+struct body_reader {
+ ne_block_reader handler;
+ ne_accept_response accept_response;
+ unsigned int use:1;
+ void *userdata;
+ struct body_reader *next;
+};
+
+/* Store hook information */
+struct hook {
+ const ne_request_hooks *hooks;
+ void *private;
+ ne_free_hooks free;
+ struct hook *next;
+};
+
+/* Per-request store for hooks.
+ * This is a bit noddy really. */
+struct hook_request {
+ struct hook *hook;
+ void *cookie;
+ struct hook_request *next;
+};
+
+#define HAVE_HOOK(st,func) (st->hook->hooks->func != NULL)
+#define HOOK_FUNC(st, func) (*st->hook->hooks->func)
+
+/* Session support. */
+struct ne_session_s {
+ /* Connection information */
+ nsocket *socket;
+
+ struct host_info server, proxy;
+
+ /* Connection states:
+ * 0: Not connected at all.
+ * 1: We have a TCP connection to the next-hop server.
+ * 2: We have a negotiated an SSL connection over the proxy's
+ * TCP tunnel.
+ *
+ * Note, 1 is all we need if we don't have a proxy server, or
+ * if we do have a proxy server and we're not using SSL.
+ */
+ unsigned int connected:2;
+
+ /* Settings */
+ unsigned int have_proxy:1; /* do we have a proxy server? */
+ unsigned int no_persist:1; /* set to disable persistent connections */
+ unsigned int use_secure:1; /* whether a secure connection is required */
+ int expect100_works:2; /* known state of 100-continue support */
+ unsigned int in_connect:1; /* doing a proxy CONNECT */
+ unsigned int request_secure_upgrade:1;
+ unsigned int accept_secure_upgrade:1;
+
+ ne_use_proxy proxy_decider;
+ void *proxy_decider_udata;
+
+ nssl_context *ssl_context;
+
+ sock_progress progress_cb;
+ void *progress_ud;
+
+ ne_notify_status notify_cb;
+ void *notify_ud;
+
+ struct hook *hooks;
+
+ char *user_agent; /* full User-Agent string */
+
+ /* The last HTTP-Version returned by the server */
+ int version_major;
+ int version_minor;
+
+ /* Error string */
+ char error[BUFSIZ];
+};
+
+struct ne_request_s {
+ const char *method;
+ char *uri, *abs_path;
+
+ /*** Request ***/
+
+ ne_buffer *headers;
+ ne_provide_body body_cb;
+ void *body_ud;
+
+ int body_fd;
+ const char *body_buffer, *body_pnt;
+ size_t body_size, body_left;
+
+ /* temporary store for request. */
+ ne_buffer *reqbuf;
+
+ /* temporary store for response lines. */
+ ne_buffer *respbuf;
+
+ /**** Response ***/
+
+ /* The transfer encoding types */
+ struct ne_response {
+ int length; /* Response entity-body content-length */
+ size_t left; /* Bytes left to read */
+ size_t chunk_left; /* Bytes of chunk left to read */
+ size_t total; /* total bytes read so far. */
+ unsigned int is_chunked; /* Are we using chunked TE? */
+ } resp;
+
+ /* List of callbacks which are passed response headers */
+ struct header_handler *header_catchers;
+
+ /* We store response header handlers in a hash table. The hash is
+ * derived from the header name in lower case. */
+
+ /* 53 is magic, of course. For a standard http_get (with
+ * redirects), 9 header handlers are defined. Two of these are
+ * for Content-Length (which is a bug, and should be fixed
+ * really). Ignoring that hash clash, the 8 *different* handlers
+ * all hash uniquely into the hash table of size 53. */
+#define HH_HASHSIZE 53
+
+ struct header_handler *header_handlers[HH_HASHSIZE];
+ /* List of callbacks which are passed response body blocks */
+ struct body_reader *body_readers;
+
+ /*** Miscellaneous ***/
+ unsigned int method_is_head:1;
+ unsigned int use_proxy:1;
+ unsigned int use_expect100:1;
+ unsigned int can_persist:1;
+ unsigned int forced_close:1;
+ unsigned int upgrade_to_tls:1;
+
+ ne_session *session;
+ ne_status status;
+
+ /* stores request-private hook info */
+ struct hook_request *hook_store;
+
+#ifdef USE_DAV_LOCKS
+ /* TODO: move this to hooks... list of locks to submit */
+ struct dav_submit_locks *if_locks;
+#endif
+
+};
+
+/* Set a new URI for the given request. */
+void ne_set_request_uri(ne_request *req, const char *uri);
+
+typedef int (*ne_push_fn)(void *userdata, const char *buf, size_t count);
+
+/* Pulls the request body for the given request, passing blocks to the
+ * given callback.
+ */
+int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud);
+
+#endif /* HTTP_PRIVATE_H */
diff --git a/neon/src/ne_props.c b/neon/src/ne_props.c
new file mode 100644
index 000000000..ff27d99ab
--- /dev/null
+++ b/neon/src/ne_props.c
@@ -0,0 +1,589 @@
+/*
+ WebDAV Properties manipulation
+ Copyright (C) 2000-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_xml.h"
+#include "ne_props.h"
+#include "ne_basic.h"
+
+struct ne_propfind_handler_s {
+ ne_session *sess;
+ ne_request *request;
+
+ int has_props; /* whether we've already written some
+ * props to the body. */
+ ne_buffer *body;
+
+ ne_207_parser *parser207;
+ ne_xml_parser *parser;
+
+ /* Callback to create the private structure. */
+ ne_props_create_complex private_creator;
+ void *private_userdata;
+
+ /* Current propset. */
+ ne_prop_result_set *current;
+
+ ne_props_result callback;
+ void *userdata;
+};
+
+#define ELM_namedprop (NE_ELM_207_UNUSED)
+
+static const struct ne_xml_elm flat_elms[] = {
+ { "", "", NE_ELM_unknown, NE_XML_COLLECT },
+ { NULL }
+};
+
+/* We build up the results of one 'response' element in memory. */
+struct prop {
+ char *name, *nspace, *value, *lang;
+ /* Store a ne_propname here too, for convienience. pname.name =
+ * name, pname.nspace = nspace, but they are const'ed in pname. */
+ ne_propname pname;
+};
+
+struct propstat {
+ struct prop *props;
+ int numprops;
+ ne_status status;
+} propstat;
+
+/* Results set. */
+struct ne_prop_result_set_s {
+ struct propstat *pstats;
+ int numpstats;
+ void *private;
+ char *href;
+};
+
+
+static int
+startelm(void *userdata, const struct ne_xml_elm *elm,
+ const char **atts);
+static int
+endelm(void *userdata, const struct ne_xml_elm *elm, const char *cdata);
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child);
+
+ne_xml_parser *ne_propfind_get_parser(ne_propfind_handler *handler)
+{
+ return handler->parser;
+}
+
+ne_request *ne_propfind_get_request(ne_propfind_handler *handler)
+{
+ return handler->request;
+}
+
+static int propfind(ne_propfind_handler *handler,
+ ne_props_result results, void *userdata)
+{
+ int ret;
+ ne_request *req = handler->request;
+
+ /* Register the flat property handler to catch any properties
+ * which the user isn't handling as 'complex'. */
+ ne_xml_push_handler(handler->parser, flat_elms,
+ check_context, startelm, endelm, handler);
+
+ /* Register the catch-all handler to ignore any cruft the
+ * server returns. */
+ ne_207_ignore_unknown(handler->parser207);
+
+ handler->callback = results;
+ handler->userdata = userdata;
+
+ ne_set_request_body_buffer(req, handler->body->data,
+ ne_buffer_size(handler->body));
+
+ ne_add_request_header(req, "Content-Type", "text/xml"); /* TODO: UTF-8? */
+
+ ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v,
+ handler->parser);
+
+ ret = ne_request_dispatch(req);
+
+ if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+ ret = NE_ERROR;
+ } else if (!ne_xml_valid(handler->parser)) {
+ ne_set_error(handler->sess, ne_xml_get_error(handler->parser));
+ ret = NE_ERROR;
+ }
+
+ return ret;
+}
+
+static void set_body(ne_propfind_handler *hdl, const ne_propname *names)
+{
+ ne_buffer *body = hdl->body;
+ int n;
+
+ if (!hdl->has_props) {
+ ne_buffer_zappend(body, "<prop>" EOL);
+ hdl->has_props = 1;
+ }
+
+ for (n = 0; names[n].name != NULL; n++) {
+ ne_buffer_concat(body, "<", names[n].name, " xmlns=\"",
+ names[n].nspace, "\"/>" EOL, NULL);
+ }
+
+}
+
+int ne_propfind_allprop(ne_propfind_handler *handler,
+ ne_props_result results, void *userdata)
+{
+ ne_buffer_zappend(handler->body, "<allprop/></propfind>" EOL);
+ return propfind(handler, results, userdata);
+}
+
+int ne_propfind_named(ne_propfind_handler *handler, const ne_propname *props,
+ ne_props_result results, void *userdata)
+{
+ set_body(handler, props);
+ ne_buffer_zappend(handler->body, "</prop></propfind>" EOL);
+ return propfind(handler, results, userdata);
+}
+
+
+/* The easy one... PROPPATCH */
+int ne_proppatch(ne_session *sess, const char *uri,
+ const ne_proppatch_operation *items)
+{
+ ne_request *req = ne_request_create(sess, "PROPPATCH", uri);
+ ne_buffer *body = ne_buffer_create();
+ int n, ret;
+
+ /* Create the request body */
+ ne_buffer_zappend(body, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
+ "<propertyupdate xmlns=\"DAV:\">");
+
+ for (n = 0; items[n].name != NULL; n++) {
+ switch (items[n].type) {
+ case ne_propset:
+ /* <set><prop><prop-name>value</prop-name></prop></set> */
+ ne_buffer_concat(body, "<set><prop>"
+ "<", items[n].name->name, " xmlns=\"",
+ items[n].name->nspace, "\">", items[n].value,
+ "</", items[n].name->name, "></prop></set>" EOL,
+ NULL);
+ break;
+
+ case ne_propremove:
+ /* <remove><prop><prop-name/></prop></remove> */
+ ne_buffer_concat(body,
+ "<remove><prop><", items[n].name->name, " xmlns=\"",
+ items[n].name->nspace, "\"/></prop></remove>" EOL,
+ NULL);
+ break;
+ }
+ }
+
+ ne_buffer_zappend(body, "</propertyupdate>" EOL);
+
+ ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+ ne_add_request_header(req, "Content-Type", "text/xml"); /* TODO: UTF-8? */
+
+ ret = ne_simple_request(sess, req);
+
+ ne_buffer_destroy(body);
+
+ return ret;
+}
+
+/* Compare two property names. */
+static int pnamecmp(const ne_propname *pn1, const ne_propname *pn2)
+{
+ return (strcasecmp(pn1->nspace, pn2->nspace) ||
+ strcasecmp(pn1->name, pn2->name));
+}
+
+/* Find property in 'set' with name 'pname'. If found, set pstat_ret
+ * to the containing propstat, likewise prop_ret, and returns zero.
+ * If not found, returns non-zero. */
+static int findprop(const ne_prop_result_set *set, const ne_propname *pname,
+ struct propstat **pstat_ret, struct prop **prop_ret)
+{
+
+ int ps, p;
+
+ for (ps = 0; ps < set->numpstats; ps++) {
+ for (p = 0; p < set->pstats[ps].numprops; p++) {
+ struct prop *prop = &set->pstats[ps].props[p];
+
+ if (pnamecmp(&prop->pname, pname) == 0) {
+ if (pstat_ret != NULL)
+ *pstat_ret = &set->pstats[ps];
+ if (prop_ret != NULL)
+ *prop_ret = prop;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+const char *ne_propset_value(const ne_prop_result_set *set,
+ const ne_propname *pname)
+{
+ struct prop *prop;
+
+ if (findprop(set, pname, NULL, &prop)) {
+ return NULL;
+ } else {
+ return prop->value;
+ }
+}
+
+const char *ne_propset_lang(const ne_prop_result_set *set,
+ const ne_propname *pname)
+{
+ struct prop *prop;
+
+ if (findprop(set, pname, NULL, &prop)) {
+ return NULL;
+ } else {
+ return prop->lang;
+ }
+}
+
+void *ne_propfind_current_private(ne_propfind_handler *handler)
+{
+ return handler->current->private;
+}
+
+void *ne_propset_private(const ne_prop_result_set *set)
+{
+ return set->private;
+}
+
+int ne_propset_iterate(const ne_prop_result_set *set,
+ ne_propset_iterator iterator, void *userdata)
+{
+ int ps, p;
+
+ for (ps = 0; ps < set->numpstats; ps++) {
+ for (p = 0; p < set->pstats[ps].numprops; p++) {
+ struct prop *prop = &set->pstats[ps].props[p];
+ int ret = iterator(userdata, &prop->pname, prop->value,
+ &set->pstats[ps].status);
+ if (ret)
+ return ret;
+
+ }
+ }
+
+ return 0;
+}
+
+const ne_status *ne_propset_status(const ne_prop_result_set *set,
+ const ne_propname *pname)
+{
+ struct propstat *pstat;
+
+ if (findprop(set, pname, &pstat, NULL)) {
+ /* TODO: it is tempting to return a dummy status object here
+ * rather than NULL, which says "Property result was not given
+ * by server." but I'm not sure if this is best left to the
+ * client. */
+ return NULL;
+ } else {
+ return &pstat->status;
+ }
+}
+
+
+/* Pick up all properties as flat properties. */
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child)
+{
+ if (child == NE_ELM_unknown && parent == NE_ELM_prop)
+ return NE_XML_VALID;
+
+ return NE_XML_DECLINE;
+}
+
+static void *start_response(void *userdata, const char *href)
+{
+ ne_prop_result_set *set = ne_calloc(sizeof(*set));
+ ne_propfind_handler *hdl = userdata;
+
+ set->href = ne_strdup(href);
+
+ if (hdl->private_creator != NULL) {
+ set->private = hdl->private_creator(hdl->private_userdata, href);
+ }
+
+ hdl->current = set;
+
+ return set;
+}
+
+static void *start_propstat(void *userdata, void *response)
+{
+ ne_prop_result_set *set = response;
+ int n;
+ struct propstat *pstat;
+
+ n = set->numpstats;
+ set->pstats = realloc(set->pstats, sizeof(struct propstat) * (n+1));
+ set->numpstats = n+1;
+
+ pstat = &set->pstats[n];
+ memset(pstat, 0, sizeof(*pstat));
+
+ /* And return this as the new pstat. */
+ return &set->pstats[n];
+}
+
+static int
+startelm(void *userdata, const struct ne_xml_elm *elm,
+ const char **atts)
+{
+ ne_propfind_handler *hdl = userdata;
+ struct propstat *pstat = ne_207_get_current_propstat(hdl->parser207);
+ struct prop *prop;
+ int n;
+ const char *lang;
+
+ /* Paranoia */
+ if (pstat == NULL) {
+ NE_DEBUG(NE_DBG_XML, "gp_startelm: No propstat found, or not my element.");
+ return -1;
+ }
+
+ /* Add a property to this propstat */
+ n = pstat->numprops;
+
+ pstat->props = realloc(pstat->props, sizeof(struct prop) * (n + 1));
+ pstat->numprops = n+1;
+
+ /* Fill in the new property. */
+ prop = &pstat->props[n];
+
+ prop->pname.name = prop->name = ne_strdup(elm->name);
+ prop->pname.nspace = prop->nspace = ne_strdup(elm->nspace);
+ prop->value = NULL;
+
+ NE_DEBUG(NE_DBG_XML, "Got property #%d: %s@@%s.\n", n,
+ prop->nspace, prop->name);
+
+ /* This is under discussion at time of writing (April '01), and it
+ * looks like we need to retrieve the xml:lang property from any
+ * element here or above.
+ *
+ * Also, I think we might need attribute namespace handling here. */
+ lang = ne_xml_get_attr(atts, "xml:lang");
+ if (lang != NULL) {
+ prop->lang = ne_strdup(lang);
+ NE_DEBUG(NE_DBG_XML, "Property language is %s\n", prop->lang);
+ } else {
+ prop->lang = NULL;
+ }
+
+ return 0;
+}
+
+static int
+endelm(void *userdata, const struct ne_xml_elm *elm, const char *cdata)
+{
+ ne_propfind_handler *hdl = userdata;
+ struct propstat *pstat = ne_207_get_current_propstat(hdl->parser207);
+ int n;
+
+ if (pstat == NULL) {
+ NE_DEBUG(NE_DBG_XML, "gp_endelm: No propstat found, or not my element.");
+ return -1;
+ }
+
+ n = pstat->numprops - 1;
+
+ NE_DEBUG(NE_DBG_XML, "Value of property #%d is %s\n", n, cdata);
+
+ pstat->props[n].value = ne_strdup(cdata);
+
+ return 0;
+}
+
+static void end_propstat(void *userdata, void *pstat_v,
+ const char *status_line, const ne_status *status,
+ const char *description)
+{
+ struct propstat *pstat = pstat_v;
+
+ /* If we get a non-2xx response back here, we wipe the value for
+ * each of the properties in this propstat, so the caller knows to
+ * look at the status instead. It's annoying, since for each prop
+ * we will have done an unnecessary strdup("") above, but there is
+ * no easy way round that given the fact that we don't know
+ * whether we've got an error or not till after we get the
+ * property element.
+ *
+ * Interestingly IIS breaks the 2518 DTD and puts the status
+ * element first in the propstat. This is useful since then we
+ * *do* know whether each subsequent empty prop element means, but
+ * we can't rely on that here. */
+ if (status->klass != 2) {
+ int n;
+
+ for (n = 0; n < pstat->numprops; n++) {
+ free(pstat->props[n].value);
+ pstat->props[n].value = NULL;
+ }
+ }
+
+ pstat->status = *status;
+}
+
+/* Frees up a results set */
+static void free_propset(ne_prop_result_set *set)
+{
+ int n;
+
+ for (n = 0; n < set->numpstats; n++) {
+ int m;
+ struct propstat *p = &set->pstats[n];
+
+ for (m = 0; m < p->numprops; m++) {
+ free(p->props[m].nspace);
+ free(p->props[m].name);
+ NE_FREE(p->props[m].lang);
+ NE_FREE(p->props[m].value);
+ }
+
+ free(set->pstats[n].props);
+ }
+
+ free(set->pstats);
+ free(set);
+}
+
+static void end_response(void *userdata, void *resource,
+ const char *status_line,
+ const ne_status *status,
+ const char *description)
+{
+ ne_propfind_handler *handler = userdata;
+ ne_prop_result_set *set = resource;
+
+ /* TODO: Handle status here too? The status element is mandatory
+ * inside each propstat, so, not much point probably. */
+
+ /* Pass back the results for this resource. */
+ if (handler->callback != NULL) {
+ handler->callback(handler->userdata, set->href, set);
+ }
+
+ free(set->href);
+
+ /* Clean up the propset tree we've just built. */
+ free_propset(set);
+}
+
+ne_propfind_handler *
+ne_propfind_create(ne_session *sess, const char *uri, int depth)
+{
+ ne_propfind_handler *ret = ne_calloc(sizeof(ne_propfind_handler));
+
+ ret->parser = ne_xml_create();
+ ret->parser207 = ne_207_create(ret->parser, ret);
+ ret->sess = sess;
+ ret->body = ne_buffer_create();
+ ret->request = ne_request_create(sess, "PROPFIND", uri);
+
+ ne_add_depth_header(ret->request, depth);
+
+ ne_207_set_response_handlers(ret->parser207,
+ start_response, end_response);
+
+ ne_207_set_propstat_handlers(ret->parser207, start_propstat,
+ end_propstat);
+
+ /* The start of the request body is fixed: */
+ ne_buffer_concat(ret->body,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+ "<propfind xmlns=\"DAV:\">", NULL);
+
+ return ret;
+}
+
+/* Destroy a propfind handler */
+void ne_propfind_destroy(ne_propfind_handler *handler)
+{
+ ne_207_destroy(handler->parser207);
+ ne_xml_destroy(handler->parser);
+ ne_buffer_destroy(handler->body);
+ ne_request_destroy(handler->request);
+ free(handler);
+}
+
+int ne_simple_propfind(ne_session *sess, const char *href, int depth,
+ const ne_propname *props,
+ ne_props_result results, void *userdata)
+{
+ ne_propfind_handler *hdl;
+ int ret;
+
+ hdl = ne_propfind_create(sess, href, depth);
+ if (props != NULL) {
+ ret = ne_propfind_named(hdl, props, results, userdata);
+ } else {
+ ret = ne_propfind_allprop(hdl, results, userdata);
+ }
+
+ ne_propfind_destroy(hdl);
+
+ return ret;
+}
+
+int ne_propnames(ne_session *sess, const char *href, int depth,
+ ne_props_result results, void *userdata)
+{
+ ne_propfind_handler *hdl;
+ int ret;
+
+ hdl = ne_propfind_create(sess, href, depth);
+
+ ne_buffer_zappend(hdl->body, "<propname/></propfind>");
+
+ ret = propfind(hdl, results, userdata);
+
+ ne_propfind_destroy(hdl);
+
+ return ret;
+}
+
+void ne_propfind_set_private(ne_propfind_handler *hdl,
+ ne_props_create_complex creator,
+ void *userdata)
+{
+ hdl->private_creator = creator;
+ hdl->private_userdata = userdata;
+}
diff --git a/neon/src/ne_props.h b/neon/src/ne_props.h
new file mode 100644
index 000000000..0e8e2b2f6
--- /dev/null
+++ b/neon/src/ne_props.h
@@ -0,0 +1,224 @@
+/*
+ WebDAV Properties manipulation
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_PROPS_H
+#define NE_PROPS_H
+
+#include "ne_request.h"
+#include "ne_207.h"
+
+BEGIN_NEON_DECLS
+
+/* There are two interfaces for fetching properties. The first is
+ * 'ne_simple_propfind', which is relatively simple, and easy to use,
+ * but only lets you fetch FLAT properties, i.e. properties which are
+ * just a string of bytes. The complex interface is 'ne_propfind_*',
+ * which is complicated, and hard to use, but lets you parse
+ * structured properties, i.e. properties which have XML content. */
+
+/* The 'ne_simple_propfind' interface. ***
+ *
+ * ne_simple_propfind allows you to fetch a set of properties for a
+ * single resource, or a tree of resources. You set the operation
+ * going by passing these arguments:
+ *
+ * - the session which should be used.
+ * - the URI and the depth of the operation (0, 1, infinite)
+ * - the names of the properties which you want to fetch
+ * - a results callback, and the userdata for the callback.
+ *
+ * For each resource found, the results callback is called, passing
+ * you two things along with the userdata you passed in originally:
+ *
+ * - the URI of the resource (const char *href)
+ * - the properties results set (const ne_prop_result_set *results)
+ * */
+
+
+typedef struct ne_prop_result_set_s ne_prop_result_set;
+
+/* Get the value of a given property. Will return NULL if there was an
+ * error fetching this property on this resource. Call
+ * ne_propset_result to get the response-status if so. */
+const char *ne_propset_value(const ne_prop_result_set *set,
+ const ne_propname *propname);
+
+/* Returns the status structure for fetching the given property on
+ * this resource. This function will return NULL if the server did not
+ * return the property (which is a server error). */
+const ne_status *ne_propset_status(const ne_prop_result_set *set,
+ const ne_propname *propname);
+
+/* Returns the private pointer for the given propset. */
+void *ne_propset_private(const ne_prop_result_set *set);
+
+/* Return language string of property (may be NULL). */
+const char *ne_propset_lang(const ne_prop_result_set *set,
+ const ne_propname *pname);
+
+/* ne_propset_iterate iterates over a properties result set,
+ * calling the callback for each property in the set. userdata is
+ * passed as the first argument to the callback. value may be NULL,
+ * indicating an error occurred fetching this property: look at
+ * status for the error in that case.
+ *
+ * If the iterator returns non-zero, ne_propset_iterate will return
+ * immediately with that value.
+ */
+typedef int (*ne_propset_iterator)(void *userdata,
+ const ne_propname *pname,
+ const char *value,
+ const ne_status *status);
+
+/* Iterate over all the properties in 'set', calling 'iterator'
+ * for each, passing 'userdata' as the first argument to callback.
+ *
+ * Returns:
+ * whatever value iterator returns.
+ */
+int ne_propset_iterate(const ne_prop_result_set *set,
+ ne_propset_iterator iterator, void *userdata);
+
+/* Callback for handling the results of fetching properties for a
+ * single resource (named by 'href'). The results are stored in the
+ * result set 'results': use ne_propset_* to examine this object. */
+typedef void (*ne_props_result)(void *userdata, const char *href,
+ const ne_prop_result_set *results);
+
+/* Fetch properties for a resource (if depth == NE_DEPTH_ZERO),
+ * or a tree of resources (if depth == NE_DEPTH_ONE or _INFINITE).
+ *
+ * Names of the properties required must be given in 'props',
+ * or if props is NULL, *all* properties are fetched.
+ *
+ * 'results' is called for each resource in the response, userdata is
+ * passed as the first argument to the callback. It is important to
+ * note that the callback is called as the response is read off the
+ * socket, so don't do anything silly in it (e.g. sleep(100), or call
+ * any functions which use this session).
+ *
+ * Note that if 'depth' is NE_DEPTH_INFINITY, some servers may refuse
+ * the request.
+ *
+ * Returns NE_*. */
+int ne_simple_propfind(ne_session *sess, const char *uri, int depth,
+ const ne_propname *props,
+ ne_props_result results, void *userdata);
+
+/* A PROPPATCH request may include any number of operations. Pass an
+ * array of these operations to ne_proppatch, with the last item
+ * having the name element being NULL. If the type is propset, the
+ * property of the given name is set to the new value. If the type is
+ * propremove, the property of the given name is deleted, and the
+ * value is ignored. */
+typedef struct {
+ const ne_propname *name;
+ enum {
+ ne_propset,
+ ne_propremove
+ } type;
+ const char *value;
+} ne_proppatch_operation;
+
+int ne_proppatch(ne_session *sess, const char *uri,
+ const ne_proppatch_operation *items);
+
+/* Retrieve property names for the resources at 'href'. 'results'
+ * callback is called for each resource. Use 'ne_propset_iterate' on
+ * the passed results object to retrieve the list of property names. */
+int ne_propnames(ne_session *sess, const char *href, int depth,
+ ne_props_result results, void *userdata);
+
+/* The complex, you-do-all-the-work, property fetch interface:
+ */
+
+struct ne_propfind_handler_s;
+typedef struct ne_propfind_handler_s ne_propfind_handler;
+
+/* Retrieve the 'private' pointer for the current propset for the
+ * given handler, as returned by the ne_props_create_complex callback
+ * installed using 'ne_propfind_set_private'. If this callback was
+ * not registered, this function will return NULL. */
+void *ne_propfind_current_private(ne_propfind_handler *handler);
+
+/* Create a PROPFIND handler, for the given resource or set of
+ * resources.
+ *
+ * Depth must be one of NE_DEPTH_*. */
+ne_propfind_handler *
+ne_propfind_create(ne_session *sess, const char *uri, int depth);
+
+/* Return the XML parser for the given handler (only need if you want
+ * to handle complex properties). */
+ne_xml_parser *ne_propfind_get_parser(ne_propfind_handler *handler);
+
+/* Return the request object for the given handler. You MUST NOT use
+ * ne_set_request_body_* on this request object. (this call is only
+ * needed if for instance, you want to add extra headers to the
+ * PROPFIND request). */
+ne_request *ne_propfind_get_request(ne_propfind_handler *handler);
+
+/* A "complex property" has a value which is structured XML. To handle
+ * complex properties, you must set up and register an XML handler
+ * which will understand the elements which make up such properties.
+ * The handler must be registered with the parser returned by
+ * 'ne_propfind_get_parser'.
+ *
+ * To store the parsed value of the property, a 'private' structure is
+ * allocated in each propset (i.e. one per resource). When parsing the
+ * property value elements, for each new resource encountered in the
+ * response, the 'creator' callback is called to retrieve a 'private'
+ * structure for this resource.
+ *
+ * Whilst in XML element callbacks you will have registered to handle
+ * complex properties, you can use the 'ne_propfind_current_private'
+ * call to retrieve the pointer to this private structure.
+ *
+ * To retrieve this 'private' structure from the propset in the
+ * results callback, simply call 'ne_propset_private'.
+ * */
+typedef void *(*ne_props_create_complex)(void *userdata,
+ const char *uri);
+
+void ne_propfind_set_private(ne_propfind_handler *handler,
+ ne_props_create_complex creator,
+ void *userdata);
+
+/* Find all properties.
+ *
+ * Returns NE_*. */
+int ne_propfind_allprop(ne_propfind_handler *handler,
+ ne_props_result result, void *userdata);
+
+/* Find properties named in a call to ne_propfind_set_flat and/or
+ * ne_propfind_set_complex.
+ *
+ * Returns NE_*. */
+int ne_propfind_named(ne_propfind_handler *handler,
+ const ne_propname *prop,
+ ne_props_result result, void *userdata);
+
+/* Destroy a propfind handler after use. */
+void ne_propfind_destroy(ne_propfind_handler *handler);
+
+END_NEON_DECLS
+
+#endif /* NE_PROPS_H */
diff --git a/neon/src/ne_redirect.c b/neon/src/ne_redirect.c
new file mode 100644
index 000000000..bb07a48eb
--- /dev/null
+++ b/neon/src/ne_redirect.c
@@ -0,0 +1,194 @@
+/*
+ HTTP-redirect support
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_session.h"
+#include "ne_request.h"
+#include "ne_alloc.h"
+#include "ne_uri.h"
+#include "ne_redirect.h"
+#include "ne_i18n.h"
+
+/* TODO: should remove this. */
+#include "ne_private.h"
+
+#define REDIRECT_ID "http://www.webdav.org/neon/hooks/http-redirect"
+
+struct redirect {
+ /* per-request stuff. */
+ char *location;
+ ne_request *req;
+ const char *method, *request_uri;
+
+ /* per-session stuff. */
+ ne_session *session;
+ ne_redirect_confirm confirm;
+ ne_redirect_notify notify;
+ void *userdata;
+};
+
+static void *create(void *session, ne_request *req,
+ const char *method, const char *uri);
+static int post_send(void *private, const ne_status *status);
+
+static const ne_request_hooks redirect_hooks = {
+ REDIRECT_ID,
+ create,
+ NULL,
+ post_send,
+ NULL
+};
+
+static void *
+create(void *session, ne_request *req, const char *method, const char *uri)
+{
+ struct redirect *red = session;
+
+ /* Free up the location. */
+ NE_FREE(red->location);
+
+ /* for handling 3xx redirects */
+ ne_add_response_header_handler(req, "Location",
+ ne_duplicate_header, &red->location);
+
+ red->req = req;
+ red->method = method;
+ red->request_uri = uri;
+
+ return red;
+}
+
+/* 2616 says we can't auto-redirect if the method is not GET or HEAD.
+ * We extend this to PROPFIND and OPTIONS, which violates a 2616 MUST,
+ * but is following the spirit of the spec, I think.
+ *
+ * In fact Roy Fielding said as much in a new-httpd posting
+ * <20010224232203.G799@waka.ebuilt.net>: the spec should allow
+ * auto-redirects of any read-only method.
+ *
+ * We have no interface for saying "this method is read-only",
+ * although this might be useful.
+ */
+static int auto_redirect(struct redirect *red)
+{
+ return (strcasecmp(red->method, "HEAD") == 0 ||
+ strcasecmp(red->method, "GET") == 0 ||
+ strcasecmp(red->method, "PROPFIND") == 0 ||
+ strcasecmp(red->method, "OPTIONS") == 0);
+}
+
+static int post_send(void *private, const ne_status *status)
+{
+ struct redirect *red = private;
+ struct uri uri;
+ int ret = NE_OK;
+
+ if ((status->code != 302 && status->code != 301) ||
+ red->location == NULL) {
+ /* Nothing to do. */
+ return NE_OK;
+ }
+
+ if (uri_parse(red->location, &uri, NULL)) {
+ /* Couldn't parse the URI */
+ ne_set_error(red->session, _("Could not parse redirect location."));
+ return NE_ERROR;
+ }
+
+ if ((uri.host != NULL &&
+ strcasecmp(uri.host, red->session->server.hostname) != 0) ||
+ (uri.port != -1 &&
+ uri.port != red->session->server.port) ||
+ (uri.scheme != NULL &&
+ strcasecmp(uri.scheme, ne_get_scheme(red->session)) != 0)) {
+ /* Cannot redirect to another server. Throw this back to the caller
+ * to have them start a new session. */
+ NE_DEBUG(NE_DBG_HTTP,
+ "Redirected to different host/port/scheme:\n"
+ "From %s://%s:%d to %s//%s:%d\n",
+ ne_get_scheme(red->session),
+ red->session->server.hostname,
+ red->session->server.port,
+ uri.scheme, uri.host, uri.port);
+ ret = NE_REDIRECT;
+ ne_set_error(red->session, _("Redirected to a different server.\n"));
+ } else {
+
+ /* Same-server redirect. Can we auto-redirect? */
+
+ if (!auto_redirect(red) &&
+ (red->confirm == NULL ||
+ !(*red->confirm)(red->userdata, red->request_uri, uri.path))) {
+ /* No auto-redirect or confirm failed. */
+ ret = NE_ERROR;
+ } else {
+ /* Okay, follow it: set the new URI and retry the request. */
+ ne_set_request_uri(red->req, uri.path);
+ ret = NE_RETRY;
+ /* Notify them that we're following the redirect. */
+ if (red->notify != NULL) {
+ red->notify(red->userdata, red->request_uri, uri.path);
+ }
+ }
+ }
+
+ /* Free up the URI. */
+ uri_free(&uri);
+
+ return ret;
+}
+
+static void free_redirect(void *cookie)
+{
+ struct redirect *red = cookie;
+ NE_FREE(red->location);
+ free(red);
+}
+
+void ne_redirect_register(ne_session *sess,
+ ne_redirect_confirm confirm,
+ ne_redirect_notify notify,
+ void *userdata)
+{
+ struct redirect *red = ne_calloc(sizeof *red);
+
+ red->confirm = confirm;
+ red->notify = notify;
+ red->userdata = userdata;
+ red->session = sess;
+
+ ne_add_hooks(sess, &redirect_hooks, red, free_redirect);
+}
+
+const char *ne_redirect_location(ne_session *sess)
+{
+ struct redirect *red = ne_session_hook_private(sess, REDIRECT_ID);
+ return red->location;
+}
+
diff --git a/neon/src/ne_redirect.h b/neon/src/ne_redirect.h
new file mode 100644
index 000000000..d18baf572
--- /dev/null
+++ b/neon/src/ne_redirect.h
@@ -0,0 +1,74 @@
+/*
+ HTTP-redirect support
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_REDIRECT_H
+#define NE_REDIRECT_H
+
+#include "ne_request.h"
+
+BEGIN_NEON_DECLS
+
+/* The redirect code does not handle redirects to a different server.
+ * For a redirect to a different server, the request fails and returns
+ * HTTP_REDIRECT. You can then retrieve the redirect location using the
+ * call http_redirect_location.
+ *
+ * (you must have called http_redirect_register for this to happen).
+ * */
+
+/* Get confirmation from the user that a redirect from
+ * URI 'src' to URI 'dest' is acceptable. Should return:
+ * Non-Zero to FOLLOW the redirect
+ * Zero to NOT follow the redirect
+ */
+typedef int (*ne_redirect_confirm)(void *userdata,
+ const char *src, const char *dest);
+
+/* Notify the user that a redirect has been automatically
+ * followed from URI 'src' to URI 'dest' */
+typedef void (*ne_redirect_notify)(void *userdata,
+ const char *src, const char *dest);
+
+/* Register redirect handling for the given session.
+ * Some redirect responses will be automatically followed.
+ * If the redirect is automatically followed, the 'notify' callback
+ * is called.
+ * For redirects which are NOT automatically followed, the
+ * 'confirm' callback is called: if this returns zero, the redirect
+ * is ignored.
+ *
+ * 'confirm' may be passed as NULL: in this case, only automatic
+ * redirects are followed. 'notify' may also be passed as NULL,
+ * automatic redirects are still followed.
+ *
+ * 'userdata' is passed as the first argument to the confirm and
+ * notify callbacks. */
+void ne_redirect_register(ne_session *sess,
+ ne_redirect_confirm confirm,
+ ne_redirect_notify notify,
+ void *userdata);
+
+/* Return location of last redirect. */
+const char *ne_redirect_location(ne_session *sess);
+
+END_NEON_DECLS
+
+#endif /* NE_REDIRECT_H */
diff --git a/neon/src/ne_request.c b/neon/src/ne_request.c
new file mode 100644
index 000000000..af524498f
--- /dev/null
+++ b/neon/src/ne_request.c
@@ -0,0 +1,1340 @@
+/*
+ HTTP request/response handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+/* This is the HTTP client request/response implementation.
+ * The goal of this code is to be modular and simple.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __EMX__
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h> /* just for Win32? */
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef HAVE_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include "ne_i18n.h"
+
+#include "ne_alloc.h"
+#include "ne_request.h"
+#include "ne_string.h" /* for ne_buffer */
+#include "ne_utils.h"
+#include "ne_socket.h"
+#include "ne_uri.h"
+
+#include "ne_private.h"
+
+#define HTTP_EXPECT_TIMEOUT 15
+/* 100-continue only used if size > HTTP_EXPECT_MINSIZ */
+#define HTTP_EXPECT_MINSIZE 1024
+
+/* with thanks to Jim Blandy; this macro simplified loads of code. */
+#define HTTP_ERR(x) do { int _ret = (x); if (_ret != NE_OK) return _ret; } while (0)
+
+static int open_connection(ne_request *req);
+
+/* The iterative step used to produce the hash value. This is DJB's
+ * magic "*33" hash function. Ralf Engelschall has done some amazing
+ * statistical analysis to show that *33 really is a good hash
+ * function: check the new-httpd list archives, or his 'str' library
+ * source code, for the details.
+ *
+ * TODO: due to limited range of characters used in header names,
+ * could maybe get a better hash function to use? */
+
+#define HH_ITERATE(hash, char) (((hash)*33 + char) % HH_HASHSIZE);
+
+/* Produce the hash value for a header name, which MUST be in lower
+ * case. */
+static unsigned int hdr_hash(const char *name)
+{
+ const char *pnt;
+ unsigned int hash = 0;
+
+ for (pnt = name; *pnt != '\0'; pnt++) {
+ hash = HH_ITERATE(hash,*pnt);
+ }
+
+ return hash;
+}
+
+static int set_sockerr(ne_request *req, const char *doing, int code)
+{
+ switch(code) {
+ case 0: /* FIXME: still needed? */
+ case SOCK_CLOSED:
+ if (req->use_proxy) {
+ snprintf(req->session->error, BUFSIZ,
+ _("%s: connection was closed by proxy server."), doing);
+ } else {
+ snprintf(req->session->error, BUFSIZ,
+ _("%s: connection was closed by server."), doing);
+ }
+ return NE_ERROR;
+ case SOCK_TIMEOUT:
+ snprintf(req->session->error, BUFSIZ,
+ _("%s: connection timed out."), doing);
+ return NE_TIMEOUT;
+ default:
+ if (req->session->socket != NULL) {
+ const char *err = sock_get_error(req->session->socket);
+ if (err != NULL) {
+ snprintf(req->session->error, BUFSIZ, "%s: %s", doing, err);
+ } else {
+ snprintf(req->session->error, BUFSIZ, _("%s: socket error."),
+ doing);
+ }
+ } else {
+ snprintf(req->session->error, BUFSIZ,
+ "%s: %s", doing, strerror(errno));
+ }
+ return NE_ERROR;
+ }
+}
+
+static char *lower_string(const char *str)
+{
+ char *ret = ne_malloc(strlen(str) + 1), *pnt;
+
+ for (pnt = ret; *str != '\0'; str++) {
+ *pnt++ = tolower(*str);
+ }
+
+ *pnt = '\0';
+
+ return ret;
+}
+
+static void notify_status(ne_session *sess, ne_conn_status status,
+ const char *info)
+{
+ if (sess->notify_cb) {
+ sess->notify_cb(sess->notify_ud, status, info);
+ }
+}
+
+void ne_duplicate_header(void *userdata, const char *value)
+{
+ char **location = userdata;
+ *location = ne_strdup(value);
+}
+
+void ne_handle_numeric_header(void *userdata, const char *value)
+{
+ int *location = userdata;
+ *location = atoi(value);
+}
+
+void ne_add_hooks(ne_session *sess,
+ const ne_request_hooks *hooks, void *private,
+ ne_free_hooks free_hooks)
+{
+ struct hook *hk = ne_malloc(sizeof(struct hook));
+ hk->hooks = hooks;
+ hk->private = private;
+ hk->next = sess->hooks;
+ hk->free = free_hooks;
+ sess->hooks = hk;
+}
+
+void *ne_request_hook_private(ne_request *req, const char *id)
+{
+ struct hook_request *hk;
+
+ for (hk = req->hook_store; hk != NULL; hk = hk->next) {
+ if (strcasecmp(hk->hook->hooks->id, id) == 0) {
+ return hk->cookie;
+ }
+ }
+
+ return NULL;
+}
+
+void *ne_session_hook_private(ne_session *sess, const char *id)
+{
+ struct hook *hk;
+
+ for (hk = sess->hooks; hk != NULL; hk = hk->next) {
+ if (strcasecmp(hk->hooks->id, id) == 0) {
+ return hk->private;
+ }
+ }
+
+ return NULL;
+}
+
+static ssize_t body_string_send(void *userdata, char *buffer, size_t count)
+{
+ ne_request *req = userdata;
+
+ if (count == 0) {
+ req->body_left = req->body_size;
+ req->body_pnt = req->body_buffer;
+ } else {
+ /* if body_left == 0 we fall through and return 0. */
+ if (req->body_left < count)
+ count = req->body_left;
+
+ memcpy(buffer, req->body_pnt, count);
+ req->body_pnt += count;
+ req->body_left -= count;
+ }
+
+ return count;
+}
+
+static ssize_t body_fd_send(void *userdata, char *buffer, size_t count)
+{
+ ne_request *req = userdata;
+
+ if (count) {
+ return read(req->body_fd, buffer, count);
+ } else {
+ /* rewind since we may have to send it again */
+ return lseek(req->body_fd, SEEK_SET, 0);
+ }
+}
+
+/* Pulls the request body from the source and pushes it to the given
+ * callback. Returns 0 on success, or NE_* code */
+int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud)
+{
+ int ret = 0;
+ char buffer[BUFSIZ];
+ size_t bytes;
+
+ /* tell the source to start again from the beginning. */
+ (void) req->body_cb(req->body_ud, NULL, 0);
+
+ while ((bytes = req->body_cb(req->body_ud, buffer, BUFSIZ)) > 0) {
+ ret = fn(ud, buffer, bytes);
+ if (ret < 0)
+ break;
+ }
+
+ if (bytes < 0) {
+ ne_set_error(req->session, _("Error reading request body."));
+ ret = NE_ERROR;
+ }
+
+ return ret;
+}
+
+/* Sends the request body down the socket.
+ * Returns 0 on success, or NE_* code */
+static int send_request_body(ne_request *req)
+{
+ int ret = ne_pull_request_body(req, (ne_push_fn)sock_fullwrite,
+ req->session->socket);
+
+ if (ret < 0) {
+ /* transfer failed */
+ req->forced_close = 1;
+ ret = set_sockerr(req, _("Could not send request body"), ret);
+ }
+
+ return ret;
+}
+
+/* Lob the User-Agent, connection and host headers in to the request
+ * headers */
+static void add_fixed_headers(ne_request *req)
+{
+ if (req->session->user_agent) {
+ ne_buffer_concat(req->headers,
+ "User-Agent: ", req->session->user_agent, EOL, NULL);
+ }
+ /* Send Connection: Keep-Alive for pre-1.1 origin servers, so we
+ * might get a persistent connection. 2068 sec 19.7.1 says we MUST
+ * NOT do this for proxies, though. So we don't. Note that on the
+ * first request on any session, we don't know whether the server
+ * is 1.1 compliant, so we presume that it is not. */
+ if (ne_version_pre_http11(req->session) && !req->use_proxy) {
+ ne_buffer_zappend(req->headers, "Keep-Alive: " EOL);
+ ne_buffer_zappend(req->headers, "Connection: TE, Keep-Alive");
+ } else {
+ ne_buffer_zappend(req->headers, "Connection: TE");
+ }
+ if (req->upgrade_to_tls) {
+ ne_buffer_zappend(req->headers, ", Upgrade");
+ }
+ ne_buffer_zappend(req->headers, EOL);
+ if (req->upgrade_to_tls) {
+ ne_buffer_zappend(req->headers, "Upgrade: TLS/1.0" EOL);
+ }
+ /* We send TE: trailers since we understand trailers in the chunked
+ * response. */
+ ne_buffer_zappend(req->headers, "TE: trailers" EOL);
+
+}
+
+int ne_accept_always(void *userdata, ne_request *req, ne_status *st)
+{
+ return 1;
+}
+
+int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st)
+{
+ return (st->klass == 2);
+}
+
+/* Exposed since redirect hooks need this.
+ *
+ * DESIGN FLAW: mutating the URI does not get passed down to hooks.
+ * auth hooks need the URI. */
+void ne_set_request_uri(ne_request *req, const char *uri)
+{
+ ne_buffer *real_uri = ne_buffer_create();
+ req->abs_path = ne_strdup(uri);
+ if (req->use_proxy && strcmp(uri, "*") != 0)
+ ne_buffer_concat(real_uri, ne_get_scheme(req->session), "://",
+ req->session->server.hostport, NULL);
+ ne_buffer_zappend(real_uri, uri);
+ req->uri = ne_buffer_finish(real_uri);
+}
+
+/* Handler for the "Transfer-Encoding" response header */
+static void te_hdr_handler(void *userdata, const char *value)
+{
+ struct ne_response *resp = userdata;
+ if (strcasecmp(value, "chunked") == 0) {
+ resp->is_chunked = 1;
+ } else {
+ resp->is_chunked = 0;
+ }
+}
+
+/* Handler for the "Connection" response header */
+static void connection_hdr_handler(void *userdata, const char *value)
+{
+ ne_request *req = userdata;
+ if (strcasecmp(value, "close") == 0) {
+ req->forced_close = 1;
+ } else if (strcasecmp(value, "Keep-Alive") == 0) {
+ req->can_persist = 1;
+ }
+}
+
+/* Initializes the request with given method and URI.
+ * URI must be abs_path - i.e., NO scheme+hostname. It will BREAK
+ * otherwise. */
+ne_request *ne_request_create(ne_session *sess,
+ const char *method, const char *uri)
+{
+ ne_request *req = ne_calloc(sizeof(ne_request));
+
+ NE_DEBUG(NE_DBG_HTTP, "Creating request...\n");
+
+ req->session = sess;
+ req->headers = ne_buffer_create();
+ req->reqbuf = ne_buffer_create();
+ req->respbuf = ne_buffer_create_sized(BUFSIZ);
+
+ /* Add in the fixed headers */
+ add_fixed_headers(req);
+
+ /* Set the standard stuff */
+ req->method = method;
+ req->method_is_head = (strcmp(req->method, "HEAD") == 0);
+
+ /* FIXME: the proxy_decider is broken if they called
+ * ne_session_proxy before ne_session_server, since in that
+ * case we have not done a name lookup on the session server. */
+ if (sess->have_proxy && sess->proxy_decider != NULL) {
+ req->use_proxy =
+ (*sess->proxy_decider)(sess->proxy_decider_udata,
+ ne_get_scheme(sess), sess->server.hostname);
+ }
+ else {
+ req->use_proxy = sess->have_proxy;
+ }
+
+ if (sess->request_secure_upgrade == 1) {
+ req->upgrade_to_tls = 1;
+ }
+
+ /* Add in handlers for all the standard HTTP headers. */
+
+ ne_add_response_header_handler(req, "Content-Length",
+ ne_handle_numeric_header, &req->resp.length);
+ ne_add_response_header_handler(req, "Transfer-Encoding",
+ te_hdr_handler, &req->resp);
+ ne_add_response_header_handler(req, "Connection",
+ connection_hdr_handler, req);
+
+ if (uri) {
+ ne_set_request_uri(req, uri);
+ }
+
+ {
+ struct hook *hk;
+ struct hook_request *store;
+ void *cookie;
+
+ NE_DEBUG(NE_DBG_HTTP, "Running request create hooks.\n");
+
+ for (hk = sess->hooks; hk != NULL; hk = hk->next) {
+ cookie = (*hk->hooks->create)(hk->private, req, method, uri);
+ if (cookie != NULL) {
+ store = ne_malloc(sizeof(struct hook_request));
+ store->hook = hk;
+ store->cookie = cookie;
+ store->next = req->hook_store;
+ req->hook_store = store;
+ }
+ }
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Request created.\n");
+
+ return req;
+}
+
+static void set_body_size(ne_request *req, size_t size)
+{
+ req->body_size = size;
+ ne_print_request_header(req, "Content-Length", "%d", size);
+}
+
+void ne_set_request_body_buffer(ne_request *req, const char *buffer,
+ size_t size)
+{
+ req->body_buffer = buffer;
+ req->body_cb = body_string_send;
+ req->body_ud = req;
+ set_body_size(req, size);
+}
+
+void ne_set_request_body_provider(ne_request *req, size_t bodysize,
+ ne_provide_body provider, void *ud)
+{
+ req->body_cb = provider;
+ req->body_ud = ud;
+ set_body_size(req, bodysize);
+}
+
+int ne_set_request_body_fd(ne_request *req, int fd)
+{
+ struct stat bodyst;
+
+ /* Get file length */
+ if (fstat(fd, &bodyst) < 0) {
+ const char *err = strerror(errno);
+ /* Stat failed */
+ snprintf(req->session->error, BUFSIZ,
+ _("Could not find file length: %s"), err);
+ NE_DEBUG(NE_DBG_HTTP, "Stat failed: %s\n", err);
+ return -1;
+ }
+ req->body_fd = fd;
+ req->body_cb = body_fd_send;
+ req->body_ud = req;
+ set_body_size(req, bodyst.st_size);
+ return 0;
+}
+
+void ne_add_request_header(ne_request *req, const char *name,
+ const char *value)
+{
+ ne_buffer_concat(req->headers, name, ": ", value, EOL, NULL);
+}
+
+void ne_print_request_header(ne_request *req, const char *name,
+ const char *format, ...)
+{
+ va_list params;
+ char buf[BUFSIZ];
+
+ va_start(params, format);
+ vsnprintf(buf, BUFSIZ, format, params);
+ va_end(params);
+
+ ne_buffer_concat(req->headers, name, ": ", buf, EOL, NULL);
+}
+
+void
+ne_add_response_header_handler(ne_request *req, const char *name,
+ ne_header_handler hdl, void *userdata)
+{
+ struct header_handler *new = ne_calloc(sizeof *new);
+ int hash;
+ new->name = lower_string(name);
+ new->handler = hdl;
+ new->userdata = userdata;
+ hash = hdr_hash(new->name);
+ new->next = req->header_handlers[hash];
+ req->header_handlers[hash] = new;
+}
+
+void ne_add_response_header_catcher(ne_request *req,
+ ne_header_handler hdl, void *userdata)
+{
+ struct header_handler *new = ne_calloc(sizeof *new);
+ new->handler = hdl;
+ new->userdata = userdata;
+ new->next = req->header_catchers;
+ req->header_catchers = new;
+}
+
+void
+ne_add_response_body_reader(ne_request *req, ne_accept_response acpt,
+ ne_block_reader rdr, void *userdata)
+{
+ struct body_reader *new = ne_malloc(sizeof(struct body_reader));
+ new->accept_response = acpt;
+ new->handler = rdr;
+ new->userdata = userdata;
+ new->next = req->body_readers;
+ req->body_readers = new;
+}
+
+void ne_request_destroy(ne_request *req)
+{
+ struct body_reader *rdr, *next_rdr;
+ struct header_handler *hdlr, *next_hdlr;
+ struct hook_request *st, *next_st;
+ int n;
+
+ NE_FREE(req->uri);
+ NE_FREE(req->abs_path);
+
+ for (rdr = req->body_readers; rdr != NULL; rdr = next_rdr) {
+ next_rdr = rdr->next;
+ free(rdr);
+ }
+
+ for (hdlr = req->header_catchers; hdlr != NULL; hdlr = next_hdlr) {
+ next_hdlr = hdlr->next;
+ free(hdlr);
+ }
+
+ for (n = 0; n < HH_HASHSIZE; n++) {
+ for (hdlr = req->header_handlers[n]; hdlr != NULL;
+ hdlr = next_hdlr) {
+ next_hdlr = hdlr->next;
+ free(hdlr->name);
+ free(hdlr);
+ }
+ }
+
+ ne_buffer_destroy(req->headers);
+ ne_buffer_destroy(req->reqbuf);
+ ne_buffer_destroy(req->respbuf);
+
+ NE_DEBUG(NE_DBG_HTTP, "Running destroy hooks.\n");
+ for (st = req->hook_store; st!=NULL; st = next_st) {
+ next_st = st->next;
+ if (HAVE_HOOK(st,destroy)) {
+ HOOK_FUNC(st,destroy)(st->cookie);
+ }
+ free(st);
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Request ends.\n");
+ free(req);
+}
+
+
+/* Reads a block of the response into buffer, which is of size buflen.
+ * Returns number of bytes read, 0 on end-of-response, or NE_* on error.
+ * TODO?: only make one actual read() call in here...
+ */
+static int read_response_block(ne_request *req, struct ne_response *resp,
+ char *buffer, size_t *buflen)
+{
+ int willread, readlen;
+ nsocket *sock = req->session->socket;
+ if (resp->is_chunked) {
+ /* We are doing a chunked transfer-encoding.
+ * It goes: `SIZE CRLF CHUNK CRLF SIZE CRLF CHUNK CRLF ...'
+ * ended by a `CHUNK CRLF 0 CRLF', a 0-sized chunk.
+ * The slight complication is that we have to cope with
+ * partial reads of chunks.
+ * For this reason, resp.chunk_left contains the number of
+ * bytes left to read in the current chunk.
+ */
+ if (resp->chunk_left == 0) {
+ long int chunk_len;
+ /* We are at the start of a new chunk. */
+ NE_DEBUG(NE_DBG_HTTP, "New chunk.\n");
+ readlen = sock_readline(sock, buffer, *buflen);
+ if (readlen <= 0) {
+ return set_sockerr(req, _("Could not read chunk size"), readlen);
+ }
+ NE_DEBUG(NE_DBG_HTTP, "[Chunk Size] < %s", buffer);
+ chunk_len = strtol(buffer, NULL, 16);
+ if (chunk_len == LONG_MIN || chunk_len == LONG_MAX) {
+ NE_DEBUG(NE_DBG_HTTP, "Couldn't read chunk size.\n");
+ ne_set_error(req->session, _("Could not parse chunk size"));
+ return -1;
+ }
+ NE_DEBUG(NE_DBG_HTTP, "Got chunk size: %ld\n", chunk_len);
+ if (chunk_len == 0) {
+ /* Zero-size chunk == end of response. */
+ NE_DEBUG(NE_DBG_HTTP, "Zero-size chunk.\n");
+ *buflen = 0;
+ return NE_OK;
+ }
+ resp->chunk_left = chunk_len;
+ }
+ willread = min(*buflen - 1, resp->chunk_left);
+ } else if (resp->length > 0) {
+ /* Have we finished reading the body? */
+ if (resp->left == 0) {
+ *buflen = 0;
+ return NE_OK;
+ }
+ willread = min(*buflen - 1, resp->left);
+ } else {
+ /* Read until socket-close */
+ willread = *buflen - 1;
+ }
+ NE_DEBUG(NE_DBG_HTTP, "Reading %d bytes of response body.\n", willread);
+ readlen = sock_read(sock, buffer, willread);
+
+ /* EOF is valid if we don't know the response body length, or
+ * we've read all of the response body, and we're not using
+ * chunked. */
+ if (readlen == SOCK_CLOSED && resp->length <= 0 && !resp->is_chunked) {
+ NE_DEBUG(NE_DBG_HTTP, "Got EOF.\n");
+ readlen = 0;
+ } else if (readlen < 0) {
+ return set_sockerr(req, _("Could not read response body"), readlen);
+ } else {
+ NE_DEBUG(NE_DBG_HTTP, "Got %d bytes.\n", readlen);
+ }
+ buffer[readlen] = '\0';
+ *buflen = readlen;
+ NE_DEBUG(NE_DBG_HTTPBODY, "Read block:\n%s\n", buffer);
+ if (resp->is_chunked) {
+ resp->chunk_left -= readlen;
+ if (resp->chunk_left == 0) {
+ char crlfbuf[2];
+ /* If we've read a whole chunk, read a CRLF */
+ readlen = sock_fullread(sock, crlfbuf, 2);
+ if (readlen < 0 || strncmp(crlfbuf, EOL, 2) != 0) {
+ return set_sockerr(req,
+ _("Error reading chunked response body"),
+ readlen);
+ }
+ }
+ } else if (resp->length > 0) {
+ resp->left -= readlen;
+ }
+ return NE_OK;
+}
+
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen)
+{
+ struct body_reader *rdr;
+ size_t readlen = buflen;
+
+ if (req->resp.length == 0) {
+ return 0;
+ }
+
+ if (read_response_block(req, &req->resp, buffer, &readlen)) {
+ /* it failed. */
+ req->forced_close = 1;
+ return -1;
+ }
+
+ if (req->session->progress_cb) {
+ req->resp.total += readlen;
+ req->session->progress_cb(req->session->progress_ud, req->resp.total,
+ req->resp.is_chunked?-1:req->resp.length);
+ }
+
+ /* TODO: call the readers when this fails too. */
+ for (rdr = req->body_readers; rdr!=NULL; rdr=rdr->next) {
+ if (rdr->use)
+ (*rdr->handler)(rdr->userdata, buffer, readlen);
+ }
+
+ return readlen;
+}
+
+/* Build the request. */
+static const char *build_request(ne_request *req)
+{
+ const char *uri;
+ struct hook_request *st;
+ ne_buffer *buf = req->reqbuf;
+
+ /* If we are talking to a proxy, we send them the absoluteURI
+ * as the Request-URI. If we are talking to a server, we just
+ * send abs_path. */
+ if (req->use_proxy)
+ uri = req->uri;
+ else
+ uri = req->abs_path;
+
+ ne_buffer_clear(buf);
+
+ /* Add in the request and the user-supplied headers */
+ ne_buffer_concat(buf, req->method, " ", uri, " HTTP/1.1" EOL,
+ req->headers->data, NULL);
+
+ /* And the all-important Host header. This is done here since it
+ * might change for a new server. */
+ ne_buffer_concat(buf, "Host: ", req->session->server.hostport,
+ EOL, NULL);
+
+
+ /* Now handle the body. */
+ req->use_expect100 = 0;
+ if ((req->session->expect100_works > -1) &&
+ (req->body_size > HTTP_EXPECT_MINSIZE) &&
+ !ne_version_pre_http11(req->session)) {
+ /* Add Expect: 100-continue. */
+ ne_buffer_zappend(buf, "Expect: 100-continue" EOL);
+ req->use_expect100 = 1;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Running pre_send hooks\n");
+ for (st = req->hook_store; st!=NULL; st = st->next) {
+ if (HAVE_HOOK(st,pre_send)) {
+ HOOK_FUNC(st,pre_send)(st->cookie, buf);
+ }
+ }
+
+ /* Final CRLF */
+ ne_buffer_zappend(buf, EOL);
+
+ return buf->data;
+}
+
+/* FIXME: this function need re-writing.
+ *
+ * Returns HTTP_*, and sets session error appropriately.
+ */
+static int send_request(ne_request *req)
+{
+ ne_session *sess = req->session;
+ int ret, try_again, send_attempt;
+ const char *request = build_request(req);
+ char *buffer = req->respbuf->data;
+
+ try_again = 1;
+
+ do {
+
+ /* FIXME: this is broken */
+ try_again--;
+
+#ifdef DEBUGGING
+ {
+ if ((NE_DBG_HTTPPLAIN&ne_debug_mask) == NE_DBG_HTTPPLAIN) {
+ /* Display everything mode */
+ NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", request);
+ } else {
+ /* Blank out the Authorization paramaters */
+ char *reqdebug = ne_strdup(request), *pnt = reqdebug;
+ while ((pnt = strstr(pnt, "Authorization: ")) != NULL) {
+ for (pnt += 15; *pnt != '\r' && *pnt != '\0'; pnt++) {
+ *pnt = 'x';
+ }
+ }
+ NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", reqdebug);
+ free(reqdebug);
+ }
+ }
+#endif /* DEBUGGING */
+
+ /* Send the Request-Line and headers */
+ for (send_attempt = 0; send_attempt < 2; send_attempt++) {
+ NE_DEBUG(NE_DBG_HTTP, "Sending headers: attempt %d\n", send_attempt);
+ /* Open the connection if necessary */
+ ret = open_connection(req);
+ if (ret != NE_OK) {
+ return ret;
+ }
+ ret = sock_send_string(req->session->socket, request);
+ if (ret == SOCK_CLOSED) {
+ /* Could happen due to a persistent connection timeout.
+ * Or the server being restarted. */
+ NE_DEBUG(NE_DBG_HTTP, "Connection was closed by server.\n");
+ ne_close_connection(req->session);
+ } else {
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ return set_sockerr(req, _("Could not send request"), ret);
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Request sent\n");
+
+ /* Now, if we are doing a Expect: 100, hang around for a short
+ * amount of time, to see if the server actually cares about the
+ * Expect and sends us a 100 Continue response if the request
+ * is valid, else an error code if it's not. This saves sending
+ * big files to the server when they will be rejected.
+ */
+
+ if (req->use_expect100) {
+ NE_DEBUG(NE_DBG_HTTP, "Waiting for response...\n");
+ ret = sock_block(sess->socket, HTTP_EXPECT_TIMEOUT);
+ switch(ret) {
+ case SOCK_TIMEOUT:
+ /* Timed out - i.e. Expect: ignored. There is a danger
+ * here that the server DOES respect the Expect: header,
+ * but was going SO slowly that it didn't get time to
+ * respond within HTTP_EXPECT_TIMEOUT.
+ * TODO: while sending the body, check to see if the
+ * server has sent anything back - if it HAS, then
+ * stop sending - this is a spec compliance SHOULD */
+ NE_DEBUG(NE_DBG_HTTP, "Wait timed out.\n");
+ sess->expect100_works = -1; /* don't try that again */
+ /* Try sending the request again without using 100-continue */
+ try_again++;
+ continue;
+ break;
+ case SOCK_CLOSED:
+ case SOCK_ERROR: /* error */
+ return set_sockerr(req, _("Error waiting for response"), ret);
+ default:
+ NE_DEBUG(NE_DBG_HTTP, "Wait got data.\n");
+ sess->expect100_works = 1; /* it works - use it again */
+ break;
+ }
+ } else if (req->body_size) {
+ /* Just chuck the file down the socket */
+ NE_DEBUG(NE_DBG_HTTP, "Sending body...\n");
+ ret = send_request_body(req);
+ if (ret == SOCK_CLOSED) {
+ /* This happens if the persistent connection times out:
+ * the first write() of the headers gets a delayed write
+ * seemingly, so the write doesn't fail till this one.
+ */
+ NE_DEBUG(NE_DBG_HTTP, "Connection closed before request sent, retrying\n");
+ try_again++;
+ ne_close_connection(req->session);
+ continue;
+ } else if (ret < 0) {
+ NE_DEBUG(NE_DBG_HTTP, "Body send failed.\n");
+ return ret;
+ }
+ NE_DEBUG(NE_DBG_HTTP, "Body sent.\n");
+
+ }
+
+ /* Now, we have either:
+ * - Sent the header and body, or
+ * - Sent the header incl. Expect: line, and got some response.
+ * In any case, we get the status line of the response.
+ */
+
+ /* HTTP/1.1 says that the server MAY emit any number of
+ * interim 100 (Continue) responses prior to the normal
+ * response. So loop while we get them. */
+
+ do {
+ if (sock_readline(sess->socket, buffer, BUFSIZ) <= 0) {
+ if (try_again) {
+ return set_sockerr(req, _("Could not read status line"), ret);
+ }
+ NE_DEBUG(NE_DBG_HTTP, "Failed to read status line.\n");
+ try_again++;
+ break;
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "[Status Line] < %s", buffer);
+
+ /* Got the status line - parse it */
+ if (ne_parse_statusline(buffer, &req->status)) {
+ ne_set_error(sess, _("Could not parse response status line."));
+ return -1;
+ }
+
+ sess->version_major = req->status.major_version;
+ sess->version_minor = req->status.minor_version;
+ snprintf(sess->error, BUFSIZ, "%d %s",
+ req->status.code, req->status.reason_phrase);
+ STRIP_EOL(sess->error);
+
+ if (req->status.klass == 1) {
+ NE_DEBUG(NE_DBG_HTTP, "Got 1xx-class.\n");
+ /* Skip any headers, we don't need them */
+ do {
+ ret = sock_readline(sess->socket, buffer, BUFSIZ);
+ if (ret <= 0) {
+ return set_sockerr(
+ req, _("Error reading response headers"), ret);
+ }
+ NE_DEBUG(NE_DBG_HTTP, "[Ignored header] < %s", buffer);
+ } while (strcmp(buffer, EOL) != 0);
+
+ if (req->use_expect100 && (req->status.code == 100)) {
+ /* We are using Expect: 100, and we got a 100-continue
+ * return code... send the request body */
+ NE_DEBUG(NE_DBG_HTTP, "Got continue... sending body now.\n");
+ HTTP_ERR(send_request_body(req));
+ NE_DEBUG(NE_DBG_HTTP, "Body sent.\n");
+ } else if (req->upgrade_to_tls && (req->status.code == 101)) {
+ /* Switch to TLS on the fly */
+ if (sock_make_secure(sess->socket, sess->ssl_context)) {
+ set_sockerr(req, _("Could not negotiate SSL session"),
+ SOCK_ERROR);
+ ne_close_connection(sess);
+ return NE_ERROR;
+ }
+ }
+ }
+ } while (req->status.klass == 1);
+
+ if (try_again == 1) {
+ /* If we're trying again, close the conn first */
+ NE_DEBUG(NE_DBG_HTTP, "Retrying request, closing connection first.\n");
+ ne_close_connection(sess);
+ }
+
+ } while (try_again == 1);
+
+ return NE_OK;
+}
+
+/* Read a message header from sock into buf, which has size 'buflen'.
+ *
+ * Returns:
+ * NE_RETRY: Success, read a header into buf.
+ * NE_OK: End of headers reached.
+ * NE_ERROR: Error (session error is set).
+ */
+static int read_message_header(ne_request *req, char *buf, size_t buflen)
+{
+ int ret, n;
+ size_t len; /* holds strlen(buf) */
+ nsocket *sock = req->session->socket;
+
+ n = sock_readline(sock, buf, buflen);
+ if (n <= 0)
+ return set_sockerr(req, _("Error reading response headers"), n);
+ NE_DEBUG(NE_DBG_HTTP, "[hdr] %s", buf);
+
+ STRIP_EOL(buf);
+
+ len = strlen(buf);
+
+ if (len == 0) {
+ NE_DEBUG(NE_DBG_HTTP, "End of headers.\n");
+ return NE_OK;
+ }
+
+ while (buflen > 0) {
+ char ch;
+
+ /* Collect any extra lines into buffer */
+ ret = sock_peek(sock, &ch, 1);
+ if (ret <= 0) {
+ /* FIXME: can sock_peek return 0? */
+ return set_sockerr(req, _("Error reading response headers"), ret);
+ }
+ if (ch != ' ' && ch != '\t') {
+ /* No continuation of this header. NUL-terminate to be paranoid. */
+ return NE_RETRY;
+ }
+
+ /* Otherwise, read the next line onto the end of 'buf'. */
+ buf += len;
+ buflen -= len;
+
+ /* Read BUFSIZ-1 bytes to guarantee that we have a \0 */
+ ret = sock_readline(sock, buf, buflen);
+ if (ret <= 0) {
+ return set_sockerr(req, _("Error reading response headers"), ret);
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "[cont] %s", buf);
+
+ STRIP_EOL(buf);
+ len = strlen(buf);
+
+ /* assert(buf[0] == ch), which implies len(buf) > 0.
+ * Otherwise the TCP stack is lying, but we'll be paranoid.
+ * This might be a \t, so replace it with a space to be
+ * friendly to applications (2616 says we MAY do this). */
+ if (len) buf[0] = ' ';
+ }
+
+ ne_set_error(req->session, _("Response header too long"));
+ return NE_ERROR;
+}
+
+static void normalize_response_length(ne_request *req)
+{
+ /* Response entity-body length calculation, bit icky.
+ * Here, we set:
+ * length==-1 if we DO NOT know the exact body length
+ * length>=0 if we DO know the body length.
+ *
+ * RFC2616, section 4.4:
+ * NO body is returned if the method is HEAD, or the resp status
+ * is 204 or 304
+ */
+ if (req->method_is_head || req->status.code==204 ||
+ req->status.code==304) {
+ req->resp.length = 0;
+ } else {
+ /* RFC2616, section 4.4: if we have a transfer encoding
+ * and a content-length, then ignore the content-length. */
+ if ((req->resp.length>-1) &&
+ (req->resp.is_chunked)) {
+ req->resp.length = -1;
+ }
+ }
+ /* Noddy noddy noddy. Testing from Apache/mod_proxy, CONNECT does
+ * not return a Content-Length... */
+ if (req->resp.length == -1 && req->session->in_connect &&
+ req->status.klass == 2) {
+ req->resp.length = 0;
+ }
+
+}
+
+/* Apache's default is 100, seems reasonable. */
+#define MAX_HEADER_FIELDS 100
+
+#define MAX_HEADER_LENGTH 8192
+
+/* Read response headers, using buffer buffer.
+ * Returns NE_* code, sets session error. */
+static int read_response_headers(ne_request *req)
+{
+ char hdr[MAX_HEADER_LENGTH] = {0};
+ int ret, count = 0;
+
+ /* Read response headers. This loop was once optimized so that
+ * GCC will put all the local vars in registers, but that was a
+ * long time ago. */
+ while ((ret = read_message_header(req, hdr, MAX_HEADER_LENGTH)) == NE_RETRY
+ && ++count < MAX_HEADER_FIELDS) {
+ struct header_handler *hdl;
+ /* hint to the compiler that we'd like these in registers */
+ register char *pnt;
+ register int hash = 0;
+
+ for (hdl = req->header_catchers; hdl != NULL; hdl = hdl->next) {
+ (*hdl->handler)(hdl->userdata, hdr);
+ }
+
+ /* Iterate over the header name, converting it to lower case and
+ * calculating the hash value as we go. */
+ for (pnt = hdr; *pnt != '\0' && *pnt != ':'; pnt++) {
+ *pnt = tolower(*pnt);
+ hash = HH_ITERATE(hash,*pnt);
+ }
+
+ if (*pnt != '\0') {
+ /* Null-term name at the : */
+ *pnt = '\0';
+
+ /* Value starts after any whitespace... */
+ do {
+ pnt++;
+ } while (*pnt == ' ' || *pnt == '\t');
+
+ NE_DEBUG(NE_DBG_HTTP, "Header Name: [%s], Value: [%s]\n", hdr, pnt);
+
+ /* Iterate through the header handlers */
+ for (hdl = req->header_handlers[hash]; hdl != NULL;
+ hdl = hdl->next) {
+ if (strcmp(hdr, hdl->name) == 0) {
+ (*hdl->handler)(hdl->userdata, pnt);
+ }
+ }
+ } else {
+ ne_set_error(req->session, _("Malformed header line."));
+ return NE_ERROR;
+ }
+ }
+
+ if (count == MAX_HEADER_FIELDS) {
+ ne_set_error(req->session,
+ _("Response exceeded maximum number of header fields."));
+ ret = NE_ERROR;
+ }
+
+ return ret;
+}
+
+int ne_begin_request(ne_request *req)
+{
+ struct body_reader *rdr;
+
+ NE_DEBUG(NE_DBG_HTTPBASIC, "Request: %s %s\n", req->method, req->uri);
+
+ req->can_persist = 0;
+ req->forced_close = 0;
+ req->resp.length = -1;
+ req->resp.is_chunked = 0;
+
+ /* Now send the request, and read the Status-Line */
+ HTTP_ERR(send_request(req));
+
+ /* Read the headers */
+ HTTP_ERR(read_response_headers(req));
+
+ /* response message logic */
+ normalize_response_length(req);
+
+ /* Prepare for reading the response entity-body. call each of the
+ * body readers and ask them whether they want to accept this
+ * response or not. */
+ for (rdr = req->body_readers; rdr != NULL; rdr=rdr->next) {
+ rdr->use = (*rdr->accept_response)(rdr->userdata, req, &req->status);
+ }
+
+ req->resp.left = req->resp.length;
+ req->resp.chunk_left = 0;
+
+ return NE_OK;
+}
+
+int ne_end_request(ne_request *req)
+{
+ struct hook_request *st;
+ int ret = NE_OK;
+
+ NE_DEBUG(NE_DBG_HTTPBASIC, "Response: %s\n", req->session->error);
+
+ /* Read headers in chunked trailers */
+ if (req->resp.is_chunked) {
+ HTTP_ERR(read_response_headers(req));
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Running post_send hooks\n");
+ for (st = req->hook_store; ret == NE_OK && st != NULL; st = st->next) {
+ if (HAVE_HOOK(st,post_send)) {
+ ret = HOOK_FUNC(st,post_send)(st->cookie, &req->status);
+ }
+ }
+
+ NE_DEBUG(NE_DBG_HTTP, "Connection status: %s, %s, %s\n",
+ req->forced_close?"forced close":"no forced close",
+ req->session->no_persist?"no persistent connection":"persistent connection",
+ ne_version_pre_http11(req->session)?"pre-HTTP/1.1":"HTTP/1.1 or later");
+
+ /* Close the connection if any of the following are true:
+ * - We have a forced close (e.g. "Connection: close" header)
+ * - We are not using persistent connections for this session
+ * - All of the following are true:
+ * * this is HTTP/1.0
+ * * and they haven't said they can do persistent connections
+ * * we've not just done a successful CONNECT
+ */
+ if (req->forced_close || req->session->no_persist ||
+ (ne_version_pre_http11(req->session) &&
+ !req->can_persist &&
+ (!req->session->in_connect || req->status.klass != 2))) {
+ ne_close_connection(req->session);
+ }
+
+ /* Retry if any hook asked us too (i.e. authentication hooks) */
+
+ return ret;
+}
+
+/* HTTP/1.x request/response mechanism
+ *
+ * Returns an NE_* return code.
+ *
+ * The status information is placed in status. The error string is
+ * placed in req->session->error
+ */
+int ne_request_dispatch(ne_request *req)
+{
+ int ret;
+ ssize_t len;
+
+ /* Loop sending the request:
+ * Retry whilst authentication fails and we supply it. */
+
+ do {
+ char buffer[BUFSIZ];
+
+ HTTP_ERR(ne_begin_request(req));
+
+ do {
+ len = ne_read_response_block(req, buffer, BUFSIZ);
+ } while (len > 0);
+
+ if (len < 0) {
+ return NE_ERROR;
+ }
+
+ ret = ne_end_request(req);
+
+ } while (ret == NE_RETRY);
+
+ NE_DEBUG(NE_DBG_HTTP | NE_DBG_FLUSH,
+ "Request ends, status %d class %dxx, error line:\n%s\n",
+ req->status.code, req->status.klass, req->session->error);
+
+ return ret;
+}
+
+const ne_status *ne_get_status(ne_request *req)
+{
+ return &(req->status);
+}
+
+/* Create a CONNECT tunnel through the proxy server.
+ * Returns HTTP_* */
+static int proxy_tunnel(ne_session *sess)
+{
+ /* Hack up an HTTP CONNECT request... */
+ ne_request *req = ne_request_create(sess, "CONNECT", NULL);
+ int ret = NE_OK;
+
+ /* Fudge the URI to be how we want it */
+ req->uri = ne_strdup(sess->server.hostport);
+
+ sess->connected = 1;
+ sess->in_connect = 1;
+
+ ret = ne_request_dispatch(req);
+
+ sess->in_connect = 0;
+
+ if (ret != NE_OK || !sess->connected ||
+ req->status.klass != 2) {
+ /* It failed */
+ ne_set_error(sess,
+ _("Could not create SSL connection through proxy server"));
+ ret = NE_ERROR;
+ }
+
+ ne_request_destroy(req);
+
+ return ret;
+}
+
+static int open_connection(ne_request *req)
+{
+ ne_session *sess = req->session;
+
+ if (req->use_proxy) {
+ switch(sess->connected) {
+ case 0:
+ /* Make the TCP connection to the proxy */
+ NE_DEBUG(NE_DBG_SOCKET, "Connecting to proxy at %s:%d...\n",
+ sess->proxy.hostname, sess->proxy.port);
+ notify_status(sess, ne_conn_connecting, sess->proxy.hostport);
+ sess->socket = sock_connect(sess->proxy.addr, sess->proxy.port);
+ if (sess->socket == NULL) {
+ (void) set_sockerr(req, _("Could not connect to proxy server"), SOCK_ERROR);
+ return NE_CONNECT;
+ }
+
+ notify_status(sess, ne_conn_connected, sess->proxy.hostport);
+
+ if (sess->progress_cb) {
+ sock_register_progress(sess->socket, sess->progress_cb,
+ sess->progress_ud);
+ }
+ sess->connected = 1;
+ /* FALL-THROUGH */
+ case 1:
+ if (sess->use_secure && !sess->in_connect) {
+ int ret;
+ ret = proxy_tunnel(sess);
+ if (ret != NE_OK) {
+ ne_close_connection(sess);
+ return ret;
+ }
+ if (sock_make_secure(sess->socket, sess->ssl_context)) {
+ (void) set_sockerr(req, _("Could not negotiate SSL session"), SOCK_ERROR);
+ ne_close_connection(sess);
+ return NE_CONNECT;
+ }
+ sess->connected = 2;
+ notify_status(sess, ne_conn_secure,
+ sock_get_version(sess->socket));
+ } else {
+ break;
+ }
+ break;
+ default:
+ /* We've got everything we need */
+ break;
+ }
+ } else if (sess->connected == 0) {
+
+ NE_DEBUG(NE_DBG_SOCKET, "Connecting to server at %s:%d...\n",
+ sess->server.hostname, sess->server.port);
+
+ notify_status(sess, ne_conn_connecting, sess->server.hostport);
+ sess->socket = sock_connect(sess->server.addr, sess->server.port);
+
+ if (sess->socket == NULL) {
+ (void) set_sockerr(req, _("Could not connect to server"), -1);
+ return NE_CONNECT;
+ }
+
+ notify_status(sess, ne_conn_connected, sess->server.hostport);
+
+ if (sess->progress_cb) {
+ sock_register_progress(sess->socket, sess->progress_cb,
+ sess->progress_ud);
+ }
+
+ if (sess->use_secure) {
+ NE_DEBUG(NE_DBG_SOCKET, "Starting SSL...\n");
+ if (sock_make_secure(sess->socket, sess->ssl_context)) {
+ (void) set_sockerr(req, _("Could not negotiate SSL session"), SOCK_ERROR);
+ ne_close_connection(sess);
+ return NE_CONNECT;
+ }
+
+ notify_status(sess, ne_conn_secure, sock_get_version(sess->socket));
+
+ }
+
+ sess->connected = 1;
+ }
+ return NE_OK;
+}
diff --git a/neon/src/ne_request.h b/neon/src/ne_request.h
new file mode 100644
index 000000000..336355fcf
--- /dev/null
+++ b/neon/src/ne_request.h
@@ -0,0 +1,271 @@
+/*
+ HTTP Request Handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_REQUEST_H
+#define NE_REQUEST_H
+
+#include "ne_utils.h" /* For ne_status */
+#include "ne_string.h" /* For sbuffer */
+#include "ne_session.h"
+
+BEGIN_NEON_DECLS
+
+#define NE_OK (0)
+#define NE_ERROR (1) /* Generic error; use ne_get_error(session) for message */
+#define NE_LOOKUP (3) /* Name lookup failed */
+#define NE_AUTH (4) /* User authentication failed on server */
+#define NE_AUTHPROXY (5) /* User authentication failed on proxy */
+#define NE_SERVERAUTH (6) /* Server authentication failed */
+#define NE_PROXYAUTH (7) /* Proxy authentication failed */
+#define NE_CONNECT (8) /* Could not connect to server */
+#define NE_TIMEOUT (9) /* Connection timed out */
+#define NE_FAILED (10) /* The precondition failed */
+#define NE_RETRY (11) /* Retry request (ne_end_request ONLY) */
+#define NE_REDIRECT (12) /* See ne_redirect.h */
+
+#define EOL "\r\n"
+
+typedef struct ne_request_s ne_request;
+
+/***** Request Handling *****/
+
+/* Create a new request, with given method and URI.
+ * Returns request pointer. */
+ne_request *
+ne_request_create(ne_session *sess, const char *method, const char *uri);
+
+/* 'buffer' will be sent as the request body with given request. */
+void ne_set_request_body_buffer(ne_request *req, const char *buffer,
+ size_t size);
+
+/* Contents of stream will be sent as the request body with the given
+ * request. Returns:
+ * 0 on okay.
+ * non-zero if could not determine length of file.
+ */
+int ne_set_request_body_fd(ne_request *req, int fd);
+
+/* Callback for providing request body blocks.
+ *
+ * Before each time the body is provided, the callback will be called
+ * once with buflen == 0. The body may have to be provided >1 time
+ * per request (for authentication retries etc.).
+ *
+ * The callback must return:
+ * <0 : error, abort.
+ * 0 : ignore 'buffer' contents, end of body.
+ * 0 < x <= buflen : buffer contains x bytes of body data.
+ */
+typedef ssize_t (*ne_provide_body)(void *userdata,
+ char *buffer, size_t buflen);
+
+/* Callback is called to provide blocks of request body. */
+void ne_set_request_body_provider(ne_request *req, size_t size,
+ ne_provide_body provider, void *userdata);
+
+/* Handling response bodies... you provide TWO callbacks:
+ *
+ * 1) 'acceptance' callback: determines whether you want to handle the
+ * response body given the response-status information, e.g., if you
+ * only want 2xx responses, say so here.
+ *
+ * 2) 'reader' callback: passed blocks of the response-body as they
+ * arrive, if the acceptance callback returned non-zero. */
+
+/* 'acceptance' callback type. Return non-zero to accept the response,
+ * else zero to ignore it. */
+typedef int (*ne_accept_response)(
+ void *userdata, ne_request *req, ne_status *st);
+
+/* An 'acceptance' callback which only accepts 2xx-class responses.
+ * Ignores userdata. */
+int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st);
+
+/* An acceptance callback which accepts all responses. Ignores
+ * userdata. */
+int ne_accept_always(void *userdata, ne_request *req, ne_status *st);
+
+/* The 'reader' callback type */
+typedef void (*ne_block_reader)(
+ void *userdata, const char *buf, size_t len);
+
+/* Add a response reader for the given request, with the given
+ * acceptance function. userdata is passed as the first argument to
+ * the acceptance + reader callbacks.
+ *
+ * The acceptance callback is called once each time the request is
+ * sent: it may be sent >1 time because of authentication retries etc.
+ * For each time the acceptance callback is called, if it returns
+ * non-zero, blocks of the response body will be passed to the reader
+ * callback as the response is read. After all the response body has
+ * been read, the callback will be called with a 'len' argument of
+ * zero.
+ */
+void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
+ ne_block_reader rdr, void *userdata);
+
+/* Handle response headers. Each handler is associated with a specific
+ * header field (indicated by name). The handler is then passed the
+ * value of this header field. */
+
+/* The header handler callback type */
+typedef void (*ne_header_handler)(void *userdata, const char *value);
+
+/* Adds a response header handler for the given request. userdata is passed
+ * as the first argument to the header handler, and the 'value' is the
+ * header field value (i.e. doesn't include the "Header-Name: " part").
+ */
+void ne_add_response_header_handler(ne_request *req, const char *name,
+ ne_header_handler hdl, void *userdata);
+
+/* Add handler which is passed ALL header values regardless of name */
+void ne_add_response_header_catcher(ne_request *req,
+ ne_header_handler hdl, void *userdata);
+
+/* Stock header handlers:
+ * 'duplicate': *(char **)userdata = strdup(value)
+ * 'numeric': *(int *)userdata = atoi(value)
+ * e.g.
+ * int mynum;
+ * ne_add_response_header_handler(myreq, "Content-Length",
+ * ne_handle_numeric_handler, &mynum);
+ * ... arranges mynum to be set to the value of the Content-Length header.
+ */
+void ne_duplicate_header(void *userdata, const char *value);
+void ne_handle_numeric_header(void *userdata, const char *value);
+
+/* Adds a header to the request with given name and value. */
+void ne_add_request_header(ne_request *req, const char *name,
+ const char *value);
+/* Adds a header to the request with given name, using printf-like
+ * format arguments for the value. */
+void ne_print_request_header(ne_request *req, const char *name,
+ const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 3, 4)))
+#endif /* __GNUC__ */
+;
+
+/* ne_request_dispatch: Sends the given request, and reads the
+ * response. Response-Status information can be retrieve with
+ * ne_get_status(req).
+ *
+ * Returns:
+ * NE_OK if request sent + response read okay.
+ * NE_AUTH if user authentication failed on origin server
+ * NE_AUTHPROXY if user authentication failed on proxy server
+ * NE_SERVERAUTH server authentication failed
+ * NE_PROXYAUTH proxy authentication failed
+ * NE_CONNECT could not connect to server/proxy server
+ * NE_TIMEOUT connection timed out mid-request
+ * NE_ERROR for other errors, and ne_get_error() should
+ * return a meaningful error string
+ *
+ * NB: NE_AUTH and NE_AUTHPROXY mean that the USER supplied the
+ * wrong username/password. SERVER/PROXYAUTH mean that, after the
+ * server has accepted a valid username/password, the server/proxy did
+ * not authenticate the response message correctly.
+ * */
+int ne_request_dispatch(ne_request *req);
+
+/* Returns a pointer to the response status information for the
+ * given request. */
+const ne_status *ne_get_status(ne_request *req)
+
+/* Declare this with attribute const, since we often call it >1 times
+ * with the same argument, and it will return the same thing each
+ * time. This lets the compiler optimize away any subsequent calls
+ * (theoretically). */
+#ifdef __GNUC__
+ __attribute__ ((const))
+#endif /* __GNUC__ */
+ ;
+
+/* Destroy memory associated with request pointer */
+void ne_request_destroy(ne_request *req);
+
+/* "Caller-pulls" request interface. This is an ALTERNATIVE interface
+ * to ne_request_dispatch: either use that, or do all this yourself:
+ *
+ * caller must call:
+ * 1. ne_begin_request (fail if returns non-NE_OK)
+ * 2. while(ne_read_response_block(...) > 0) ... loop ...;
+ * 3. ne_end_request
+ * If ne_end_request returns NE_RETRY, MUST go back to 1.
+ */
+int ne_begin_request(ne_request *req);
+int ne_end_request(ne_request *req);
+
+/* Read a block of the response. buffer must be at least 128 bytes.
+ * 'buflen' must be length of buffer.
+ *
+ * Returns:
+ * <0 - error, stop reading.
+ * 0 - end of response
+ * >0 - number of bytes read into buffer.
+ */
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);
+
+/**** Request hooks handling *****/
+
+typedef struct {
+ /* A slight hack? Allows access to the hook private information,
+ * externally... */
+ const char *id; /* id could be a URI to be globally unique */
+ /* e.g. "http://webdav.org/neon/hooks/davlock" */
+
+ /* Register a new request: return value passed to subsequent
+ * handlers as '_private'. Session cookie passed as 'session'. */
+ void *(*create)(void *cookie, ne_request *req,
+ const char *method, const char *uri);
+ /* Just before sending the request: let them add headers if they want */
+ void (*pre_send)(void *_private, ne_buffer *request);
+ /* After sending the request. May return:
+ * NE_OK everything is okay
+ * NE_RETRY try sending the request again.
+ * otherwise: request fails. Returned to _dispatch caller.
+ */
+ int (*post_send)(void *_private, const ne_status *status);
+ /* Clean up after yourself. */
+ void (*destroy)(void *_private);
+} ne_request_hooks;
+
+typedef void (*ne_free_hooks)(void *cookie);
+
+/* Add in hooks.
+ * 'cookie' will be passed to each call to the 'create' handler of the hooks.
+ * If 'free_hooks' is non-NULL, it will called when the session is destroyed,
+ * to free any data associated with 'cookie'.
+ */
+void ne_add_hooks(ne_session *sess,
+ const ne_request_hooks *hooks, void *cookie,
+ ne_free_hooks free_cookie);
+
+/* Return private data for a new request */
+void *ne_request_hook_private(ne_request *req, const char *id);
+
+/* Return private data for the session */
+void *ne_session_hook_private(ne_session *sess, const char *id);
+
+END_NEON_DECLS
+
+#endif /* NE_REQUEST_H */
+
diff --git a/neon/src/ne_session.c b/neon/src/ne_session.c
new file mode 100644
index 000000000..9f730d5ac
--- /dev/null
+++ b/neon/src/ne_session.c
@@ -0,0 +1,264 @@
+/*
+ HTTP session handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_session.h"
+#include "ne_alloc.h"
+#include "ne_utils.h"
+
+#include "ne_private.h"
+
+#define NEON_USERAGENT "neon/" NEON_VERSION;
+
+/* Initializes an HTTP session */
+ne_session *ne_session_create(void)
+{
+ ne_session *sess = ne_calloc(sizeof *sess);
+
+ NE_DEBUG(NE_DBG_HTTP, "HTTP session begins.\n");
+ strcpy(sess->error, "Unknown error.");
+ sess->version_major = -1;
+ sess->version_minor = -1;
+ /* Default expect-100 to OFF. */
+ sess->expect100_works = -1;
+ return sess;
+}
+
+int ne_session_destroy(ne_session *sess)
+{
+ struct hook *hk;
+
+ NE_DEBUG(NE_DBG_HTTP, "ne_session_destroy called.\n");
+
+ /* Clear the hooks. */
+ hk = sess->hooks;
+ while (hk) {
+ struct hook *nexthk = hk->next;
+ if (hk->free) {
+ hk->free(hk->private);
+ }
+ free(hk);
+ hk = nexthk;
+ }
+
+ NE_FREE(sess->server.hostname);
+ NE_FREE(sess->server.hostport);
+ NE_FREE(sess->proxy.hostport);
+ NE_FREE(sess->user_agent);
+
+ if (sess->connected) {
+ ne_close_connection(sess);
+ }
+
+ free(sess);
+ return NE_OK;
+}
+
+int ne_version_pre_http11(ne_session *s)
+{
+ return (s->version_major<1 || (s->version_major==1 && s->version_minor<1));
+}
+
+static char *get_hostport(struct host_info *host)
+{
+ size_t len = strlen(host->hostname);
+ char *ret = ne_malloc(len + 10);
+ strcpy(ret, host->hostname);
+ if (host->port != 80) {
+ snprintf(ret + len, 9, ":%d", host->port);
+ }
+ return ret;
+}
+
+static void
+set_hostinfo(struct host_info *info, const char *hostname, int port)
+{
+ NE_FREE(info->hostport);
+ NE_FREE(info->hostname);
+ info->hostname= ne_strdup(hostname);
+ info->port = port;
+ info->hostport = get_hostport(info);
+}
+
+static int lookup_host(ne_session *sess, struct host_info *info)
+{
+ if (sess->notify_cb) {
+ sess->notify_cb(sess->notify_ud, ne_conn_namelookup, info->hostname);
+ }
+ if (sock_name_lookup(info->hostname, &info->addr)) {
+ return NE_LOOKUP;
+ } else {
+ return NE_OK;
+ }
+}
+
+int ne_session_server(ne_session *sess, const char *hostname, int port)
+{
+ if (sess->connected && !sess->have_proxy) {
+ /* Force reconnect */
+ ne_close_connection(sess);
+ }
+ set_hostinfo(&sess->server, hostname, port);
+ /* We do a name lookup on the origin server if either:
+ * 1) we do not have a proxy server
+ * 2) we *might not* have a proxy server (since we have a 'proxy decider' function).
+ */
+ if (!sess->have_proxy || sess->proxy_decider) {
+ return lookup_host(sess, &sess->server);
+ } else {
+ return NE_OK;
+ }
+}
+
+void ne_set_secure_context(ne_session *sess, nssl_context *ctx)
+{
+ sess->ssl_context = ctx;
+}
+
+int ne_set_request_secure_upgrade(ne_session *sess, int req_upgrade)
+{
+#ifdef ENABLE_SSL
+ sess->request_secure_upgrade = req_upgrade;
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+int ne_set_accept_secure_upgrade(ne_session *sess, int acc_upgrade)
+{
+#ifdef ENABLE_SSL
+ sess->accept_secure_upgrade = acc_upgrade;
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+int ne_set_secure(ne_session *sess, int use_secure)
+{
+#ifdef ENABLE_SSL
+ sess->use_secure = use_secure;
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+void ne_session_decide_proxy(ne_session *sess, ne_use_proxy use_proxy,
+ void *userdata)
+{
+ sess->proxy_decider = use_proxy;
+ sess->proxy_decider_udata = userdata;
+}
+
+int ne_session_proxy(ne_session *sess, const char *hostname, int port)
+{
+ if (sess->connected) {
+ /* Force reconnect */
+ ne_close_connection(sess);
+ }
+ sess->have_proxy = 1;
+ set_hostinfo(&sess->proxy, hostname, port);
+ return lookup_host(sess, &sess->proxy);
+}
+
+void ne_set_error(ne_session *sess, const char *errstring)
+{
+ strncpy(sess->error, errstring, BUFSIZ);
+ sess->error[BUFSIZ-1] = '\0';
+ STRIP_EOL(sess->error);
+}
+
+
+void ne_set_progress(ne_session *sess,
+ sock_progress progress, void *userdata)
+{
+ sess->progress_cb = progress;
+ sess->progress_ud = userdata;
+}
+
+void ne_set_status(ne_session *sess,
+ ne_notify_status status, void *userdata)
+{
+ sess->notify_cb = status;
+ sess->notify_ud = userdata;
+}
+
+void ne_set_expect100(ne_session *sess, int use_expect100)
+{
+ if (use_expect100) {
+ sess->expect100_works = 1;
+ } else {
+ sess->expect100_works = -1;
+ }
+}
+
+void ne_set_persist(ne_session *sess, int persist)
+{
+ sess->no_persist = !persist;
+}
+
+void ne_set_useragent(ne_session *sess, const char *token)
+{
+ static const char *fixed = " " NEON_USERAGENT;
+ NE_FREE(sess->user_agent);
+ CONCAT2(sess->user_agent, token, fixed);
+}
+
+const char *ne_get_server_hostport(ne_session *sess) {
+ return sess->server.hostport;
+}
+
+const char *ne_get_scheme(ne_session *sess)
+{
+ if (sess->use_secure) {
+ return "https";
+ } else {
+ return "http";
+ }
+}
+
+
+const char *ne_get_error(ne_session *sess) {
+ return sess->error;
+}
+
+int ne_close_connection(ne_session *sess)
+{
+ NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
+ if (sess->connected > 0) {
+ sock_close(sess->socket);
+ sess->socket = NULL;
+ }
+ sess->connected = 0;
+ NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
+ return 0;
+}
+
diff --git a/neon/src/ne_session.h b/neon/src/ne_session.h
new file mode 100644
index 000000000..7397349a6
--- /dev/null
+++ b/neon/src/ne_session.h
@@ -0,0 +1,194 @@
+/*
+ HTTP session handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_SESSION_H
+#define NE_SESSION_H 1
+
+#include "ne_socket.h" /* for nssl_context */
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+typedef struct ne_session_s ne_session;
+
+/*
+ * Session handling:
+ * Call order:
+ * 0. sock_init MANDATORY
+ * 1. ne_session_create MANDATORY
+ * 2. ne_session_proxy OPTIONAL
+ * 3. ne_session_server MANDATORY
+ * 4. ne_set_* OPTIONAL
+ * ... --- Any HTTP request method ---
+ * n. ne_session_destroy MANDATORY
+ */
+
+/* Create a new HTTP session */
+ne_session *ne_session_create(void);
+
+/* Finish an HTTP session */
+int ne_session_destroy(ne_session *sess);
+
+/* Prematurely force the connection to be closed for the given
+ * session. */
+int ne_close_connection(ne_session *sess);
+
+/* Set the server or proxy server to be used for the session.
+ * Returns:
+ * NE_LOOKUP if the DNS lookup for hostname failed.
+ * NE_OK otherwise.
+ *
+ * Note that if a proxy is being used, ne_session_proxy should be
+ * called BEFORE ne_session_server, so the DNS lookup can be skipped
+ * on the server. */
+int ne_session_server(ne_session *sess, const char *hostname, int port);
+int ne_session_proxy(ne_session *sess, const char *hostname, int port);
+
+/* Callback to determine whether the proxy server should be used or
+ * not for a request to the given hostname using the given scheme.
+ * Scheme will be "http" or "https" etc.
+ * Must return:
+ * Zero to indicate the proxy should NOT be used
+ * Non-zero to indicate the proxy SHOULD be used.
+ */
+typedef int (*ne_use_proxy)(void *userdata,
+ const char *scheme, const char *hostname);
+
+/* Register the callback for determining whether the proxy server
+ * should be used or not here. 'userdata' will be passed as the first
+ * argument to the callback. The callback is only called if a proxy
+ * server has been set up using ne_session_proxy.
+ *
+ * This function MUST be called before calling ne_session_server.
+ */
+void ne_session_decide_proxy(ne_session *sess, ne_use_proxy use_proxy,
+ void *userdata);
+
+/* Set protocol options for session:
+ * expect100: Defaults to OFF
+ * persist: Defaults to ON
+ *
+ * expect100: When set, send the "Expect: 100-continue" request header
+ * with requests with bodies.
+ *
+ * persist: When set, use a persistent connection. (Generally,
+ * you don't want to turn this off.)
+ * */
+void ne_set_expect100(ne_session *sess, int use_expect100);
+void ne_set_persist(ne_session *sess, int persist);
+
+/* Set a progress callback for the session. */
+void ne_set_progress(ne_session *sess,
+ sock_progress progress, void *userdata);
+
+typedef enum {
+ ne_conn_namelookup, /* lookup up hostname (info = hostname) */
+ ne_conn_connecting, /* connecting to host (info = hostname) */
+ ne_conn_connected, /* connected to host (info = hostname) */
+ ne_conn_secure /* connection now secure (info = crypto level) */
+} ne_conn_status;
+
+typedef void (*ne_notify_status)(void *userdata,
+ ne_conn_status status,
+ const char *info);
+
+/* Set a status notification callback for the session, to report
+ * connection status. */
+void ne_set_status(ne_session *sess,
+ ne_notify_status status, void *userdata);
+
+/* Using SSL/TLS connections:
+ *
+ * Session settings:
+ * secure: Defaults to OFF
+ * secure_context: No callbacks, any protocol allowed.
+ * request_secure_upgrade: Defaults to OFF
+ * accept_secure_ugprade: Defaults to OFF
+ *
+ * secure_context: When set, specifies the settings to use for secure
+ * connections, and any callbacks (see nsocket.h). The lifetime of
+ * the context object must be >= to the lifetime of the session
+ * object.
+ *
+ * secure: When set, use a secure (SSL/TLS) connection to the origin
+ * server. This will tunnel (using CONNECT) through the proxy server
+ * if one is being used.
+ *
+ * NB: ne_set_scure will return -1 if SSL is not supported by the
+ * library (i.e., it was not built against OpenSSL), or 0 if it is.
+ * */
+void ne_set_secure_context(ne_session *sess, nssl_context *ctx);
+int ne_set_secure(ne_session *sess, int secure);
+
+/*
+ * NOTE: don't bother using the two _secure_update functions yet.
+ * "secure upgrades" (RFC2817) are not currently supported by any HTTP
+ * servers.
+ *
+ * request_secure_upgrade: When set, notify the server with each
+ * request made that an upgrade to a secure connection is desired, if
+ * available.
+ *
+ * accept_secure_upgrade: When set, allow a server-requested upgrade
+ * to a secure connection.
+ *
+ * These return as per ne_set_secure. */
+int ne_set_request_secure_upgrade(ne_session *sess, int req_upgrade);
+int ne_set_accept_secure_upgrade(ne_session *sess, int acc_upgrade);
+
+/* Sets the user-agent string. neon/VERSION will be appended, to make
+ * the full header "User-Agent: product neon/VERSION".
+ * If this function is not called, the User-Agent header is not sent.
+ * The product string must follow the RFC2616 format, i.e.
+ * product = token ["/" product-version]
+ * product-version = token
+ * where token is any alpha-numeric-y string [a-zA-Z0-9]*
+ */
+void ne_set_useragent(ne_session *sess, const char *product);
+
+/* Determine if next-hop server claims HTTP/1.1 compliance. Returns:
+ * 0 if next-hop server does NOT claim HTTP/1.1 compliance
+ * non-zero if next-hop server DOES claim HTTP/1.1 compliance
+ * Not that the "next-hop" server is the proxy server if one is being
+ * used, otherwise, the origin server.
+ */
+int ne_version_pre_http11(ne_session *sess);
+
+/* Returns the 'hostport' URI segment for the end-server, e.g.
+ * "my.server.com:8080" or "www.server.com"
+ * (port segment is ommitted if == 80)
+ */
+const char *ne_get_server_hostport(ne_session *sess);
+
+/* Returns the URL scheme being used for the current session.
+ * Does NOT include a trailing ':'.
+ * Example returns: "http" or "https".
+ */
+const char *ne_get_scheme(ne_session *sess);
+
+/* Set the error string for the session */
+void ne_set_error(ne_session *sess, const char *errstring);
+/* Retrieve the error string for the session */
+const char *ne_get_error(ne_session *sess);
+
+END_NEON_DECLS
+
+#endif /* NE_SESSION_H */
diff --git a/neon/src/ne_socket.c b/neon/src/ne_socket.c
new file mode 100644
index 000000000..1ab4c1e2b
--- /dev/null
+++ b/neon/src/ne_socket.c
@@ -0,0 +1,924 @@
+/*
+ socket handling routines
+ Copyright (C) 1998-2001, Joe Orton <joe@light.plus.com>,
+ except where otherwise indicated.
+
+ Portions are:
+ Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+ Originally under GPL in Mutt, http://www.mutt.org/
+ Relicensed under LGPL for neon, http://www.webdav.org/neon/
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ The sock_readline() function is:
+
+ Copyright (c) 1999 Eric S. Raymond
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef WIN32
+#include <WinSock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif /* WIN32 */
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <errno.h>
+
+#include <fcntl.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+/* SOCKS support. */
+#ifdef HAVE_SOCKS_H
+#include <socks.h>
+#endif
+
+#include "ne_i18n.h"
+#include "ne_utils.h"
+#include "ne_string.h"
+#include "ne_socket.h"
+#include "ne_alloc.h"
+
+#if defined(BEOS_PRE_BONE)
+#define NEON_WRITE(a,b,c) send(a,b,c,0)
+#define NEON_READ(a,b,c) recv(a,b,c,0)
+#define NEON_CLOSE(s) closesocket(s)
+#elif defined(WIN32)
+#define NEON_WRITE(a,b,c) send(a,b,c,0)
+#define NEON_READ(a,b,c) recv(a,b,c,0)
+#define NEON_CLOSE(s) closesocket(s)
+#else /* really Unix! */
+#define NEON_WRITE(a,b,c) write(a,b,c)
+#define NEON_READ(a,b,c) read(a,b,c)
+#define NEON_CLOSE(s) close(s)
+#endif
+
+#ifdef ENABLE_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+/* Whilst the OpenSSL interface *looks* like it is not thread-safe, it
+ * appears to do horrible gymnastics to maintain per-thread global
+ * variables for error reporting. UGH! */
+#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))
+
+#endif
+
+/* BeOS doesn't have fd==sockets on anything pre-bone, so see if
+ * we need to drop back to our old ways...
+ */
+#ifdef __BEOS__
+ #ifndef BONE_VERSION /* if we have BONE this isn't an issue... */
+ #define BEOS_PRE_BONE
+ #endif
+#endif
+
+struct nsocket_s {
+ int fd;
+ const char *error; /* Store error string here */
+ sock_progress progress_cb;
+ void *progress_ud;
+#ifdef ENABLE_SSL
+ SSL *ssl;
+ SSL_CTX *default_ctx;
+#endif
+#ifdef BEOS_PRE_BONE
+#define MAX_PEEK_BUFFER 1024
+ char peeked_bytes[MAX_PEEK_BUFFER];
+ char *peeked_bytes_curpos;
+ int peeked_bytes_avail;
+#endif
+};
+
+struct nssl_context_s {
+#ifdef ENABLE_SSL
+ SSL_CTX *ctx;
+#endif
+ nssl_accept cert_accept;
+ void *accept_ud; /* userdata for callback */
+
+ /* private key prompt callback */
+ nssl_key_prompt key_prompt;
+ void *key_userdata;
+ const char *key_file;
+};
+
+void sock_register_progress(nsocket *sock, sock_progress cb, void *userdata)
+{
+ sock->progress_cb = cb;
+ sock->progress_ud = userdata;
+}
+
+void sock_call_progress(nsocket *sock, off_t progress, off_t total)
+{
+ if (sock->progress_cb) {
+ sock->progress_cb(sock->progress_ud, progress, total);
+ }
+}
+
+int sock_init(void)
+{
+#ifdef WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD(2, 2);
+
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0)
+ return -1;
+
+#endif
+
+#ifdef ENABLE_SSL
+ SSL_load_error_strings();
+ SSL_library_init();
+
+ NE_DEBUG(NE_DBG_SOCKET, "Initialized SSL.\n");
+#endif
+
+ return 0;
+}
+
+void sock_exit(void)
+{
+#ifdef WIN32
+ WSACleanup();
+#endif
+}
+
+/* sock_read is read() with a timeout of SOCKET_READ_TIMEOUT. */
+int sock_read(nsocket *sock, char *buffer, size_t count)
+{
+ int ret;
+
+ if (count == 0) {
+ NE_DEBUG(NE_DBG_SOCKET, "Passing 0 to sock_read is probably bad.\n");
+ /* But follow normal read() semantics anyway... */
+ return 0;
+ }
+
+ ret = sock_block(sock, SOCKET_READ_TIMEOUT);
+ if (ret == 0) {
+ /* Got data */
+ do {
+#ifdef ENABLE_SSL
+ if (sock->ssl) {
+ ret = SSL_read(sock->ssl, buffer, count);
+ } else {
+#endif
+#ifndef BEOS_PRE_BONE
+ ret = NEON_READ(sock->fd, buffer, count);
+#else
+ if (sock->peeked_bytes_avail > 0) {
+ /* simply return the peeked bytes.... */
+ if (count >= sock->peeked_bytes_avail){
+ /* we have room */
+ strncpy(buffer, sock->peeked_bytes_curpos,
+ sock->peeked_bytes_avail);
+ ret = sock->peeked_bytes_avail;
+ sock->peeked_bytes_avail = 0;
+ } else {
+ strncpy(buffer, sock->peeked_bytes_curpos, count);
+ sock->peeked_bytes_curpos += count;
+ sock->peeked_bytes_avail -= count;
+ ret = count;
+ }
+ } else {
+ ret = recv(sock->fd, buffer, count, 0);
+ }
+#endif
+#ifdef ENABLE_SSL
+ }
+#endif
+ } while (ret == -1 && errno == EINTR);
+ if (ret < 0) {
+ sock->error = strerror(errno);
+ ret = SOCK_ERROR;
+ } else if (ret == 0) {
+ /* This might not or might not be an error depending on
+ the context. */
+ sock->error = _("Connection was closed by server");
+ NE_DEBUG(NE_DBG_SOCKET, "read returned zero.\n");
+ ret = SOCK_CLOSED;
+ }
+ }
+ return ret;
+}
+
+/* sock_peek is recv(...,MSG_PEEK) with a timeout of SOCKET_TIMEOUT.
+ * Returns length of data read or SOCK_* on error */
+int sock_peek(nsocket *sock, char *buffer, size_t count)
+{
+ int ret;
+ ret = sock_block(sock, SOCKET_READ_TIMEOUT);
+ if (ret < 0) {
+ return ret;
+ }
+ /* Got data */
+#ifdef ENABLE_SSL
+ if (sock->ssl) {
+ ret = SSL_peek(sock->ssl, buffer, count);
+ /* TODO: This is the fetchmail fix as in sock_readline.
+ * Do we really need it? */
+ if (ret == 0) {
+ if (sock->ssl->shutdown) {
+ return SOCK_CLOSED;
+ }
+ if (0 != ERR_get_error()) {
+ sock->error = ERROR_SSL_STRING;
+ return SOCK_ERROR;
+ }
+ }
+ } else {
+#endif
+ do {
+#ifndef BEOS_PRE_BONE
+ ret = recv(sock->fd, buffer, count, MSG_PEEK);
+#else /* we're on BeOS pre-BONE so we need to use the buffer... */
+ if (sock->peeked_bytes_avail > 0) {
+ /* we've got some from last time!!! */
+ if (count >= sock->peeked_bytes_avail) {
+ strncpy(buffer, sock->peeked_bytes_curpos, sock->peeked_bytes_avail);
+ ret = sock->peeked_bytes_avail;
+ } else {
+ strncpy(buffer, sock->peeked_bytes_curpos, count);
+ ret = count;
+ }
+ } else {
+ if (count > MAX_PEEK_BUFFER)
+ count = MAX_PEEK_BUFFER;
+ ret = recv(sock->fd, buffer, count, 0);
+ sock->peeked_bytes_avail = ret;
+ strncpy(sock->peeked_bytes, buffer, ret);
+ sock->peeked_bytes_curpos = sock->peeked_bytes;
+ }
+#endif
+ } while (ret == -1 && errno == EINTR);
+#ifdef ENABLE_SSL
+ }
+#endif
+ /* According to the Single Unix Spec, recv() will return
+ * zero if the socket has been closed the other end. */
+ if (ret == 0) {
+ ret = SOCK_CLOSED;
+ } else if (ret < 0) {
+ sock->error = strerror(errno);
+ ret = SOCK_ERROR;
+ }
+ return ret;
+}
+
+/* Blocks waiting for read input on the given socket for the given time.
+ * Returns:
+ * 0 if data arrived
+ * SOCK_TIMEOUT if data did not arrive before timeout
+ * SOCK_ERROR on error
+ */
+int sock_block(nsocket *sock, int timeout)
+{
+ struct timeval tv;
+ fd_set fds;
+ int ret;
+
+#ifdef ENABLE_SSL
+ if (sock->ssl) {
+ /* There may be data already available in the
+ * SSL buffers */
+ if (SSL_pending(sock->ssl)) {
+ return 0;
+ }
+ /* Otherwise, we should be able to select on
+ * the socket as per normal. Probably? */
+ }
+#endif
+#ifdef BEOS_PRE_BONE
+ if (sock->peeked_bytes_avail > 0) {
+ return 0;
+ }
+#endif
+
+ /* Init the fd set */
+ FD_ZERO(&fds);
+ FD_SET(sock->fd, &fds);
+ /* Set the timeout */
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ do {
+ ret = select(sock->fd+1, &fds, NULL, NULL, &tv);
+ } while (ret == -1 && errno == EINTR);
+
+ switch(ret) {
+ case 0:
+ return SOCK_TIMEOUT;
+ case -1:
+ sock->error = strerror(errno);
+ return SOCK_ERROR;
+ default:
+ return 0;
+ }
+}
+
+/* Send the given line down the socket with CRLF appended.
+ * Returns 0 on success or SOCK_* on failure. */
+int sock_sendline(nsocket *sock, const char *line)
+{
+ char *buffer;
+ int ret;
+ CONCAT2(buffer, line, "\r\n");
+ ret = sock_send_string(sock, buffer);
+ free(buffer);
+ return ret;
+}
+
+int sock_readfile_blocked(nsocket *sock, off_t length,
+ sock_block_reader reader, void *userdata)
+{
+ char buffer[BUFSIZ];
+ int ret;
+ off_t done = 0;
+ do {
+ ret = sock_read(sock, buffer, BUFSIZ);
+ if (ret < 0) {
+ if (length == -1 && ret == SOCK_CLOSED) {
+ /* Not an error condition. */
+ return 0;
+ }
+ return ret;
+ }
+ done += ret;
+ sock_call_progress(sock, done, length);
+ (*reader)(userdata, buffer, ret);
+ } while ((length == -1) || (done < length));
+ return 0;
+}
+
+
+/* Send a block of data down the given fd.
+ * Returns 0 on success or SOCK_* on failure */
+int sock_fullwrite(nsocket *sock, const char *data, size_t length)
+{
+ ssize_t wrote;
+
+#ifdef ENABLE_SSL
+ if (sock->ssl) {
+ /* joe: ssl.h says SSL_MODE_ENABLE_PARTIAL_WRITE must
+ * be enabled to have SSL_write return < length...
+ * so, SSL_write should never return < length. */
+ wrote = SSL_write(sock->ssl, data, length);
+ if (wrote >= 0 && (size_t)wrote < length) {
+ NE_DEBUG(NE_DBG_SOCKET, "SSL_write returned less than length!\n");
+ sock->error = ERROR_SSL_STRING;
+ return SOCK_ERROR;
+ }
+ } else {
+#endif
+ const char *pnt = data;
+ size_t sent = 0;
+
+ while (sent < length) {
+ wrote = NEON_WRITE(sock->fd, pnt, length-sent);
+ if (wrote < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EPIPE) {
+ return SOCK_CLOSED;
+ } else {
+ sock->error = strerror(errno);
+ return SOCK_ERROR;
+ }
+ }
+ sent += wrote;
+ pnt += wrote;
+#ifdef ENABLE_SSL
+ }
+#endif
+ }
+ return 0;
+}
+
+/* Sends the given string down the given socket.
+ * Returns 0 on success or -1 on failure. */
+int sock_send_string(nsocket *sock, const char *data)
+{
+ return sock_fullwrite(sock, data, strlen(data));
+}
+
+/* This is from from Eric Raymond's fetchmail (SockRead() in socket.c)
+ * since I wouldn't have a clue how to do it properly.
+ * This function is Copyright 1999 (C) Eric Raymond.
+ * Modifications Copyright 2000 (C) Joe Orton
+ */
+int sock_readline(nsocket *sock, char *buf, int len)
+{
+ char *newline, *bp = buf;
+ int n;
+
+ do {
+ /*
+ * The reason for these gymnastics is that we want two things:
+ * (1) to read \n-terminated lines,
+ * (2) to return the true length of data read, even if the
+ * data coming in has embedded NULS.
+ */
+#ifdef ENABLE_SSL
+
+ if (sock->ssl) {
+ /* Hack alert! */
+ /* OK... SSL_peek works a little different from MSG_PEEK
+ Problem is that SSL_peek can return 0 if there is no
+ data currently available. If, on the other hand, we
+ loose the socket, we also get a zero, but the SSL_read
+ then SEGFAULTS! To deal with this, we'll check the
+ error code any time we get a return of zero from
+ SSL_peek. If we have an error, we bail. If we don't,
+ we read one character in SSL_read and loop. This
+ should continue to work even if they later change the
+ behavior of SSL_peek to "fix" this problem... :-(*/
+ NE_DEBUG(NE_DBG_SOCKET, "SSL readline... \n");
+ if ((n = SSL_peek(sock->ssl, bp, len)) < 0) {
+ sock->error = ERROR_SSL_STRING;
+ return(-1);
+ }
+ if (0 == n) {
+ /* SSL_peek says no data... Does he mean no data
+ or did the connection blow up? If we got an error
+ then bail! */
+ NE_DEBUG(NE_DBG_SOCKET, "SSL_Peek says no data!\n");
+ /* Check properly to see if the connection has closed */
+ if (sock->ssl->shutdown) {
+ NE_DEBUG(NE_DBG_SOCKET, "SSL says shutdown.");
+ return SOCK_CLOSED;
+ } else if (0 != (n = ERR_get_error())) {
+ NE_DEBUG(NE_DBG_SOCKET, "SSL error occured.\n");
+ sock->error = ERROR_SSL_STRING;
+ return -1;
+ }
+
+ /* We didn't get an error so read at least one
+ character at this point and loop */
+ n = 1;
+ /* Make sure newline start out NULL! We don't have a
+ * string to pass through the strchr at this point yet
+ * */
+ newline = NULL;
+ } else if ((newline = memchr(bp, '\n', n)) != NULL)
+ n = newline - bp + 1;
+ n = SSL_read(sock->ssl, bp, n);
+ NE_DEBUG(NE_DBG_SOCKET, "SSL_read returned %d\n", n);
+ if (n == -1) {
+ sock->error = ERROR_SSL_STRING;
+ return(-1);
+ }
+ /* Check for case where our single character turned out to
+ * be a newline... (It wasn't going to get caught by
+ * the strchr above if it came from the hack...). */
+ if (NULL == newline && 1 == n && '\n' == *bp) {
+ /* Got our newline - this will break
+ out of the loop now */
+ newline = bp;
+ }
+ } else {
+#endif
+ if ((n = sock_peek(sock, bp, len)) <= 0)
+ return n;
+ if ((newline = memchr(bp, '\n', n)) != NULL)
+ n = newline - bp + 1;
+ if ((n = sock_read(sock, bp, n)) < 0)
+ return n;
+#ifdef ENABLE_SSL
+ }
+#endif
+ bp += n;
+ len -= n;
+ if (len < 1) {
+ sock->error = _("Line too long");
+ return SOCK_FULL;
+ }
+ } while (!newline && len);
+ *bp = '\0';
+ return bp - buf;
+}
+
+/*** End of ESR-copyrighted section ***/
+
+/* Reads readlen bytes from fd and write to sock.
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or -1 on error.
+ */
+int sock_transfer(int fd, nsocket *sock, off_t readlen)
+{
+ char buffer[BUFSIZ];
+ size_t curlen; /* total bytes yet to read from srcfd */
+ off_t sumwrlen; /* total bytes written to destfd */
+
+ if (readlen == -1) {
+ curlen = BUFSIZ; /* so the buffer size test works */
+ } else {
+ curlen = readlen; /* everything to do */
+ }
+ sumwrlen = 0; /* nowt done yet */
+
+ while (curlen > 0) {
+ int rdlen, wrlen;
+
+ /* Get a chunk... if the number of bytes that are left to read
+ * is less than the buffer size, only read that many bytes. */
+ rdlen = read(fd, buffer, (readlen==-1)?BUFSIZ:(min(BUFSIZ, curlen)));
+ sock_call_progress(sock, sumwrlen, readlen);
+ if (rdlen < 0) {
+ if (errno == EPIPE) {
+ return SOCK_CLOSED;
+ } else {
+ sock->error = strerror(errno);
+ return SOCK_ERROR;
+ }
+ } else if (rdlen == 0) {
+ /* End of file... get out of here */
+ break;
+ }
+ if (readlen != -1)
+ curlen -= rdlen;
+
+ /* Otherwise, we have bytes! Write them to destfd */
+
+ wrlen = sock_fullwrite(sock, buffer, rdlen);
+ if (wrlen < 0) {
+ return wrlen;
+ }
+
+ sumwrlen += rdlen;
+ }
+ sock_call_progress(sock, sumwrlen, readlen);
+ return sumwrlen;
+}
+
+/* Reads buflen bytes into buffer until it's full.
+ * Returns 0 on success, -1 on error */
+int sock_fullread(nsocket *sock, char *buffer, int buflen)
+{
+ char *pnt; /* current position within buffer */
+ int len;
+ pnt = buffer;
+ while (buflen > 0) {
+ len = sock_read(sock, pnt, buflen);
+ if (len < 0) return len;
+ buflen -= len;
+ pnt += len;
+ }
+ return 0;
+}
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure.
+ */
+int sock_name_lookup(const char *hostname, struct in_addr *addr)
+{
+ struct hostent *hp;
+ unsigned long laddr;
+
+ /* TODO?: a possible problem here, is that if we are passed an
+ * invalid IP address e.g. "500.500.500.500", then this gets
+ * passed to gethostbyname and returned as "Host not found".
+ * Arguably wrong, but maybe difficult to detect correctly what is
+ * an invalid IP address and what is a hostname... can hostnames
+ * begin with a numeric character? */
+ laddr = (unsigned long)inet_addr(hostname);
+ if ((int)laddr == -1) {
+ /* inet_addr failed. */
+ hp = gethostbyname(hostname);
+ if (hp == NULL) {
+#if 0
+ /* Need to get this back somehow, but we don't have
+ * an nsocket * yet... */
+ switch(h_errno) {
+ case HOST_NOT_FOUND:
+ sock->error = _("Host not found");
+ break;
+ case TRY_AGAIN:
+ sock->error = _("Host not found (try again later?)");
+ break;
+ case NO_ADDRESS:
+ sock->error = _("Host exists but has no address.");
+ break;
+ case NO_RECOVERY:
+ default:
+ sock->error = _("Non-recoverable error in resolver library.");
+ break;
+ }
+#endif
+ return SOCK_ERROR;
+ }
+ memcpy(addr, hp->h_addr, hp->h_length);
+ } else {
+ addr->s_addr = laddr;
+ }
+ return 0;
+}
+
+static nsocket *create_sock(int fd)
+{
+ nsocket *sock = ne_calloc(sizeof *sock);
+#ifdef ENABLE_SSL
+ sock->default_ctx = SSL_CTX_new(SSLv23_client_method());
+#endif
+ sock->fd = fd;
+ return sock;
+}
+
+/* Opens a socket to the given port at the given address.
+ * Returns -1 on failure, or the socket on success.
+ * portnum must be in HOST byte order */
+nsocket *sock_connect(const struct in_addr addr,
+ unsigned short int portnum)
+{
+ struct sockaddr_in sa;
+ int fd;
+
+ /* Create the socket */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return NULL;
+ /* Connect the nsocket */
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portnum); /* host -> net byte orders */
+ sa.sin_addr = addr;
+ if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+ (void) NEON_CLOSE(fd);
+ return NULL;
+ }
+ /* Success - return the nsocket */
+ return create_sock(fd);
+}
+
+nsocket *sock_accept(int listener)
+{
+ int fd = accept(listener, NULL, NULL);
+ if (fd > 0) {
+ return create_sock(fd);
+ } else {
+ return NULL;
+ }
+}
+
+int sock_get_fd(nsocket *sock)
+{
+ return sock->fd;
+}
+
+nssl_context *sock_create_ssl_context(void)
+{
+ nssl_context *ctx = ne_calloc(sizeof *ctx);
+#ifdef ENABLE_SSL
+ ctx->ctx = SSL_CTX_new(SSLv23_client_method());
+#endif
+ return ctx;
+}
+
+void sock_destroy_ssl_context(nssl_context *ctx)
+{
+#ifdef ENABLE_SSL
+ SSL_CTX_free(ctx->ctx);
+#endif
+ free(ctx);
+}
+
+#ifdef ENABLE_SSL
+void sock_disable_tlsv1(nssl_context *c)
+{
+ SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1);
+}
+void sock_disable_sslv2(nssl_context *c)
+{
+ SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv2);
+
+}
+void sock_disable_sslv3(nssl_context *c)
+{
+ SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv3);
+}
+
+/* The callback neon installs with OpenSSL for giving the private key
+ * prompt. FIXME: WTH is 'rwflag'? */
+static int key_prompt_cb(char *buf, int len, int rwflag, void *userdata)
+{
+ nssl_context *ctx = userdata;
+ int ret;
+ ret = ctx->key_prompt(ctx->key_userdata, ctx->key_file, buf, len);
+ if (ret)
+ return -1;
+ /* Obscurely OpenSSL requires the callback to return the length of
+ * the password, this seems a bit weird so we don't expose this in
+ * the neon API. */
+ return strlen(buf);
+}
+
+void sock_set_key_prompt(nssl_context *ctx,
+ nssl_key_prompt prompt, void *userdata)
+{
+ SSL_CTX_set_default_passwd_cb(ctx->ctx, key_prompt_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(ctx->ctx, ctx);
+ ctx->key_prompt = prompt;
+ ctx->key_userdata = userdata;
+}
+
+#else
+void sock_disable_tlsv1(nssl_context *c) {}
+void sock_disable_sslv2(nssl_context *c) {}
+void sock_disable_sslv3(nssl_context *c) {}
+void sock_set_key_prompt(nssl_context *c, nssl_key_prompt p, void *u) {}
+#endif
+
+int sock_make_secure(nsocket *sock, nssl_context *ctx)
+{
+#ifdef ENABLE_SSL
+ int ret;
+ SSL_CTX *ssl_ctx;
+
+ if (ctx) {
+ ssl_ctx = ctx->ctx;
+ } else {
+ ssl_ctx = sock->default_ctx;
+ }
+
+ sock->ssl = SSL_new(ssl_ctx);
+ if (!sock->ssl) {
+ sock->error = ERROR_SSL_STRING;
+ /* Usually goes wrong because: */
+ fprintf(stderr, "Have you called sock_init()!?\n");
+ return SOCK_ERROR;
+ }
+
+#ifdef SSL_MODE_AUTO_RETRY
+ /* OpenSSL 0.9.6 and later... */
+ (void) SSL_set_mode(sock->ssl, SSL_MODE_AUTO_RETRY);
+#endif
+
+ SSL_set_fd(sock->ssl, sock->fd);
+
+ ret = SSL_connect(sock->ssl);
+ if (ret == -1) {
+ sock->error = ERROR_SSL_STRING;
+ SSL_free(sock->ssl);
+ sock->ssl = NULL;
+ return SOCK_ERROR;
+ }
+
+#if 0
+ /* Tommi Komulainen <Tommi.Komulainen@iki.fi> has donated his SSL
+ * cert verification from the mutt IMAP/SSL code under the
+ * LGPL... it will plug in here */
+ ret = sock_check_certicate(sock);
+ if (ret) {
+ SSL_shutdown(sock->ssl);
+ SSL_free(sock->ssl);
+ sock->ssl = NULL;
+ return ret;
+ }
+#endif
+
+ NE_DEBUG(NE_DBG_SOCKET, "SSL connected: version %s\n",
+ SSL_get_version(sock->ssl));
+ return 0;
+#else
+ sock->error = _("This application does not have SSL support.");
+ return SOCK_ERROR;
+#endif
+}
+
+const char *sock_get_version(nsocket *sock)
+{
+#ifdef ENABLE_SSL
+ return SSL_get_version(sock->ssl);
+#else
+ return NULL;
+#endif
+}
+
+const char *sock_get_error(nsocket *sock)
+{
+ return sock->error;
+}
+
+/* Closes given nsocket */
+int sock_close(nsocket *sock) {
+ int ret;
+#ifdef ENABLE_SSL
+ if (sock->ssl) {
+ SSL_shutdown(sock->ssl);
+ SSL_free(sock->ssl);
+ }
+ SSL_CTX_free(sock->default_ctx);
+#endif
+ ret = NEON_CLOSE(sock->fd);
+ free(sock);
+ return ret;
+}
+
+/* FIXME: get error messages back to the caller. */
+int sock_set_client_cert(nssl_context *ctx, const char *cert, const char *key)
+{
+#ifdef ENABLE_SSL
+ if (SSL_CTX_use_certificate_file(ctx->ctx, cert, SSL_FILETYPE_PEM) <= 0) {
+ NE_DEBUG(NE_DBG_SOCKET, "Could not load cert file.\n");
+ return -1;
+ }
+
+ /* The cert file can contain the private key too, apparently. Not
+ * sure under what circumstances this is sensible, but hey. */
+ if (key == NULL)
+ key = cert;
+
+ /* Set this so the callback can tell the user what's going on. */
+ ctx->key_file = key;
+
+ if (SSL_CTX_use_PrivateKey_file(ctx->ctx, key, SSL_FILETYPE_PEM) <= 0) {
+ NE_DEBUG(NE_DBG_SOCKET, "Could not load private key file.\n");
+ return -1;
+ }
+
+ /* Sanity check. */
+ if (!SSL_CTX_check_private_key(ctx->ctx)) {
+ NE_DEBUG(NE_DBG_SOCKET, "Private key does not match certificate.\n");
+ return -1;
+ }
+
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/* Returns HOST byte order port of given name */
+int sock_service_lookup(const char *name) {
+ struct servent *ent;
+ ent = getservbyname(name, "tcp");
+ if (ent == NULL) {
+ return 0;
+ } else {
+ return ntohs(ent->s_port);
+ }
+}
diff --git a/neon/src/ne_socket.h b/neon/src/ne_socket.h
new file mode 100644
index 000000000..d0e0cf9c7
--- /dev/null
+++ b/neon/src/ne_socket.h
@@ -0,0 +1,228 @@
+/*
+ socket handling interface
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_SOCKET_H
+#define NE_SOCKET_H
+
+#ifdef WIN32
+#include <WinSock2.h>
+#include <stddef.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+#define SOCK_ERROR -1
+/* Read/Write timed out */
+#define SOCK_TIMEOUT -2
+/* Passed buffer was full */
+#define SOCK_FULL -3
+/* Socket was closed */
+#define SOCK_CLOSED -4
+
+/* Socket read timeout */
+#define SOCKET_READ_TIMEOUT 120
+
+struct nsocket_s;
+typedef struct nsocket_s nsocket;
+
+typedef void (*sock_block_reader) (
+ void *userdata, const char *buf, size_t len);
+
+typedef void (*sock_progress)(void *userdata, off_t progress, off_t total);
+
+void sock_register_progress(nsocket *sock, sock_progress cb, void *userdata);
+
+void sock_call_progress(nsocket *sock, off_t progress, off_t total);
+
+/* Initialize the socket library. If you don't do this, SSL WILL NOT WORK.
+ * Returns 0 on success, or non-zero on screwed up SSL library. */
+int sock_init(void);
+
+/* Shutdown the socket library. */
+void sock_exit(void);
+
+/* sock_read is read() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ * SOCK_* on error,
+ * 0 on no data to read (due to EOF),
+ * >0 length of data read into buffer.
+ */
+int sock_read(nsocket *sock, char *buffer, size_t count);
+
+/* sock_peek is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ * SOCK_* on error,
+ * 0 on no data to read (due to EOF),
+ * >0 length of data read into buffer.
+ */
+int sock_peek(nsocket *sock, char *buffer, size_t count);
+
+/* Blocks waiting for data on the given socket for the given time.
+ * Returns:
+ * SOCK_* on error,
+ * SOCK_TIMEOUT on no data within timeout,
+ * 0 if data arrived on the socket.
+ */
+int sock_block(nsocket *sock, int timeout);
+
+/* Reads readlen bytes from fd and writes to socket.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or SOCK_* on error.
+ */
+int sock_transfer(int fd, nsocket *sock, off_t readlen);
+
+/* Sends the given line to given socket, CRLF appended */
+int sock_sendline(nsocket *sock, const char *line);
+/* Sends the given block of data down the nsocket */
+int sock_fullwrite(nsocket *sock, const char *data, size_t length);
+/* Sends the null-terminated string down the given nsocket */
+int sock_send_string(nsocket *sock, const char *string);
+
+/* Reads a line from given nsocket */
+int sock_readline(nsocket *sock, char *line, int len);
+/* Reads a chunk of data. */
+int sock_fullread(nsocket *sock, char *buffer, int buflen);
+
+/* Creates and connects a nsocket */
+nsocket *sock_connect(const struct in_addr host,
+ unsigned short int portnum);
+
+/* Not as good as accept(2), missing parms 2+3.
+ * Addings parms 2+3 would probably mean passing socklen_t as an
+ * int then casting internally, since we don't really want to
+ * autogenerate the header file to be correct for the build platform.
+ */
+nsocket *sock_accept(int listener);
+
+/* Returns the file descriptor used for the socket */
+int sock_get_fd(nsocket *sock);
+
+/* Closes the socket and frees the nsocket object. */
+int sock_close(nsocket *sock);
+
+const char *sock_get_version(nsocket *sock);
+
+const char *sock_get_error(nsocket *sock);
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure. */
+int sock_name_lookup(const char *hostname, struct in_addr *addr);
+
+/* Returns the standard TCP port for the given service */
+int sock_service_lookup(const char *name);
+
+/* Read from socket, passing each block read to reader callback.
+ * Pass userdata as first argument to reader callback.
+ *
+ * If length is -1, keep going till EOF is returned. SOCK_CLOSED
+ * is never returned in this case.
+ *
+ * Otherwise, read exactly 'length' bytes. If EOF is encountered
+ * before length bytes have been read, and SOCK_CLOSED will be
+ * returned.
+ *
+ * Returns:
+ * 0 on success,
+ * SOCK_* on error (SOCK_CLOSED is a special case, as above)
+ */
+int sock_readfile_blocked(nsocket *sock, off_t length,
+ sock_block_reader reader, void *userdata);
+
+/* Auxiliary for use with SSL. */
+struct nssl_context_s;
+typedef struct nssl_context_s nssl_context;
+
+/* Netscape's prompts on getting a certificate which it doesn't
+ * recognize the CA for:
+ * 1. Hey, I don't recognize the CA for this cert.
+ * 2. Here is the certificate: for foo signed by BLAH,
+ * using encryption level BLEE
+ * 3. Allow: accept for this session only,
+ * don't accept
+ * accept forever
+ */
+nssl_context *sock_create_ssl_context(void);
+
+void sock_destroy_ssl_context(nssl_context *ctx);
+
+/* Callback to decide whether the user will accept the
+ * given certificate or not */
+typedef struct {
+ char *owner; /* Multi-line string describing owner of
+ * certificate */
+ char *issuer; /* As above for issuer of certificate */
+ /* Strings the certificate is valid between */
+ char *valid_from, *valid_till;
+ /* Certificate fingerprint */
+ char *fingerprint;
+} nssl_certificate;
+
+/* Returns:
+ * 0 -> User accepts the certificate
+ * non-zero -> user does NOT accept the certificate.
+ */
+typedef int (*nssl_accept)(void *userdata, const nssl_certificate *info);
+
+void sock_set_cert_accept(nssl_context *c,
+ nssl_accept accepter, void *userdata);
+
+/* Callback for retrieving the private key password.
+ * Filename will be the filename of the private key file.
+ * Must return:
+ * 0 on success. buf must be filled in with the password.
+ * non-zero if the user cancelled the prompt.
+ *
+ * FIXME: this is inconsistent with the HTTP authentication callbacks. */
+typedef int (*nssl_key_prompt)(void *userdata, const char *filename,
+ char *buf, int buflen);
+
+void sock_set_key_prompt(nssl_context *c,
+ nssl_key_prompt prompt, void *userdata);
+
+/* For PEM-encoded client certificates: use the given client
+ * certificate and private key file.
+ * Returns: 0 if certificate is read okay,
+ * non-zero otherwise.
+
+ * For decoding the private key file, the callback above will be used
+ * to prompt for the password. If no callback has been set, then the
+ * OpenSSL default will be used: the prompt appears on the terminal.
+ * */
+int sock_set_client_cert(nssl_context *ctx, const char *certfile,
+ const char *keyfile);
+
+void sock_disable_tlsv1(nssl_context *c);
+void sock_disable_sslv2(nssl_context *c);
+void sock_disable_sslv3(nssl_context *c);
+
+/* Ctx is OPTIONAL. If it is NULL, defaults are used. */
+int sock_make_secure(nsocket *sock, nssl_context *ctx);
+
+END_NEON_DECLS
+
+#endif /* NE_SOCKET_H */
diff --git a/neon/src/ne_string.c b/neon/src/ne_string.c
new file mode 100644
index 000000000..b205eb31e
--- /dev/null
+++ b/neon/src/ne_string.c
@@ -0,0 +1,469 @@
+/*
+ String utility functions
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_string.h"
+
+char *ne_token(char **str, char separator, const char *quotes)
+{
+ char *pnt, *ret = NULL;
+
+ if (quotes) {
+
+ for (pnt = *str; *pnt != '\0'; pnt++) {
+ char *quot = strchr(quotes, *pnt);
+
+ if (quot) {
+ char *qclose = strchr(pnt+1, *quot);
+
+ if (!qclose) {
+ /* no closing quote: invalid string. */
+ return NULL;
+ }
+
+ pnt = qclose;
+ } else if (*pnt == separator) {
+ /* found end of token. */
+ *pnt = '\0';
+ ret = *str;
+ *str = pnt + 1;
+ break;
+ }
+ }
+
+ } else {
+ pnt = strchr(*str, separator);
+ if (pnt != NULL) {
+ *pnt = '\0';
+ ret = *str;
+ *str = pnt + 1;
+ }
+ }
+
+ if (ret == NULL) {
+ /* no separator found: return end of string. */
+ ret = *str;
+ *str = NULL;
+ }
+
+ return ret;
+}
+
+char *ne_shave(char *str, const char *whitespace)
+{
+ char *pnt, *ret = str;
+
+ while (strchr(whitespace, *ret) != NULL) {
+ ret++;
+ }
+
+ /* pnt points at the NUL terminator. */
+ pnt = &ret[strlen(ret)];
+
+ while (pnt > ret && strchr(whitespace, *(pnt-1)) != NULL) {
+ pnt--;
+ }
+
+ *pnt = '\0';
+ return ret;
+}
+
+/* TODO: deprecate all these and use ne_token() instead. */
+
+char **split_string(const char *str, const char separator,
+ const char *quotes, const char *whitespace)
+{
+ return split_string_c(str, separator, quotes, whitespace, NULL);
+}
+
+char **split_string_c(const char *str, const char separator,
+ const char *quotes, const char *whitespace,
+ int *give_count)
+{
+ char **comps;
+ const char *pnt, *quot = NULL,
+ *start, *end; /* The start of the current component */
+ int count, /* The number of components */
+ iswhite, /* is it whitespace */
+ issep, /* is it the separator */
+ curr, /* current component index */
+ length, /* length of component */
+ leading_wspace; /* in leading whitespace still? */
+
+ /* Inefficient, but easier - first off, count the number of
+ * components we have. */
+ count = 1;
+ for (pnt = str; *pnt!='\0'; pnt++) {
+ if (quotes != NULL) {
+ quot = strchr(quotes, *pnt);
+ }
+ if (quot != NULL) {
+ /* We found a quote, so skip till the next quote */
+ for (pnt++; (*pnt!=*quot) && (*pnt!='\0'); pnt++)
+ /* nullop */;
+ } else if (*pnt == separator) {
+ count++;
+ }
+ }
+
+ if (give_count) {
+ /* Write the count */
+ *give_count = count;
+ }
+
+ /* Now, have got the number of components.
+ * Allocate the comps array. +1 for the NULL */
+ comps = ne_malloc(sizeof(char *) * (count + 1));
+
+ comps[count] = NULL;
+
+ quot = end = start = NULL;
+ curr = 0;
+ leading_wspace = 1;
+
+ /* Now fill in the array */
+ for (pnt = str; *pnt != '\0'; pnt++) {
+ /* What is the current character - quote, whitespace, separator? */
+ if (quotes != NULL) {
+ quot = strchr(quotes, *pnt);
+ }
+ iswhite = (whitespace!=NULL) &&
+ (strchr(whitespace, *pnt) != NULL);
+ issep = (*pnt == separator);
+ /* What to do? */
+ if (leading_wspace) {
+ if (quot!=NULL) {
+ /* Quoted bit */
+ start = pnt;
+ length = 1;
+ leading_wspace = 0;
+ } else if (issep) {
+ /* Zero-length component */
+ comps[curr++] = ne_strdup("");
+ } else if (!iswhite) {
+ start = end = pnt;
+ length = 1;
+ leading_wspace = 0;
+ }
+ } else {
+ if (quot!=NULL) {
+ /* Quoted bit */
+ length++;
+ } else if (issep) {
+ /* End of component - enter it into the array */
+ length = (end - start) + 1;
+ comps[curr] = ne_malloc(length+1);
+ memcpy(comps[curr], start, length);
+ comps[curr][length] = '\0';
+ curr++;
+ leading_wspace = 1;
+ } else if (!iswhite) {
+ /* Not whitespace - update end marker */
+ end = pnt;
+ }
+ }
+ if (quot != NULL) {
+ /* Skip to closing quote */
+ for (pnt++; *pnt!=*quot && *pnt != '\0'; ++pnt)
+ /* nullop */;
+ /* Last non-wspace char is closing quote */
+ end = pnt;
+ }
+ }
+ /* Handle final component */
+ if (leading_wspace) {
+ comps[curr] = ne_strdup("");
+ } else {
+ /* End of component - enter it into the array */
+ length = (end - start) + 1;
+ comps[curr] = ne_malloc(length+1);
+ memcpy(comps[curr], start, length);
+ comps[curr][length] = '\0';
+ }
+ return comps;
+}
+
+char **pair_string(const char *str, const char compsep, const char kvsep,
+ const char *quotes, const char *whitespace)
+{
+ char **comps, **pairs, *split;
+ int count = 0, n, length;
+ comps = split_string_c(str, compsep, quotes, whitespace, &count);
+ /* Allocate space for 2* as many components as split_string returned,
+ * +2 for the NULLS. */
+ pairs = ne_malloc((2*count+2) * sizeof(char *));
+ if (pairs == NULL) {
+ return NULL;
+ }
+ for (n = 0; n < count; n++) {
+ /* Find the split */
+ split = strchr(comps[n], kvsep);
+ if (split == NULL) {
+ /* No seperator found */
+ length = strlen(comps[n]);
+ } else {
+ length = split-comps[n];
+ }
+ /* Enter the key into the array */
+ pairs[2*n] = comps[n];
+ /* Null-terminate the key */
+ pairs[2*n][length] = '\0';
+ pairs[2*n+1] = split?(split + 1):NULL;
+ }
+ free(comps);
+ pairs[2*count] = pairs[2*count+1] = NULL;
+ return pairs;
+}
+
+void split_string_free(char **components)
+{
+ char **pnt = components;
+ while (*pnt != NULL) {
+ free(*pnt);
+ pnt++;
+ }
+ free(components);
+}
+
+void pair_string_free(char **pairs)
+{
+ int n;
+ for (n = 0; pairs[n] != NULL; n+=2) {
+ free(pairs[n]);
+ }
+ free(pairs);
+}
+
+char *shave_string(const char *str, const char ch)
+{
+ size_t len = strlen(str);
+ char *ret;
+ if (str[len-1] == ch) {
+ len--;
+ }
+ if (str[0] == ch) {
+ len--;
+ str++;
+ }
+ ret = ne_malloc(len + 1);
+ memcpy(ret, str, len);
+ ret[len] = '\0';
+ return ret;
+}
+
+char *ne_concat(const char *str, ...)
+{
+ va_list ap;
+ ne_buffer *tmp = ne_buffer_create();
+
+ ne_buffer_zappend(tmp, str);
+
+ va_start(ap, str);
+ ne_buffer_concat(tmp, ap);
+ va_end(ap);
+
+ return ne_buffer_finish(tmp);
+}
+
+void ne_buffer_clear(ne_buffer *buf)
+{
+ memset(buf->data, 0, buf->length);
+ buf->used = 1;
+}
+
+/* Grows for given size, returns 0 on success, -1 on error. */
+int ne_buffer_grow(ne_buffer *buf, size_t newsize)
+{
+ size_t newlen, oldbuflen;
+
+#define NE_BUFFER_GROWTH 512
+
+ if (newsize <= buf->length) return 0; /* big enough already */
+ /* FIXME: ah, can't remember my maths... better way to do this? */
+ newlen = ((newsize / NE_BUFFER_GROWTH) + 1) * NE_BUFFER_GROWTH;
+
+ oldbuflen = buf->length;
+ /* Reallocate bigger buffer */
+ buf->data = realloc(buf->data, newlen);
+ if (buf->data == NULL) return -1;
+ buf->length = newlen;
+ /* Zero-out the new bit of buffer */
+ memset(buf->data+oldbuflen, 0, newlen-oldbuflen);
+
+ return 0;
+}
+
+int ne_buffer_concat(ne_buffer *buf, ...)
+{
+ va_list ap;
+ char *next;
+ size_t totallen = buf->used;
+
+ /* Find out how much space we need for all the args */
+ va_start(ap, buf);
+ do {
+ next = va_arg(ap, char *);
+ if (next != NULL) {
+ totallen += strlen(next);
+ }
+ } while (next != NULL);
+ va_end(ap);
+
+ /* Grow the buffer */
+ if (ne_buffer_grow(buf, totallen))
+ return -1;
+
+ /* Now append the arguments to the buffer */
+ va_start(ap, buf);
+ do {
+ next = va_arg(ap, char *);
+ if (next != NULL) {
+ /* TODO: use stpcpy */
+ strcat(buf->data, next);
+ }
+ } while (next != NULL);
+ va_end(ap);
+
+ buf->used = totallen;
+ return 0;
+}
+
+/* Append zero-terminated string... returns 0 on success or -1 on
+ * realloc failure. */
+int ne_buffer_zappend(ne_buffer *buf, const char *str)
+{
+ size_t len = strlen(str);
+
+ if (ne_buffer_grow(buf, buf->used + len)) {
+ return -1;
+ }
+ strcat(buf->data, str);
+ buf->used += len;
+ return 0;
+}
+
+int ne_buffer_append(ne_buffer *buf, const char *data, size_t len)
+{
+ if (ne_buffer_grow(buf, buf->used + len)) {
+ return -1;
+ }
+ memcpy(buf->data + buf->used - 1, data, len);
+ buf->used += len;
+ buf->data[buf->used - 1] = '\0';
+ return 0;
+}
+
+ne_buffer *ne_buffer_create(void)
+{
+ return ne_buffer_create_sized(512);
+}
+
+ne_buffer *ne_buffer_create_sized(size_t s)
+{
+ ne_buffer *buf = ne_malloc(sizeof(struct ne_buffer_s));
+ buf->data = ne_calloc(s);
+ buf->length = s;
+ buf->used = 1;
+ return buf;
+}
+
+void ne_buffer_destroy(ne_buffer *buf)
+{
+ if (buf->data) {
+ free(buf->data);
+ }
+ free(buf);
+}
+
+char *ne_buffer_finish(ne_buffer *buf)
+{
+ char *ret = buf->data;
+ free(buf);
+ return ret;
+}
+
+void ne_buffer_altered(ne_buffer *buf)
+{
+ buf->used = strlen(buf->data) + 1;
+}
+
+char *ne_utf8_encode(const char *str)
+{
+ char *buffer = ne_malloc(strlen(str) * 2 + 1);
+ int in, len = strlen(str);
+ char *out;
+
+ for (in = 0, out = buffer; in < len; in++, out++) {
+ if ((unsigned char)str[in] <= 0x7D) {
+ *out = str[in];
+ } else {
+ *out++ = 0xC0 | ((str[in] & 0xFC) >> 6);
+ *out = str[in] & 0xBF;
+ }
+ }
+
+ /* nul-terminate */
+ *out = '\0';
+ return buffer;
+}
+
+/* Single byte range 0x00 -> 0x7F */
+#define SINGLEBYTE_UTF8(ch) (((unsigned char) (ch)) < 0x80)
+
+char *ne_utf8_decode(const char *str)
+{
+ int n, m, len = strlen(str);
+ char *dest = ne_malloc(len + 1);
+
+ for (n = 0, m = 0; n < len; n++, m++) {
+ if (SINGLEBYTE_UTF8(str[n])) {
+ dest[m] = str[n];
+ } else {
+ /* We only deal with 8-bit data, which will be encoded as
+ * two bytes of UTF-8 */
+ if ((len - n < 2) || (str[n] & 0xFC) != 0xC0) {
+ free(dest);
+ return NULL;
+ } else {
+ /* see hip_xml.c for comments. */
+ dest[m] = ((str[n] & 0x03) << 6) | (str[n+1] & 0x3F);
+ n++;
+ }
+ }
+ }
+
+ dest[m] = '\0';
+ return dest;
+}
diff --git a/neon/src/ne_string.h b/neon/src/ne_string.h
new file mode 100644
index 000000000..517bcfefc
--- /dev/null
+++ b/neon/src/ne_string.h
@@ -0,0 +1,212 @@
+/*
+ String utility functions
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_STRING_H
+#define NE_STRING_H
+
+#include "ne_defs.h"
+#include "ne_alloc.h"
+
+#include <stdarg.h>
+
+BEGIN_NEON_DECLS
+
+/* Returns an ne_malloc-allocated UTF-8 encoded copy of 'str'. */
+char *ne_utf8_encode(const char *str);
+
+/* Returns an ne_malloc-allocated UTF-8 decode copy of 'str'.
+ * Returns NULL if any of the characters in 'str' are non-8-bit.
+ */
+char *ne_utf8_decode(const char *str);
+
+/* Simple string tokenizer.
+ *
+ * Returns the next token in *str between *str and 'separator'
+ * or NUL terminator, skipping over any parts quoted using
+ * a pair of any character found in 'quotes'. After returning,
+ * *str will point to the next character after the separator,
+ * or NULL if no more separator characters were found.
+ *
+ * quotes may be NULL.
+ *
+ * Returns NULL if unbalanced quotes are found. (this will not
+ * happen if quotes == NULL).
+ */
+char *ne_token(char **str, char separator, const char *quotes);
+
+/* Return portion of 'str' with any characters in 'whitespace' shaved
+ * off the beginning and end. Modifies str. */
+char *ne_shave(char *str, const char *whitespace);
+
+/* Splits str into component parts using given seperator,
+ * skipping given whitespace around separators and at the
+ * beginning and end of str. Separators are ignored within
+ * any pair of characters specified as being quotes.
+ * Returns array of components followed by a NULL pointer. The
+ * components are taken from dynamically allocated memory, and
+ * the entire array can be easily freed using split_string_free.
+ *
+ * aka: What strtok should have been.
+ */
+char **split_string(const char *str, const char seperator,
+ const char *quotes, const char *whitespace);
+
+/* As above, but returns count of items as well */
+char **split_string_c(const char *str, const char seperator,
+ const char *quotes, const char *whitespace, int *count);
+
+/* A bit like split_string, except each component is split into a pair.
+ * Each pair is returned consecutively in the return array.
+ * e.g.:
+ * strpairs("aa=bb,cc=dd", ',', '=', NULL, NULL)
+ * => "aa", "bb", "cc", "dd", NULL, NULL
+ * Note, that if a component is *missing* it's key/value separator,
+ * then the 'value' in the array will be a NULL pointer. But, if the
+ * value is zero-length (i.e., the component separator follows directly
+ * after the key/value separator, or with only whitespace inbetween),
+ * then the value in the array will be a zero-length string.
+ * e.g.:
+ * pair_string("aaaa,bb=cc,dd=,ee=ff", ',', '=', NULL, NULL)
+ * => "aaaa", NULL, "bb", "cc", "dd", "", "ee", "ff"
+ * A NULL key indicates the end of the array (the value will also
+ * be NULL, for convenience).
+ */
+char **pair_string(const char *str, const char compsep, const char kvsep,
+ const char *quotes, const char *whitespace);
+
+/* Frees the array returned by split_string */
+void split_string_free(char **components);
+
+/* Frees the array returned by pair_string */
+void pair_string_free(char **pairs);
+
+/* Returns a string which is str with ch stripped from
+ * beggining and end, if present in either. The string returned
+ * is dynamically allocated using malloc().
+ *
+ * e.g. shave_string("abbba", 'a') => "bbb". */
+char *shave_string(const char *str, const char ch);
+
+#define EOL "\r\n"
+
+#define STRIP_EOL(str) \
+do { \
+ char *p; \
+ if ((p = strrchr(str, '\r')) != NULL) *p = '\0'; \
+ if ((p = strrchr(str, '\n')) != NULL) *p = '\0'; \
+} while (0)
+
+/* Return concatenation of all given strings. */
+char *ne_concat(const char *str, ...);
+
+/* String buffer handling. (Strings are zero-terminated still).
+ * A string buffer sbuffer * which grows dynamically with the string. */
+
+struct ne_buffer_s {
+ char *data; /* contents: null-terminated string. */
+ size_t used; /* used bytes in buffer */
+ size_t length; /* length of buffer */
+};
+
+typedef struct ne_buffer_s ne_buffer;
+
+/* Returns size of data in buffer, equiv to strlen(ne_buffer_data(buf)) */
+#define ne_buffer_size(buf) ((buf)->used - 1)
+
+/* Concatenate all given strings onto the end of the buffer.
+ * The strings must be null-terminated, and MUST be followed by a
+ * NULL argument marking the end of the list.
+ * Returns:
+ * 0 on success
+ * non-zero on error
+ */
+int ne_buffer_concat(ne_buffer *buf, ...);
+
+/* Create a new ne_buffer. Returns NULL on error */
+ne_buffer *ne_buffer_create(void);
+
+/* Create a new ne_buffer of given minimum size. Returns NULL on error */
+ne_buffer *ne_buffer_create_sized(size_t size);
+
+/* Destroys (deallocates) a buffer */
+void ne_buffer_destroy(ne_buffer *buf);
+
+/* Append a zero-terminated string 'str' to buf.
+ * Returns 0 on success, non-zero on error. */
+int ne_buffer_zappend(ne_buffer *buf, const char *str);
+
+/* Append 'len' bytes of 'data' to buf. 'data' does not need to be
+ * zero-terminated. The resultant string will have a zero-terminator,
+ * either way. Returns 0 on success, non-zero on error. */
+int ne_buffer_append(ne_buffer *buf, const char *data, size_t len);
+
+/* Empties the contents of buf; makes the buffer zero-length. */
+void ne_buffer_clear(ne_buffer *buf);
+
+/* Grows the ne_buffer to a minimum size.
+ * Returns 0 on success, non-zero on error */
+int ne_buffer_grow(ne_buffer *buf, size_t size);
+
+void ne_buffer_altered(ne_buffer *buf);
+
+/* Destroys a buffer, WITHOUT freeing the data, and returns the
+ * data. */
+char *ne_buffer_finish(ne_buffer *buf);
+
+/* TODO: do these with stpcpy instead... more efficient, but means
+ * bloat on non-GNU platforms. */
+
+/* TODO: could replace with glib equiv's where available, too */
+
+/* NOTES:
+ * - These abort() on malloc() returning NULL
+ * - You will need to #include <string.h> / <strings.h> YOURSELF to
+ * prototype strlen and strcat.
+ */
+
+#define CONCAT2(out, str1, str2) \
+do { \
+ out = ne_malloc(strlen(str1) + strlen(str2) + 1); \
+ strcpy(out, str1); \
+ strcat(out, str2); \
+} while (0)
+
+#define CONCAT3(out, str1, str2, str3) \
+do { \
+ out = ne_malloc(strlen(str1) + strlen(str2) + strlen(str3) + 1); \
+ strcpy(out, str1); \
+ strcat(out, str2); \
+ strcat(out, str3); \
+} while (0)
+
+#define CONCAT4(out, str1, str2, str3, str4) \
+do { \
+ out = ne_malloc(strlen(str1) + strlen(str2) \
+ + strlen(str3) + strlen(str4) + 1); \
+ strcpy(out, str1); \
+ strcat(out, str2); \
+ strcat(out, str3); \
+ strcat(out, str4); \
+} while (0)
+
+END_NEON_DECLS
+
+#endif /* NE_STRING_H */
diff --git a/neon/src/ne_uri.c b/neon/src/ne_uri.c
new file mode 100644
index 000000000..fbc2a8d0d
--- /dev/null
+++ b/neon/src/ne_uri.c
@@ -0,0 +1,313 @@
+/*
+ HTTP URI handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <ctype.h>
+
+#include "ne_utils.h" /* for 'min' */
+#include "ne_string.h" /* for CONCAT3 */
+#include "ne_alloc.h"
+#include "ne_uri.h"
+
+char *uri_parent(const char *uri)
+{
+ const char *pnt;
+ char *ret;
+ pnt = uri+strlen(uri)-1;
+ while (*(--pnt) != '/' && pnt >= uri) /* noop */;
+ if (pnt < uri) {
+ /* not a valid absPath */
+ return NULL;
+ }
+ /* uri
+ * V
+ * |---|
+ * /foo/bar/
+ */
+ ret = ne_malloc((pnt - uri) + 2);
+ memcpy(ret, uri, (pnt - uri) + 1);
+ ret[1+(pnt-uri)] = '\0';
+ pnt++;
+ return ret;
+}
+
+int uri_has_trailing_slash(const char *uri)
+{
+ size_t len = strlen(uri);
+ return ((len > 0) &&
+ (uri[len-1] == '/'));
+}
+
+const char *uri_abspath(const char *uri)
+{
+ const char *ret;
+ /* Look for the scheme: */
+ ret = strstr(uri, "://");
+ if (ret == NULL) {
+ /* No scheme */
+ ret = uri;
+ } else {
+ /* Look for the abs_path */
+ ret = strchr(ret+3, '/');
+ if (ret == NULL) {
+ /* Uh-oh */
+ ret = uri;
+ }
+ }
+ return ret;
+}
+
+/* TODO: not a proper URI parser */
+int uri_parse(const char *uri, struct uri *parsed,
+ const struct uri *defaults)
+{
+ const char *pnt, *slash, *colon, *atsign;
+
+ parsed->port = -1;
+ parsed->host = NULL;
+ parsed->path = NULL;
+ parsed->scheme = NULL;
+ parsed->authinfo = NULL;
+
+ if (strlen(uri) == 0) {
+ return -1;
+ }
+
+ pnt = strstr(uri, "://");
+ if (pnt) {
+ parsed->scheme = ne_strndup(uri, pnt - uri);
+ pnt += 3; /* start of hostport segment */
+ } else {
+ pnt = uri;
+ if (defaults && defaults->scheme != NULL) {
+ parsed->scheme = ne_strdup(defaults->scheme);
+ }
+ }
+
+ atsign = strchr(pnt, '@');
+ slash = strchr(pnt, '/');
+
+ /* Check for an authinfo segment in the hostport segment. */
+ if (atsign != NULL && (slash == NULL || atsign < slash)) {
+ parsed->authinfo = ne_strndup(pnt, atsign - pnt);
+ pnt = atsign + 1;
+ }
+
+ colon = strchr(pnt, ':');
+
+ if (slash == NULL) {
+ parsed->path = ne_strdup("/");
+ if (colon == NULL) {
+ if (defaults) parsed->port = defaults->port;
+ parsed->host = ne_strdup(pnt);
+ } else {
+ parsed->port = atoi(colon+1);
+ parsed->host = ne_strndup(pnt, colon - pnt);
+ }
+ } else {
+ if (colon == NULL || colon > slash) {
+ /* No port segment */
+ if (defaults) parsed->port = defaults->port;
+ if (slash != uri) {
+ parsed->host = ne_strndup(pnt, slash - pnt);
+ } else {
+ /* No hostname segment. */
+ }
+ } else {
+ /* Port segment */
+ parsed->port = atoi(colon + 1);
+ parsed->host = ne_strndup(pnt, colon - pnt);
+ }
+ parsed->path = ne_strdup(slash);
+ }
+
+ return 0;
+}
+
+void uri_free(struct uri *uri)
+{
+ NE_FREE(uri->host);
+ NE_FREE(uri->path);
+ NE_FREE(uri->scheme);
+ NE_FREE(uri->authinfo);
+}
+
+/* Returns an absoluteURI */
+char *uri_absolute(const char *uri, const char *scheme,
+ const char *hostport)
+{
+ char *ret;
+ /* Is it absolute already? */
+ if (strncmp(uri, scheme, strlen(scheme)) == 0) {
+ /* Yes it is */
+ ret = ne_strdup(uri);
+ } else {
+ /* Oh no it isn't */
+ CONCAT3(ret, scheme, hostport, uri);
+ }
+ return ret;
+}
+
+/* Un-escapes a URI. Returns ne_malloc-allocated URI */
+char *uri_unescape(const char *uri)
+{
+ const char *pnt;
+ char *ret, *retpos, buf[5] = { "0x00\0" };
+ retpos = ret = ne_malloc(strlen(uri) + 1);
+ for (pnt = uri; *pnt != '\0'; pnt++) {
+ if (*pnt == '%') {
+ if (!isxdigit((unsigned char) pnt[1]) ||
+ !isxdigit((unsigned char) pnt[2])) {
+ /* Invalid URI */
+ return NULL;
+ }
+ buf[2] = *++pnt; buf[3] = *++pnt; /* bit faster than memcpy */
+ *retpos++ = (char)strtol(buf, NULL, 16);
+ } else {
+ *retpos++ = *pnt;
+ }
+ }
+ *retpos = '\0';
+ return ret;
+}
+
+/* RFC2396 spake:
+ * "Data must be escaped if it does not have a representation
+ * using an unreserved character".
+ */
+
+/* Lookup table: character classes from 2396. (This is overkill) */
+
+#define SP 0 /* space = <US-ASCII coded character 20 hexadecimal> */
+#define CO 0 /* control = <US-ASCII coded characters 00-1F and 7F hexadecimal> */
+#define DE 0 /* delims = "<" | ">" | "#" | "%" | <"> */
+#define UW 0 /* unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" */
+#define MA 1 /* mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" */
+#define AN 2 /* alphanum = alpha | digit */
+#define RE 2 /* reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," */
+
+static const char uri_chars[128] = {
+/* +2 +4 +6 +8 +10 +12 +14 */
+/* 0 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO,
+/* 16 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO,
+/* 32 */ SP, MA, DE, DE, RE, DE, RE, MA, MA, MA, MA, RE, RE, MA, MA, RE,
+/* 48 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, RE, RE, DE, RE, DE, RE,
+/* 64 */ RE, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN,
+/* 80 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, UW, MA,
+/* 96 */ UW, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN,
+/* 112 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, MA, CO
+};
+
+#define ESCAPE(ch) (((const signed char)(ch) < 0 || \
+ uri_chars[(unsigned int)(ch)] == 0))
+
+#undef SP
+#undef CO
+#undef DE
+#undef UW
+#undef MA
+#undef AN
+#undef RE
+
+/* Escapes the abspath segment of a URI.
+ * Returns ne_malloc-allocated string.
+ */
+char *uri_abspath_escape(const char *abs_path)
+{
+ const char *pnt;
+ char *ret, *retpos;
+ int count = 0;
+ for (pnt = abs_path; *pnt != '\0'; pnt++) {
+ if (ESCAPE(*pnt)) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ return ne_strdup(abs_path);
+ }
+ /* An escaped character is "%xx", i.e., two MORE
+ * characters than the original string */
+ retpos = ret = ne_malloc(strlen(abs_path) + 2*count + 1);
+ for (pnt = abs_path; *pnt != '\0'; pnt++) {
+ if (ESCAPE(*pnt)) {
+ /* Escape it - %<hex><hex> */
+ sprintf(retpos, "%%%02x", (unsigned char) *pnt);
+ retpos += 3;
+ } else {
+ /* It's cool */
+ *retpos++ = *pnt;
+ }
+ }
+ *retpos = '\0';
+ return ret;
+}
+
+#undef ESCAPE
+
+/* TODO: implement properly */
+int uri_compare(const char *a, const char *b)
+{
+ int ret = strcasecmp(a, b);
+ if (ret) {
+ /* This logic says: "If the lengths of the two URIs differ by
+ * exactly one, and the LONGER of the two URIs has a trailing
+ * slash and the SHORTER one DOESN'T, then..." */
+ int traila = uri_has_trailing_slash(a),
+ trailb = uri_has_trailing_slash(b),
+ lena = strlen(a), lenb = strlen(b);
+ if (traila != trailb && abs(lena - lenb) == 1 &&
+ ((traila && lena > lenb) || (trailb && lenb > lena))) {
+ /* Compare them, ignoring the trailing slash on the longer
+ * URI */
+ if (strncasecmp(a, b, min(lena, lenb)) == 0)
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+/* Give it a path segment, it returns non-zero if child is
+ * a child of parent. */
+int uri_childof(const char *parent, const char *child)
+{
+ char *root = ne_strdup(child);
+ int ret;
+ if (strlen(parent) >= strlen(child)) {
+ ret = 0;
+ } else {
+ /* root is the first of child, equal to length of parent */
+ root[strlen(parent)] = '\0';
+ ret = (uri_compare(parent, root) == 0);
+ }
+ free(root);
+ return ret;
+}
diff --git a/neon/src/ne_uri.h b/neon/src/ne_uri.h
new file mode 100644
index 000000000..12cb49728
--- /dev/null
+++ b/neon/src/ne_uri.h
@@ -0,0 +1,75 @@
+/*
+ HTTP URI handling
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_URI_H
+#define NE_URI_H
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Un-escapes a URI. Returns malloc-allocated URI on success,
+ * or NULL on failure (malloc failure or invalid %<HEX><HEX> sequence). */
+char *uri_unescape(const char *uri);
+
+/* Escapes the abspath segment of a URI.
+ * Returns malloc-allocated string on success, or NULL on malloc failure.
+ */
+char *uri_abspath_escape(const char *abs_path);
+
+/* Returns abspath segment in (absolute) uri */
+const char *uri_abspath(const char *uri);
+
+/* Returns parent of path */
+char *uri_parent(const char *path);
+
+/* Returns strcmp-like value giving comparison between a and b,
+ * ignoring trailing-slashes. */
+int uri_compare(const char *a, const char *b);
+
+/* Returns an absolute URI from a possibly-relative 'uri', using
+ * given scheme + hostport segment.
+ * Returns malloc-allocated string on success, or NULL on malloc failure. */
+char *uri_absolute(const char *uri, const char *scheme, const char *hostport);
+
+/* Returns non-zero if child is a child of parent */
+int uri_childof(const char *parent, const char *child);
+
+/* Returns non-zero if uri has a trailing slash character */
+int uri_has_trailing_slash(const char *uri);
+
+struct uri {
+ char *scheme;
+ char *host;
+ int port;
+ char *path;
+ char *authinfo;
+};
+
+/* Parse 'uri' and place parsed segments in *parsed. */
+int uri_parse(const char *uri, struct uri *parsed, const struct uri *defaults);
+
+void uri_free(struct uri *parsed);
+
+END_NEON_DECLS
+
+#endif /* NE_URI_H */
+
diff --git a/neon/src/ne_utils.c b/neon/src/ne_utils.c
new file mode 100644
index 000000000..fe6afa87b
--- /dev/null
+++ b/neon/src/ne_utils.c
@@ -0,0 +1,160 @@
+/*
+ HTTP utility functions
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <ctype.h> /* isdigit() for ne_parse_statusline */
+
+#include "ne_utils.h"
+
+#include "ne_dates.h"
+
+#ifdef ENABLE_SSL
+#include <openssl/opensslv.h>
+#endif
+
+#ifdef HAVE_XMLVERSION_H
+/* libxml2: pick up the version string. */
+#include <xmlversion.h>
+#endif
+
+int ne_debug_mask = 0;
+FILE *ne_debug_stream = NULL;
+
+void ne_debug_init(FILE *stream, int mask)
+{
+ ne_debug_stream = stream;
+ ne_debug_mask = mask;
+}
+
+void ne_debug(int ch, const char *template, ...)
+{
+ va_list params;
+ if ((ch&ne_debug_mask) != ch) return;
+ fflush(stdout);
+ va_start(params, template);
+ vfprintf(ne_debug_stream, template, params);
+ va_end(params);
+ if ((ch&NE_DBG_FLUSH) == NE_DBG_FLUSH) {
+ fflush(ne_debug_stream);
+ }
+}
+
+static const char *version_string = "neon " NEON_VERSION ": "
+#ifdef NEON_IS_LIBRARY
+ "Library build"
+#else
+ "Bundled build"
+#endif
+#ifdef HAVE_EXPAT
+ ", Expat"
+#else
+#ifdef HAVE_LIBXML
+ ", libxml"
+#ifdef LIBXML_DOTTED_VERSION
+ " " LIBXML_DOTTED_VERSION
+#endif
+#endif
+#endif
+#ifdef ENABLE_SSL
+ ", SSL support using "
+#ifdef OPENSSL_VERSION_TEXT
+ OPENSSL_VERSION_TEXT
+#else
+ "unknown SSL library"
+#endif /* OPENSSL_VERSION_TEXT */
+#else /* !ENABLE_SSL */
+ ", no OpenSSL support"
+#endif /* ENABLE_SSL */
+ "."
+;
+
+const char *ne_version_string(void)
+{
+ return version_string;
+}
+
+int ne_version_minimum(int major, int minor)
+{
+ return
+ (NEON_VERSION_MAJOR < major) ||
+ (NEON_VERSION_MINOR < minor);
+}
+
+int ne_parse_statusline(const char *status_line, ne_status *st)
+{
+ const char *part;
+ int major, minor, status_code, klass;
+
+ /* Check they're speaking the right language */
+ status_line = strstr(status_line, "HTTP/");
+ if (status_line == NULL) {
+ return -1;
+ }
+
+ /* And find out which dialect of this peculiar language
+ * they can talk... */
+ major = 0;
+ minor = 0;
+ /* Note, we're good children, and accept leading zero's on the
+ * version numbers */
+ for (part = status_line + 5; *part != '\0' && isdigit(*part); part++) {
+ major = major*10 + (*part-'0');
+ }
+ if (*part != '.') {
+ return -1;
+ }
+ for (part++ ; *part != '\0' && isdigit(*part); part++) {
+ minor = minor*10 + (*part-'0');
+ }
+ if (*part != ' ') {
+ return -1;
+ }
+ /* Skip any spaces */
+ for (; *part == ' ' ; part++) /* noop */;
+ /* Now for the Status-Code. part now points at the first Y in
+ * "HTTP/x.x YYY". We want value of YYY... could use atoi, but
+ * probably quicker this way. */
+ if (!isdigit(part[0]) || !isdigit(part[1]) || !isdigit(part[2]) ||
+ part[3] != ' ') {
+ return -1;
+ }
+ status_code = 100*(part[0]-'0') + 10*(part[1]-'0') + (part[2]-'0');
+ klass = part[0]-'0';
+ /* Skip whitespace between status-code and reason-phrase */
+ for (part+=3; *part == ' ' || *part == '\t'; part++) /* noop */;
+ if (*part == '\0') {
+ return -1;
+ }
+ /* Fill in the results */
+ st->major_version = major;
+ st->minor_version = minor;
+ st->reason_phrase = part;
+ st->code = status_code;
+ st->klass = klass;
+ return 0;
+}
diff --git a/neon/src/ne_utils.h b/neon/src/ne_utils.h
new file mode 100644
index 000000000..f269a681f
--- /dev/null
+++ b/neon/src/ne_utils.h
@@ -0,0 +1,133 @@
+/*
+ HTTP utility functions
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_UTILS_H
+#define NE_UTILS_H
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Returns a human-readable version string like:
+ * "neon 0.2.0: Library build, OpenSSL support"
+ */
+const char *ne_version_string(void);
+
+/* Returns non-zero if the neon API compiled in is less than
+ * major.minor. i.e.
+ * I am: 1.2 - neon_version_check(1, 3) => -1
+ * I am: 0.10 - neon_version_check(0, 9) => 0
+ */
+int ne_version_minimum(int major, int minor);
+
+#define HTTP_QUOTES "\"'"
+#define HTTP_WHITESPACE " \r\n\t"
+
+#ifndef WIN32
+#undef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifdef WIN32
+/* some of the API uses ssize_t so we need to define it. */
+#define ssize_t int
+#endif
+
+/* CONSIDER: mutt has a nicer way of way of doing debugging output... maybe
+ * switch to like that. */
+
+#ifdef __GNUC__
+/* really, we want to do this if we have any C99-capable compiler, so
+ * what's a better test? */
+
+#ifndef NE_DEBUGGING
+#define NE_DEBUG(x, fmt, args...)
+#else
+#define NE_DEBUG(x, fmt, args...) do { \
+ if (((x)&ne_debug_mask) == (x)) { \
+ fflush(stdout); \
+ fprintf(ne_debug_stream, fmt, ##args); \
+ if (((x) & NE_DBG_FLUSH) == NE_DBG_FLUSH) \
+ fflush(ne_debug_stream); \
+ } \
+} while (0)
+#endif
+
+#else /* !__GNUC__ */
+
+#ifndef NE_DEBUGGING
+#define NE_DEBUG if (0) ne_debug
+#else /* DEBUGGING */
+#define NE_DEBUG ne_debug
+#endif /* DEBUGGING */
+
+#endif
+
+#define NE_DBG_SOCKET (1<<0)
+#define NE_DBG_HTTP (1<<1)
+#define NE_DBG_XML (1<<2)
+#define NE_DBG_HTTPAUTH (1<<3)
+#define NE_DBG_HTTPPLAIN (1<<4)
+#define NE_DBG_LOCKS (1<<5)
+#define NE_DBG_XMLPARSE (1<<6)
+#define NE_DBG_HTTPBODY (1<<7)
+#define NE_DBG_HTTPBASIC (1<<8)
+#define NE_DBG_FLUSH (1<<30)
+
+void ne_debug_init(FILE *stream, int mask);
+extern int ne_debug_mask;
+extern FILE *ne_debug_stream;
+
+void ne_debug(int ch, const char *, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif /* __GNUC__ */
+;
+
+/* Storing an HTTP status result */
+typedef struct {
+ int major_version;
+ int minor_version;
+ int code; /* Status-Code value */
+ /* We can't use 'class' as the member name since this crashes
+ * with the C++ reserved keyword 'class', annoyingly.
+ * This was '_class' previously, but that was even MORE annoying.
+ * So know it is klass. */
+ int klass; /* Class of Status-Code (1-5) */
+ const char *reason_phrase;
+} ne_status;
+
+/* Parser for strings which follow the Status-Line grammar from
+ * RFC2616.
+ * Returns:
+ * 0 on success, *s will be filled in.
+ * -1 on parse error.
+ */
+int ne_parse_statusline(const char *status_line, ne_status *s);
+
+END_NEON_DECLS
+
+#endif /* NE_UTILS_H */
diff --git a/neon/src/ne_xml.c b/neon/src/ne_xml.c
new file mode 100644
index 000000000..0920c1fa9
--- /dev/null
+++ b/neon/src/ne_xml.c
@@ -0,0 +1,862 @@
+/*
+ Higher Level Interface to XML Parsers.
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_i18n.h"
+
+#include "ne_alloc.h"
+#include "ne_xml.h"
+#include "ne_utils.h"
+#include "ne_string.h"
+
+#ifdef HAVE_EXPAT
+/******** Expat ***********/
+#ifdef HAVE_OLD_EXPAT
+/* jclark expat */
+#include "xmlparse.h"
+#else /* not HAVE_OLD_EXPAT */
+/* new-style expat */
+#include "expat.h"
+#endif
+
+typedef XML_Char ne_xml_char;
+
+#else /* not HAVE_EXPAT */
+# ifdef HAVE_LIBXML
+
+#ifdef HAVE_XMLVERSION_H
+/* libxml2: pick up the version string. */
+#include <xmlversion.h>
+#endif
+
+/******** libxml **********/
+# include <parser.h>
+typedef xmlChar ne_xml_char;
+
+# else /* not HAVE_LIBXML */
+# error need an XML parser
+# endif /* not HAVE_LIBXML */
+#endif /* not HAVE_EXPAT */
+
+/* Approx. one screen of text: */
+#define ERR_SIZE (2048)
+
+/* A list of elements */
+struct ne_xml_handler {
+ const struct ne_xml_elm *elements; /* put it in static memory */
+ ne_xml_validate_cb validate_cb; /* validation function */
+ ne_xml_startelm_cb startelm_cb; /* on-complete element function */
+ ne_xml_endelm_cb endelm_cb; /* on-complete element function */
+ ne_xml_cdata_cb cdata_cb; /* cdata callback for mixed mode */
+ void *userdata;
+ struct ne_xml_handler *next;
+};
+
+#ifdef HAVE_LIBXML
+static void sax_error(void *ctx, const char *msg, ...);
+#endif
+
+struct ne_xml_state {
+ /* The element details */
+ const struct ne_xml_elm *elm;
+
+ /* Storage for an unknown element */
+ struct ne_xml_elm elm_real;
+ char *real_name;
+
+ /* Namespaces declared in this element */
+ ne_xml_char *default_ns; /* A default namespace */
+ struct ne_xml_nspace *nspaces; /* List of other namespace scopes */
+
+ unsigned int mixed:1; /* are we in MIXED mode? */
+
+ /* Extras */
+ struct ne_xml_handler *handler; /* Where the element was declared */
+ struct ne_xml_state *parent; /* The parent in the tree */
+};
+
+
+/* TODO:
+ * could move 'valid' into state, maybe allow optional
+ * continuation past an invalid branch.
+ */
+
+/* We pass around a ne_xml_parser as the userdata in the parsing
+ * library. This maintains the current state of the parse and various
+ * other bits and bobs. Within the parse, we store the current branch
+ * of the tree, i.e., the current element and all its parents, up to
+ * the root, but nothing other than that. */
+struct ne_xml_parser_s {
+ struct ne_xml_state *root; /* the root of the document */
+ struct ne_xml_state *current; /* current element in the branch */
+ ne_buffer *buffer; /* the CDATA/collect buffer */
+ unsigned int valid:1; /* currently valid? */
+ unsigned int want_cdata:1; /* currently collecting CDATA? */
+ unsigned int collect; /* current collect depth */
+ struct ne_xml_handler *top_handlers; /* always points at the
+ * handler on top of the stack. */
+#ifdef HAVE_EXPAT
+ XML_Parser parser;
+#else
+ xmlParserCtxtPtr parser;
+#endif
+ char error[ERR_SIZE];
+};
+
+static void destroy_state(struct ne_xml_state *s);
+
+static const char *friendly_name(const struct ne_xml_elm *elm)
+{
+ switch(elm->id) {
+ case NE_ELM_root:
+ return _("document root");
+ case NE_ELM_unknown:
+ return _("unknown element");
+ default:
+ if (elm->name) {
+ return elm->name;
+ } else {
+ return _("unspecified");
+ }
+ }
+}
+
+const static struct ne_xml_elm root_element =
+{ "@<root>@", NE_ELM_root, 0 };
+
+/* The callback handlers */
+static void start_element(void *userdata, const ne_xml_char *name, const ne_xml_char **atts);
+static void end_element(void *userdata, const ne_xml_char *name);
+static void char_data(void *userdata, const ne_xml_char *cdata, int len);
+
+#define NE_XML_DECODE_UTF8
+
+#ifdef NE_XML_DECODE_UTF8
+
+/* UTF-8 decoding */
+
+/* Single byte range 0x00 -> 0x7F */
+#define SINGLEBYTE_UTF8(ch) (((unsigned char) (ch)) < 0x80)
+
+/* Decode a double byte UTF8 string.
+ * Returns 0 on success or non-zero on error. */
+static inline int decode_utf8_double(char *dest, const char *src);
+
+#endif
+
+/* Linked list of namespace scopes */
+struct ne_xml_nspace {
+ ne_xml_char *name;
+ ne_xml_char *uri;
+ struct ne_xml_nspace *next;
+};
+
+/* And an auxiliary */
+static int parse_element(ne_xml_parser *p, struct ne_xml_state *state,
+ const ne_xml_char *name, const ne_xml_char **atts);
+
+#ifdef HAVE_LIBXML
+
+/* Could be const as far as we care, but libxml doesn't want that */
+static xmlSAXHandler sax_handler = {
+ NULL, /* internalSubset */
+ NULL, /* isStandalone */
+ NULL, /* hasInternalSubset */
+ NULL, /* hasExternalSubset */
+ NULL, /* resolveEntity */
+ NULL, /* getEntity */
+ NULL, /* entityDecl */
+ NULL, /* notationDecl */
+ NULL, /* attributeDecl */
+ NULL, /* elementDecl */
+ NULL, /* unparsedEntityDecl */
+ NULL, /* setDocumentLocator */
+ NULL, /* startDocument */
+ NULL, /* endDocument */
+ start_element, /* startElement */
+ end_element, /* endElement */
+ NULL, /* reference */
+ char_data, /* characters */
+ NULL, /* ignorableWhitespace */
+ NULL, /* processingInstruction */
+ NULL, /* comment */
+ NULL, /* xmlParserWarning */
+ sax_error, /* xmlParserError */
+ NULL, /* xmlParserError */
+ NULL, /* getParameterEntity */
+ char_data /* cdataBlock */
+};
+
+#endif /* HAVE_LIBXML */
+
+#ifdef NE_XML_DECODE_UTF8
+
+static inline int
+decode_utf8_double(char *dest, const char *src)
+{
+ /* From utf-8 man page; two-byte encoding is:
+ * 0x00000080 - 0x000007FF:
+ * 110xxxxx 10xxxxxx
+ * If more than 8-bits of those x's are set, we fail.
+ * So, we check that the first 6 bits of the first byte are:
+ * 110000.
+ * Then decode like:
+ * 110000xx 10yyyyyy -> xxyyyyyy
+ * Do this with a mask and a compare:
+ * zzzzzzzz
+ * & 11111100 <=> 0xFC
+ * == 11000000 <=> 0xC0
+ *
+ * joe: A real C hacker would probably do some funky bit
+ * inversion, and turn this into an is-not-zero test,
+ * but I'm a fake, so...
+ */
+ if ((src[0] & 0xFC) == 0xC0) {
+ dest[0] = ((src[0] & 0x03) << 6) | (src[1] & 0x3F);
+ /* nb.
+ * 00000011 = 0x03
+ * 00111111 = 0x3F
+ */
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+#endif
+
+int ne_xml_currentline(ne_xml_parser *p)
+{
+#ifdef HAVE_EXPAT
+ return XML_GetCurrentLineNumber(p->parser);
+#else
+ return p->parser->input->line;
+#endif
+}
+
+static int find_handler(ne_xml_parser *p, struct ne_xml_state *state)
+{
+ struct ne_xml_handler *cur, *unk_handler = NULL;
+ const char *name = state->elm_real.name, *nspace = state->elm_real.nspace;
+ int n, got_unknown = 0;
+
+ for (cur = state->parent->handler; cur != NULL; cur = cur->next) {
+ for (n = 0; (cur->elements[n].nspace != NULL || (
+ cur->elements[n].nspace == NULL &&
+ cur->elements[n].id == NE_ELM_unknown)); n++) {
+ if (cur->elements[n].nspace != NULL &&
+ (strcasecmp(cur->elements[n].name, name) == 0 &&
+ strcasecmp(cur->elements[n].nspace, nspace) == 0)) {
+
+ switch ((*cur->validate_cb)(state->parent->elm->id, cur->elements[n].id)) {
+ case NE_XML_VALID:
+ NE_DEBUG(NE_DBG_XML, "Validated by handler.\n");
+ state->handler = cur;
+ state->elm = &cur->elements[n];
+ return 0;
+ case NE_XML_INVALID:
+ NE_DEBUG(NE_DBG_XML, "Invalid context.\n");
+ snprintf(p->error, ERR_SIZE,
+ _("XML is not valid (%s found in parent %s)"),
+ friendly_name(&cur->elements[n]),
+ friendly_name(state->parent->elm));
+ return -1;
+ default:
+ /* ignore it */
+ NE_DEBUG(NE_DBG_XML, "Declined by handler.\n");
+ break;
+ }
+ }
+ if (!got_unknown && cur->elements[n].id == NE_ELM_unknown) {
+ switch ((*cur->validate_cb)(state->parent->elm->id, NE_ELM_unknown)) {
+ case NE_XML_VALID:
+ unk_handler = cur;
+ got_unknown = 1;
+ state->elm_real.id = NE_ELM_unknown;
+ state->elm_real.flags = cur->elements[n].flags;
+ break;
+ case NE_XML_INVALID:
+ NE_DEBUG(NE_DBG_XML, "Invalid context.\n");
+ snprintf(p->error, ERR_SIZE,
+ _("XML is not valid (%s found in parent %s)"),
+ friendly_name(&cur->elements[n]),
+ friendly_name(state->parent->elm));
+ return -1;
+ default:
+ NE_DEBUG(NE_DBG_XML, "Declined by handler.\n");
+ break;
+ }
+ }
+ }
+ }
+ if (!cur && got_unknown) {
+ /* Give them the unknown handler */
+ NE_DEBUG(NE_DBG_XMLPARSE, "Using unknown element handler\n");
+ state->handler = unk_handler;
+ state->elm = &state->elm_real;
+ return 0;
+ } else {
+ NE_DEBUG(NE_DBG_XMLPARSE, "Unexpected element\n");
+ snprintf(p->error, ERR_SIZE,
+ _("Unknown XML element `%s (in %s)'"), name, nspace);
+ return -1;
+ }
+}
+
+/* Called with the start of a new element. */
+static void
+start_element(void *userdata, const ne_xml_char *name, const ne_xml_char **atts)
+{
+ ne_xml_parser *p = userdata;
+ struct ne_xml_state *s;
+
+ if (!p->valid) {
+ /* We've stopped parsing */
+ NE_DEBUG(NE_DBG_XML, "Parse died. Ignoring start of element: %s\n", name);
+ return;
+ }
+
+ /* If we are in collect mode, print the element to the buffer */
+ if (p->collect) {
+ /* In Collect Mode. */
+ const ne_xml_char *pnt = strchr(name, ':');
+ if (pnt == NULL) {
+ pnt = name;
+ } else {
+ pnt++;
+ }
+ ne_buffer_concat(p->buffer, "<", pnt, NULL);
+ if (atts != NULL) {
+ int n;
+ for (n = 0; atts[n] != NULL; n+=2) {
+ ne_buffer_concat(p->buffer, " ", atts[n], "=\"", atts[n+1], "\"",
+ NULL);
+ }
+ }
+ ne_buffer_zappend(p->buffer, ">");
+ /* One deeper */
+ p->collect++;
+ return;
+ }
+
+ /* Set the new state */
+ s = ne_calloc(sizeof(struct ne_xml_state));
+ s->parent = p->current;
+ p->current = s;
+
+ /* We need to handle namespaces ourselves */
+ if (parse_element(p, s, name, atts)) {
+ /* it bombed. */
+ p->valid = 0;
+ return;
+ }
+
+ /* Map the element name to an id */
+ NE_DEBUG(NE_DBG_XML, "Mapping element name %s@@%s... ",
+ s->elm_real.nspace, s->elm_real.name);
+
+ if (find_handler(p, s)) {
+ p->valid = 0;
+ return;
+ }
+
+ NE_DEBUG(NE_DBG_XMLPARSE, "mapped to id %d\n", s->elm->id);
+
+ /* Do we want cdata? */
+ p->want_cdata = ((s->elm->flags & NE_XML_CDATA) == NE_XML_CDATA);
+ p->collect = ((s->elm->flags & NE_XML_COLLECT) == NE_XML_COLLECT);
+
+ /* Is this element using mixed-mode? */
+ s->mixed = ((s->elm->flags & NE_XML_MIXED) == NE_XML_MIXED);
+
+ if (s->handler->startelm_cb) {
+ if ((*s->handler->startelm_cb)(s->handler->userdata, s->elm,
+ (const char **) atts)) {
+ NE_DEBUG(NE_DBG_XML, "Startelm callback failed.\n");
+ p->valid = 0;
+ }
+ } else {
+ NE_DEBUG(NE_DBG_XML, "No startelm handler.\n");
+ }
+
+}
+
+/* Destroys given state */
+static void destroy_state(struct ne_xml_state *s)
+{
+ struct ne_xml_nspace *this_ns, *next_ns;
+ NE_DEBUG(NE_DBG_XMLPARSE, "Freeing namespaces...\n");
+ NE_FREE(s->default_ns);
+ NE_FREE(s->real_name);
+ /* Free the namespaces */
+ this_ns = s->nspaces;
+ while (this_ns != NULL) {
+ next_ns = this_ns->next;
+ free(this_ns->name);
+ free(this_ns->uri);
+ free(this_ns);
+ this_ns = next_ns;
+ };
+ NE_DEBUG(NE_DBG_XMLPARSE, "Finished freeing namespaces.\n");
+ free(s);
+}
+
+static void char_data(void *userdata, const ne_xml_char *data, int len)
+{
+ ne_xml_parser *p = userdata;
+
+ if (p->current->mixed) {
+ (*p->current->handler->cdata_cb)(
+ p->current->handler->userdata, p->current->elm, data, len);
+ return;
+ }
+
+ if (!p->want_cdata || !p->valid) return;
+ /* First, if this is the beginning of the CDATA, skip all
+ * leading whitespace, we don't want it. */
+ NE_DEBUG(NE_DBG_XMLPARSE, "Given %d bytes of cdata.\n", len);
+ if (ne_buffer_size(p->buffer) == 0) {
+ int wslen = 0;
+ /* Ignore any leading whitespace */
+ while (wslen < len &&
+ (data[wslen] == ' ' || data[wslen] == '\r' ||
+ data[wslen] == '\n' || data[wslen] == '\t')) {
+ wslen++;
+ }
+ data += wslen;
+ len -= wslen;
+ NE_DEBUG(NE_DBG_XMLPARSE, "Skipped %d bytes of leading whitespace.\n",
+ wslen);
+ if (len == 0) {
+ NE_DEBUG(NE_DBG_XMLPARSE, "Zero bytes of content.\n");
+ return;
+ }
+ }
+
+#ifdef NE_XML_DECODE_UTF8
+
+ if ((p->current->elm->flags & NE_XML_UTF8DECODE) == NE_XML_UTF8DECODE) {
+ int n, m, clen;
+ char *dest;
+
+ clen = ne_buffer_size(p->buffer);
+ ne_buffer_grow(p->buffer, clen + len + 1);
+ dest = p->buffer->data + clen;
+
+/* #define TOO_MUCH_DEBUG 1 */
+ for (n = 0, m = 0; n < len; n++, m++) {
+#ifdef TOO_MUCH_DEBUG
+ NE_DEBUG(NE_DBG_XML, "decoding 0x%02x", 0xFF & data[n]);
+#endif
+ if (SINGLEBYTE_UTF8(data[n])) {
+ dest[m] = data[n];
+ } else {
+ /* An optimisation here: we only deal with 8-bit
+ * data, which will be encoded as two bytes of UTF-8 */
+ if ((len - n < 2) ||
+ decode_utf8_double(&dest[m], &data[n])) {
+ /* Failed to decode! */
+ NE_DEBUG(NE_DBG_XML, "Could not decode UTF-8 data.\n");
+ strcpy(p->error, "XML parser received non-8-bit data");
+ p->valid = 0;
+ return;
+ } else {
+#ifdef TOO_MUCH_DEBUG
+ NE_DEBUG(NE_DBG_XML, "UTF-8 two-bytes decode: "
+ "0x%02hx 0x%02hx -> 0x%02hx!\n",
+ data[n] & 0xFF, data[n+1] & 0xFF, dest[m] & 0xFF);
+#endif
+ /* Skip the second byte */
+ n++;
+ }
+ }
+ }
+ ne_buffer_altered(p->buffer);
+ } else {
+ ne_buffer_append(p->buffer, data, len);
+ }
+
+#else /* !NE_XML_DECODE_UTF8 */
+
+ ne_buffer_append(p->buffer, data, len);
+
+#endif
+
+}
+
+/* Called with the end of an element */
+static void end_element(void *userdata, const ne_xml_char *name)
+{
+ ne_xml_parser *p = userdata;
+ struct ne_xml_state *s = p->current;
+ if (!p->valid) {
+ /* We've stopped parsing */
+ NE_DEBUG(NE_DBG_XML, "Parse died. Ignoring end of element: %s\n", name);
+ return;
+ }
+ if (p->collect > 0) {
+ if (--p->collect) {
+ const ne_xml_char *pnt = strchr(name, ':');
+ if (pnt == NULL) {
+ pnt = name;
+ } else {
+ pnt++;
+ }
+ ne_buffer_concat(p->buffer, "</", pnt, ">", NULL);
+ return;
+ }
+ }
+
+ /* process it */
+ if (s->handler->endelm_cb) {
+ NE_DEBUG(NE_DBG_XMLPARSE, "Calling endelm callback for %s.\n", s->elm->name);
+ if ((*s->handler->endelm_cb)(s->handler->userdata, s->elm,
+ p->want_cdata?p->buffer->data:NULL)) {
+ NE_DEBUG(NE_DBG_XML, "Endelm callback failed.\n");
+ p->valid = 0;
+ }
+ }
+ p->current = s->parent;
+ /* Move the current pointer up the branch */
+ NE_DEBUG(NE_DBG_XML, "Back in element: %s\n", friendly_name(p->current->elm));
+ if (p->want_cdata) {
+ ne_buffer_clear(p->buffer);
+ }
+ destroy_state(s);
+}
+
+/* Parses the attributes, and handles XML namespaces.
+ * With a little bit of luck.
+ * Returns:
+ * the element name on success
+ * or NULL on error.
+ */
+static int parse_element(ne_xml_parser *p, struct ne_xml_state *state,
+ const ne_xml_char *name, const ne_xml_char **atts)
+{
+ struct ne_xml_nspace *ns;
+ const ne_xml_char *pnt;
+ struct ne_xml_state *xmlt;
+
+ NE_DEBUG(NE_DBG_XMLPARSE, "Parsing elm of name: [%s]\n", name);
+ /* Parse the atts for namespace declarations... if we have any atts.
+ * expat will never pass us atts == NULL, but libxml will. */
+ if (atts != NULL) {
+ int attn;
+ for (attn = 0; atts[attn]!=NULL; attn+=2) {
+ NE_DEBUG(NE_DBG_XMLPARSE, "Got attribute: [%s] = [%s]\n", atts[attn], atts[attn+1]);
+ if (strcasecmp(atts[attn], "xmlns") == 0) {
+ /* New default namespace */
+ state->default_ns = ne_strdup(atts[attn+1]);
+ NE_DEBUG(NE_DBG_XMLPARSE, "New default namespace: %s\n",
+ state->default_ns);
+ } else if (strncasecmp(atts[attn], "xmlns:", 6) == 0) {
+ /* New namespace scope */
+ ns = ne_calloc(sizeof(struct ne_xml_nspace));
+ ns->next = state->nspaces;
+ state->nspaces = ns;
+ ns->name = ne_strdup(atts[attn]+6); /* skip the xmlns= */
+ ns->uri = ne_strdup(atts[attn+1]);
+ NE_DEBUG(NE_DBG_XMLPARSE, "New namespace scope: %s -> %s\n",
+ ns->name, ns->uri);
+ }
+ }
+ }
+ /* Now check the elm name for a namespace scope */
+ pnt = strchr(name, ':');
+ if (pnt == NULL) {
+ /* No namespace prefix - have we got a default? */
+ state->real_name = ne_strdup(name);
+ NE_DEBUG(NE_DBG_XMLPARSE, "No prefix found, searching for default.\n");
+ for (xmlt = state; xmlt!=NULL; xmlt=xmlt->parent) {
+ if (xmlt->default_ns != NULL) {
+ state->elm_real.nspace = xmlt->default_ns;
+ break;
+ }
+ }
+ if (state->elm_real.nspace == NULL) {
+ NE_DEBUG(NE_DBG_XMLPARSE, "No default namespace, using empty.\n");
+ state->elm_real.nspace = "";
+ }
+ } else {
+ NE_DEBUG(NE_DBG_XMLPARSE, "Got namespace scope. Trying to resolve...");
+ /* Have a scope - resolve it */
+ for (xmlt = state; state->elm_real.nspace==NULL && xmlt!=NULL; xmlt=xmlt->parent) {
+ for (ns = xmlt->nspaces; ns!=NULL; ns=ns->next) {
+ /* Just compare against the bit before the :
+ * pnt points to the colon. */
+ if (strncasecmp(ns->name, name, pnt-name) == 0) {
+ /* Scope matched! Hoorah */
+ state->elm_real.nspace = ns->uri;
+ /* end the search */
+ break;
+ }
+ }
+ }
+ if (state->elm_real.nspace != NULL) {
+ NE_DEBUG(NE_DBG_XMLPARSE, "Resolved prefix to [%s]\n", state->elm_real.nspace);
+ /* The name is everything after the ':' */
+ if (pnt[1] == '\0') {
+ snprintf(p->error, ERR_SIZE,
+ "Element name missing in '%s' at line %d.",
+ name, ne_xml_currentline(p));
+ NE_DEBUG(NE_DBG_XMLPARSE, "No element name after ':'. Failed.\n");
+ return -1;
+ }
+ state->real_name = ne_strdup(pnt+1);
+ } else {
+ NE_DEBUG(NE_DBG_XMLPARSE, "Undeclared namespace.\n");
+ snprintf(p->error, ERR_SIZE,
+ "Undeclared namespace in '%s' at line %d.",
+ name, ne_xml_currentline(p));
+ return -1;
+ }
+ }
+ state->elm_real.name = state->real_name;
+ return 0;
+}
+
+ne_xml_parser *ne_xml_create(void)
+{
+ ne_xml_parser *p = ne_calloc(sizeof *p);
+ /* Initialize other stuff */
+ p->valid = 1;
+ /* Placeholder for the root element */
+ p->current = p->root = ne_calloc(sizeof(struct ne_xml_state));
+ p->root->elm = &root_element;
+ /* Initialize the cdata buffer */
+ p->buffer = ne_buffer_create();
+#ifdef HAVE_EXPAT
+ p->parser = XML_ParserCreate(NULL);
+ if (p->parser == NULL) {
+ abort();
+ }
+ XML_SetElementHandler(p->parser, start_element, end_element);
+ XML_SetCharacterDataHandler(p->parser, char_data);
+ XML_SetUserData(p->parser, (void *) p);
+#else
+ p->parser = xmlCreatePushParserCtxt(&sax_handler,
+ (void *)p, NULL, 0, NULL);
+ if (p->parser == NULL) {
+ abort();
+ }
+#endif
+ return p;
+}
+
+static void push_handler(ne_xml_parser *p,
+ struct ne_xml_handler *handler)
+{
+
+ /* If this is the first handler registered, update the
+ * base pointer too. */
+ if (p->top_handlers == NULL) {
+ p->root->handler = handler;
+ p->top_handlers = handler;
+ } else {
+ p->top_handlers->next = handler;
+ p->top_handlers = handler;
+ }
+}
+
+void ne_xml_push_handler(ne_xml_parser *p,
+ const struct ne_xml_elm *elements,
+ ne_xml_validate_cb validate_cb,
+ ne_xml_startelm_cb startelm_cb,
+ ne_xml_endelm_cb endelm_cb,
+ void *userdata)
+{
+ struct ne_xml_handler *hand = ne_calloc(sizeof(struct ne_xml_handler));
+
+ hand->elements = elements;
+ hand->validate_cb = validate_cb;
+ hand->startelm_cb = startelm_cb;
+ hand->endelm_cb = endelm_cb;
+ hand->userdata = userdata;
+
+ push_handler(p, hand);
+}
+
+void ne_xml_push_mixed_handler(ne_xml_parser *p,
+ const struct ne_xml_elm *elements,
+ ne_xml_validate_cb validate_cb,
+ ne_xml_startelm_cb startelm_cb,
+ ne_xml_cdata_cb cdata_cb,
+ ne_xml_endelm_cb endelm_cb,
+ void *userdata)
+{
+ struct ne_xml_handler *hand = ne_calloc(sizeof *hand);
+
+ hand->elements = elements;
+ hand->validate_cb = validate_cb;
+ hand->startelm_cb = startelm_cb;
+ hand->cdata_cb = cdata_cb;
+ hand->endelm_cb = endelm_cb;
+ hand->userdata = userdata;
+
+ push_handler(p, hand);
+}
+
+void ne_xml_parse_v(void *userdata, const char *block, size_t len)
+{
+ ne_xml_parser *p = userdata;
+ /* FIXME: The two XML parsers break all our nice abstraction by
+ * choosing different char *'s. The swine. This cast will come
+ * back and bite us someday, no doubt. */
+ ne_xml_parse(p, block, len);
+}
+
+/* Parse the given block of input of length len */
+void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len)
+{
+ int ret, flag;
+ /* duck out if it's broken */
+ if (!p->valid) {
+ NE_DEBUG(NE_DBG_XML, "Not parsing %d bytes.\n", len);
+ return;
+ }
+ if (len == 0) {
+ flag = -1;
+ block = "";
+ NE_DEBUG(NE_DBG_XML, "Got 0-length buffer, end of document.\n");
+ } else {
+ NE_DEBUG(NE_DBG_XML, "Parsing %d length buffer.\n", len);
+ flag = 0;
+ }
+ /* Note, don't write a parser error if !p->valid, since an error
+ * will already have been written in that case. */
+#ifdef HAVE_EXPAT
+ ret = XML_Parse(p->parser, block, len, flag);
+ NE_DEBUG(NE_DBG_XMLPARSE, "XML_Parse returned %d\n", ret);
+ if (ret == 0 && p->valid) {
+ snprintf(p->error, ERR_SIZE,
+ "XML parse error at line %d: %s",
+ XML_GetCurrentLineNumber(p->parser),
+ XML_ErrorString(XML_GetErrorCode(p->parser)));
+ p->valid = 0;
+ }
+#else
+ ret = xmlParseChunk(p->parser, block, len, flag);
+ NE_DEBUG(NE_DBG_XMLPARSE, "xmlParseChunk returned %d\n", ret);
+ if (p->parser->errNo && p->valid) {
+ /* FIXME: error handling */
+ snprintf(p->error, ERR_SIZE, "XML parse error at line %d.",
+ ne_xml_currentline(p));
+ p->valid = 0;
+ }
+#endif
+}
+
+int ne_xml_valid(ne_xml_parser *p)
+{
+ return p->valid;
+}
+
+void ne_xml_destroy(ne_xml_parser *p)
+{
+ struct ne_xml_state *s, *parent;
+ struct ne_xml_handler *hand, *next;
+
+ ne_buffer_destroy(p->buffer);
+
+ /* Free up the handlers on the stack: the root element has the
+ * pointer to the base of the handler stack. */
+ for (hand = p->root->handler; hand!=NULL; hand=next) {
+ next = hand->next;
+ free(hand);
+ }
+
+ /* Clean up any states which may remain.
+ * If p.valid, then this should be only the root element. */
+ for (s = p->current; s!=NULL; s=parent) {
+ parent = s->parent;
+ destroy_state(s);
+ }
+
+#ifdef HAVE_EXPAT
+ XML_ParserFree(p->parser);
+#else
+ xmlFreeParserCtxt(p->parser);
+#endif
+
+ free(p);
+}
+
+void ne_xml_set_error(ne_xml_parser *p, const char *msg)
+{
+ snprintf(p->error, ERR_SIZE, msg);
+}
+
+#ifdef HAVE_LIBXML
+static void sax_error(void *ctx, const char *msg, ...)
+{
+ ne_xml_parser *p = ctx;
+ va_list ap;
+ char buf[1024];
+
+ va_start(ap, msg);
+ vsnprintf(buf, 1024, msg, ap);
+ va_end(ap);
+
+ snprintf(p->error, ERR_SIZE,
+ _("XML parse error at line %d: %s."),
+ p->parser->input->line, buf);
+
+ p->valid = 0;
+}
+#endif
+
+const char *ne_xml_get_error(ne_xml_parser *p)
+{
+ return p->error;
+}
+
+
+const char *ne_xml_get_attr(const char **attrs, const char *name)
+{
+ int n;
+
+#ifdef HAVE_LIBXML
+ /* libxml passes a NULL attribs array to the startelm callback if
+ * there are no attribs on the element. expat passes a
+ * zero-length array. */
+ if (attrs == NULL) {
+ return NULL;
+ }
+#endif
+
+ for (n = 0; attrs[n] != NULL; n += 2) {
+ if (strcmp(attrs[n], name) == 0) {
+ return attrs[n+1];
+ }
+ }
+
+ return NULL;
+}
diff --git a/neon/src/ne_xml.h b/neon/src/ne_xml.h
new file mode 100644
index 000000000..2d2c36585
--- /dev/null
+++ b/neon/src/ne_xml.h
@@ -0,0 +1,184 @@
+/*
+ Higher Level Interface to XML Parsers.
+ Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_XML_H
+#define NE_XML_H
+
+BEGIN_NEON_DECLS
+
+/* Generic XML parsing interface...
+
+ Definitions:
+
+ * A handler knows how to parse a certain set of XML elements.
+ * The handler stack
+
+ *
+
+
+ Basic principle is that you provide these things:
+ 1) a list of elements which you wish to handle
+ 2) a validation callback which tells the parser whether you
+ want to handle this element.
+ 3) a callback function which is called when a complete element
+ has been parsed (provided you are handling the element).
+
+ This code then deals with the boring stuff like:
+ - Dealing with XML namespaces
+ - Collecting CDATA
+
+ The element list is stored in a 'ne_xml_elm' array. For each
+ element, you specify the element name, an element id, and some
+ flags for that element.
+
+const static struct ne_xml_elm[] = {
+ { "DAV:", "resourcetype", ELM_resourcetype, 0 },
+ { "DAV:", "collection", ELM_collection, NE_ELM_CDATA },
+ { NULL }
+};
+
+ This list contains two elements, resourcetype and collection, both in
+ the "DAV:" namespace. The collection element can contain cdata.
+
+*/
+
+/* Reserved element id's */
+#define NE_ELM_unknown -1
+#define NE_ELM_root 0
+
+#define NE_ELM_UNUSED (100)
+
+typedef int ne_xml_elmid;
+
+struct ne_xml_elm;
+
+/* An element */
+struct ne_xml_elm {
+ const char *nspace, *name;
+ ne_xml_elmid id;
+ unsigned int flags;
+};
+
+/* Function to check element context...
+ This callback must return:
+ NE_XML_VALID ->
+ Yes, this is valid XML, and I want to handle this element.
+ NE_XML_INVALID ->
+ No, this is NOT valid XML, and parsing should stop.
+ NE_XML_DECLINE ->
+ I don't know anything about this element, someone else
+ can handle it.
+*/
+
+
+#define NE_XML_VALID (0)
+#define NE_XML_INVALID (-1)
+#define NE_XML_DECLINE (-2)
+
+/* Validate a new child element. */
+typedef int (*ne_xml_validate_cb)
+ (ne_xml_elmid parent, ne_xml_elmid child);
+
+typedef int (*ne_xml_startelm_cb)
+ (void *userdata, const struct ne_xml_elm *elm, const char **atts);
+
+/* Called when a complete element is parsed */
+typedef int (*ne_xml_endelm_cb)
+ (void *userdata, const struct ne_xml_elm *s, const char *cdata);
+
+typedef void (*ne_xml_cdata_cb)
+ (void *userdata, const struct ne_xml_elm *s,
+ const char *cdata, int len);
+
+struct ne_xml_parser_s;
+typedef struct ne_xml_parser_s ne_xml_parser;
+
+/* Flags */
+/* This element has no children */
+#define NE_XML_CDATA (1<<1)
+/* Collect complete contents of this node as cdata */
+#define NE_XML_COLLECT ((1<<2) | NE_XML_CDATA)
+/* Decode UTF-8 data in cdata. */
+#define NE_XML_UTF8DECODE (1<<3)
+/* This element uses MIXED mode */
+#define NE_XML_MIXED (1<<4)
+
+/* Initialise the parser */
+ne_xml_parser *ne_xml_create(void);
+
+/* Push a handler onto the handler stack for the given list of elements.
+ * elements must be an array, with the last element .nspace being NULL.
+ * Callbacks are called in order:
+ * 1. validate_cb
+ * 2. startelm_cb
+ * 3. endelm_cb
+ * (then back to the beginning again).
+ * If any of the callbacks ever return non-zero, the parse will STOP.
+ * userdata is passed as the first argument to startelm_cb and endelm_cb.
+ */
+void ne_xml_push_handler(ne_xml_parser *p,
+ const struct ne_xml_elm *elements,
+ ne_xml_validate_cb validate_cb,
+ ne_xml_startelm_cb startelm_cb,
+ ne_xml_endelm_cb endelm_cb,
+ void *userdata);
+
+/* Add a handler which uses a mixed-mode cdata callback */
+void ne_xml_push_mixed_handler(ne_xml_parser *p,
+ const struct ne_xml_elm *elements,
+ ne_xml_validate_cb validate_cb,
+ ne_xml_startelm_cb startelm_cb,
+ ne_xml_cdata_cb cdata_cb,
+ ne_xml_endelm_cb endelm_cb,
+ void *userdata);
+
+/* Returns non-zero if the parse was valid, zero if it failed (e.g.,
+ * any of the callbacks return non-zero, the XML was not well-formed,
+ * etc). Use ne_xml_get_error to retrieve the error message if it
+ * failed. */
+int ne_xml_valid(ne_xml_parser *p);
+
+/* Destroys the parser. Any operations on it then have
+ * undefined results. */
+void ne_xml_destroy(ne_xml_parser *p);
+
+/* Parse the given block of input of length len. Block does
+ * not need to be NULL-terminated. */
+void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len);
+
+/* As above, casting (ne_xml_parser *)userdata internally.
+ * (This function can be passed to http_add_response_body_reader) */
+void ne_xml_parse_v(void *userdata, const char *block, size_t len);
+
+/* Return current parse line for errors */
+int ne_xml_currentline(ne_xml_parser *p);
+
+/* Set error message for parser */
+void ne_xml_set_error(ne_xml_parser *p, const char *msg);
+
+const char *ne_xml_get_error(ne_xml_parser *p);
+
+/* Get attribute of given name. TODO: doesn't consider namespaces. */
+const char *ne_xml_get_attr(const char **attrs, const char *name);
+
+END_NEON_DECLS
+
+#endif /* NE_XML_H */
diff --git a/neon/src/neon.h b/neon/src/neon.h
new file mode 100644
index 000000000..4ebf94722
--- /dev/null
+++ b/neon/src/neon.h
@@ -0,0 +1,3 @@
+
+#undef NEON_VERSION
+#define NEON_VERSION "0.1.0"
diff --git a/neon/src/neon_config.h b/neon/src/neon_config.h
new file mode 100644
index 000000000..d691bd545
--- /dev/null
+++ b/neon/src/neon_config.h
@@ -0,0 +1,8 @@
+
+#ifndef NEON_CONFIG_H
+#define NEON_CONFIG_H
+
+#undef NEON_VERSION
+#define NEON_VERSION "0.2.0"
+
+#endif
diff --git a/neon/src/neon_defs.h b/neon/src/neon_defs.h
new file mode 100644
index 000000000..f029edf28
--- /dev/null
+++ b/neon/src/neon_defs.h
@@ -0,0 +1,10 @@
+
+#undef BEGIN_NEON_DECLS
+#undef END_NEON_DECLS
+#ifdef __cplusplus
+# define BEGIN_NEON_DECLS extern "C" {
+# define END_NEON_DECLS }
+#else
+# define BEGIN_NEON_DECLS /* empty */
+# define END_NEON_DECLS /* empty */
+#endif
diff --git a/neon/src/neon_i18n.c b/neon/src/neon_i18n.c
new file mode 100644
index 000000000..509ca9dba
--- /dev/null
+++ b/neon/src/neon_i18n.c
@@ -0,0 +1,30 @@
+/*
+ Internationalization of neon
+ Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: neon_i18n.c,v 1.2 2000/07/16 15:45:04 joe Exp
+*/
+
+void neon_i18n_init(void)
+{
+#if defined(ENABLE_NLS) && defined(NEON_IS_LIBRARY)
+ bindtextdomain("neon", LOCALEDIR);
+ /* if neon is build bundled in, then there is probably
+ * no point in this... probably. */
+#endif
+}
diff --git a/neon/src/neon_i18n.h b/neon/src/neon_i18n.h
new file mode 100644
index 000000000..5d82eacb8
--- /dev/null
+++ b/neon/src/neon_i18n.h
@@ -0,0 +1,11 @@
+
+/* Include this to use I18N inside neon */
+
+#undef _
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(str) gettext(str)
+#else
+#define _(str) (str)
+#endif /* ENABLE_NLS */
+#define N_(str) (str)
diff --git a/neon/src/neon_md5.h b/neon/src/neon_md5.h
new file mode 100644
index 000000000..6ff784c5e
--- /dev/null
+++ b/neon/src/neon_md5.h
@@ -0,0 +1,157 @@
+/* Declaration of functions and data types used for MD5 sum computing
+ library functions.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef NEON_MD5_H
+#define NEON_MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+ to determine an unsigned integral type that is 32 bits wide. An
+ alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+ doing that would require that the configure script compile and *run*
+ the resulting executable. Locally running cross-compiled executables
+ is usually not possible. */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+# define UINT_MAX_32_BITS 4294967295U
+# else
+# define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+ This should be valid for all systems GNU cares about because
+ that doesn't include 16-bit systems, and only modern systems
+ (that certainly have <limits.h>) have 64+-bit integral types. */
+
+# ifndef UINT_MAX
+# define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+ typedef unsigned int md5_uint32;
+# else
+# if USHRT_MAX == UINT_MAX_32_BITS
+ typedef unsigned short md5_uint32;
+# else
+# if ULONG_MAX == UINT_MAX_32_BITS
+ typedef unsigned long md5_uint32;
+# else
+ /* The following line is intended to evoke an error.
+ Using #error is not portable enough. */
+ "Cannot determine unsigned 32-bit data type."
+# endif
+# endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+# define __P(x) x
+#else
+# define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps. */
+struct md5_ctx
+{
+ md5_uint32 A;
+ md5_uint32 B;
+ md5_uint32 C;
+ md5_uint32 D;
+
+ md5_uint32 total[2];
+ md5_uint32 buflen;
+ char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+extern void __md5_init_ctx __P ((struct md5_ctx *ctx));
+extern void md5_init_ctx __P ((struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void __md5_process_block __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+extern void md5_process_block __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void __md5_process_bytes __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+extern void md5_process_bytes __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 16 bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *__md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *__md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+extern int __md5_stream __P ((FILE *stream, void *resblock));
+extern int md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+extern void *__md5_buffer __P ((const char *buffer, size_t len,
+ void *resblock));
+extern void *md5_buffer __P ((const char *buffer, size_t len,
+ void *resblock));
+
+#endif /* NEON_MD5_H */
diff --git a/neon/src/nsocket.h b/neon/src/nsocket.h
new file mode 100644
index 000000000..dd5c30a14
--- /dev/null
+++ b/neon/src/nsocket.h
@@ -0,0 +1,192 @@
+/*
+ socket handling interface
+ Copyright (C) 1998-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NSOCKET_H
+#define NSOCKET_H
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+
+#include <neon_defs.h>
+
+BEGIN_NEON_DECLS
+
+#define SOCK_ERROR -1
+/* Read/Write timed out */
+#define SOCK_TIMEOUT -2
+/* Passed buffer was full */
+#define SOCK_FULL -3
+/* Socket was closed */
+#define SOCK_CLOSED -4
+
+/* Socket read timeout */
+#define SOCKET_READ_TIMEOUT 60
+
+typedef enum {
+ sock_namelookup, /* Looking up hostname given by info */
+ sock_connecting, /* Connecting to server */
+ sock_connected, /* Connection established */
+ sock_secure_details /* Secure connection details */
+} sock_status;
+
+struct nsocket_s;
+typedef struct nsocket_s nsocket;
+
+typedef void (*sock_block_reader) (
+ void *userdata, const char *buf, size_t len);
+
+typedef void (*sock_progress)(void *userdata, size_t progress, size_t total);
+typedef void (*sock_notify)(void *userdata,
+ sock_status status, const char *info);
+
+void sock_register_progress(sock_progress cb, void *userdata);
+void sock_register_notify(sock_notify cb, void *userdata);
+
+void sock_call_progress(size_t progress, size_t total);
+
+/* Initialize the socket library. If you don't do this, SSL WILL NOT WORK.
+ * Returns 0 on success, or non-zero on screwed up SSL library. */
+int sock_init(void);
+
+/* sock_read is read() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ * SOCK_* on error,
+ * 0 on no data to read (due to EOF),
+ * >0 length of data read into buffer.
+ */
+int sock_read(nsocket *sock, char *buffer, size_t count);
+
+/* sock_peek is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ * SOCK_* on error,
+ * 0 on no data to read (due to EOF),
+ * >0 length of data read into buffer.
+ */
+int sock_peek(nsocket *sock, char *buffer, size_t count);
+
+/* Blocks waiting for data on the given socket for the given time.
+ * Returns:
+ * SOCK_* on error,
+ * SOCK_TIMEOUT on no data within timeout,
+ * 0 if data arrived on the socket.
+ */
+int sock_block(nsocket *sock, int timeout);
+
+/* Reads readlen bytes from fd and writes to socket.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or SOCK_* on error.
+ */
+int sock_transfer(int fd, nsocket *sock, ssize_t readlen);
+
+/* Sends the given line to given socket, CRLF appended */
+int sock_sendline(nsocket *sock, const char *line);
+/* Sends the given block of data down the nsocket */
+int sock_fullwrite(nsocket *sock, const char *data, size_t length);
+/* Sends the null-terminated string down the given nsocket */
+int sock_send_string(nsocket *sock, const char *string);
+
+/* Reads a line from given nsocket */
+int sock_readline(nsocket *sock, char *line, int len);
+/* Reads a chunk of data. */
+int sock_fullread(nsocket *sock, char *buffer, int buflen);
+
+/* Creates and connects a nsocket */
+nsocket *sock_connect(const struct in_addr host, int portnum);
+
+nsocket *sock_connect_u(const struct in_addr addr, int portnum, int call_fe);
+
+/* Not as good as accept(2), missing parms 2+3.
+ * Addings parms 2+3 would probably mean passing socklen_t as an
+ * int then casting internally, since we don't really want to
+ * autogenerate the header file to be correct for the build platform.
+ */
+nsocket *sock_accept(int listener);
+
+/* Returns the file descriptor used for the socket */
+int sock_get_fd(nsocket *sock);
+
+/* Closes the socket and frees the nsocket object. */
+int sock_close(nsocket *socket);
+
+const char *sock_get_error(nsocket *sock);
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure. */
+int sock_name_lookup(const char *hostname, struct in_addr *addr);
+
+/* Returns the standard TCP port for the given service */
+int sock_service_lookup(const char *name);
+
+int sock_readfile_blocked(nsocket *socket, size_t length,
+ sock_block_reader reader, void *userdata);
+
+/* Auxiliary for use with SSL. */
+struct nssl_context_s;
+typedef struct nssl_context_s nssl_context;
+
+/* Netscape's prompts on getting a certificate which it doesn't
+ * recognize the CA for:
+ * 1. Hey, I don't recognize the CA for this cert.
+ * 2. Here is the certificate: for foo signed by BLAH,
+ * using encryption level BLEE
+ * 3. Allow: accept for this session only,
+ * don't accept
+ * accept forever
+ */
+nssl_context *sock_create_ssl_context(void);
+
+void sock_destroy_ssl_context(nssl_context *ctx);
+
+/* Callback to decide whether the user will accept the
+ * given certificate or not */
+typedef struct {
+ char *owner; /* Multi-line string describing owner of
+ * certificate */
+ char *issuer; /* As above for issuer of certificate */
+ /* Strings the certificate is valid between */
+ char *valid_from, *valid_till;
+ /* Certificate fingerprint */
+ char *fingerprint;
+} nssl_certificate;
+
+/* Returns:
+ * 0 -> User accepts the certificate
+ * non-zero -> user does NOT accept the certificate.
+ */
+typedef int (*nssl_accept)(void *userdata, const nssl_certificate *info);
+
+void sock_set_cert_accept(nssl_context *c,
+ nssl_accept accepter, void *userdata);
+void sock_set_certificate_file(nssl_context *c, const char *fname);
+
+void sock_disable_tlsv1(nssl_context *c);
+void sock_disable_sslv2(nssl_context *c);
+void sock_disable_sslv3(nssl_context *c);
+
+/* Ctx is OPTIONAL. If it is NULL, defaults are used. */
+int sock_make_secure(nsocket *sock, nssl_context *ctx);
+
+END_NEON_DECLS
+
+#endif /* NSOCKET_H */
diff --git a/neon/src/socket.c b/neon/src/socket.c
new file mode 100644
index 000000000..a4899ac28
--- /dev/null
+++ b/neon/src/socket.c
@@ -0,0 +1,592 @@
+/*
+ socket handling routines
+ Copyright (C) 1998, 1999, 2000, Joe Orton <joe@orton.demon.co.uk>,
+ except where otherwise indicated.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ The sock_readline() function is:
+
+ Copyright (c) 1999 Eric S. Raymond
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+ Id: socket.c,v 1.7 2000/05/10 13:24:42 joe Exp
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include "string_utils.h"
+#include "http_utils.h"
+#include "socket.h"
+
+static sock_progress progress_cb = NULL;
+static sock_notify notify_cb = NULL;
+static void *progress_ud, *notify_ud;
+
+void sock_register_progress( sock_progress cb, void *userdata )
+{
+ progress_cb = cb;
+ progress_ud = userdata;
+}
+
+void sock_register_notify( sock_notify cb, void *userdata )
+{
+ notify_cb = cb;
+ notify_ud = userdata;
+}
+
+void sock_call_progress( size_t progress, size_t total )
+{
+ if( progress_cb ) {
+ (*progress_cb)( progress_ud, progress, total );
+ }
+}
+
+/* sock_read is read() with a timeout of SOCKET_READ_TIMEOUT. */
+int sock_read( int sock, void *buffer, size_t count ) {
+ int ret;
+ ret = sock_block( sock, SOCKET_READ_TIMEOUT );
+ if( ret == 0 ) {
+ /* Got data */
+ do {
+ ret = read( sock, buffer, count );
+ } while( ret == -1 && errno == EINTR );
+ if( ret < 0 ) {
+ ret = SOCK_ERROR;
+ }
+ }
+ return ret;
+}
+
+/* sock_recv is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns length of data read or SOCK_* on error */
+int sock_recv( int sock, void *buffer, size_t count, unsigned int flags ) {
+ int ret;
+ ret = sock_block( sock, SOCKET_READ_TIMEOUT );
+ if( ret < 0 ) {
+ return ret;
+ }
+ /* Got data */
+ do {
+ ret = recv( sock, buffer, count, flags );
+ } while( ret == -1 && errno == EINTR );
+ if( ret < 0 ) {
+ ret = SOCK_ERROR;
+ }
+ return ret;
+}
+
+/* Blocks waiting for read input on the given socket for the given time.
+ * Returns:
+ * 0 if data arrived
+ * SOCK_TIMEOUT if data did not arrive before timeout
+ * SOCK_ERROR on error
+ */
+int sock_block( int sock, int timeout ) {
+ struct timeval tv;
+ fd_set fds;
+ int ret;
+
+ /* Init the fd set */
+ FD_ZERO( &fds );
+ FD_SET( sock, &fds );
+ /* Set the timeout */
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ do {
+ ret = select( sock+1, &fds, NULL, NULL, &tv );
+ } while( ret == -1 && errno == EINTR );
+
+ switch( ret ) {
+ case 0:
+ return SOCK_TIMEOUT;
+ case -1:
+ return SOCK_ERROR;
+ default:
+ return 0;
+ }
+}
+
+/* Send the given line down the socket with CRLF appended.
+ * Returns 0 on success or SOCK_* on failure. */
+int sock_sendline( int sock, const char *line ) {
+ char *buffer;
+ int ret;
+ CONCAT2( buffer, line, "\r\n" );
+ ret = sock_send_string( sock, buffer );
+ free( buffer );
+ return ret;
+}
+
+/* Reads from fd, passing blocks to reader, also calling
+ * fe_t_p.
+ * Returns 0 on success or SOCK_* on error. */
+int sock_readfile_blocked( int fd, size_t length,
+ sock_block_reader reader, void *userdata ) {
+ char buffer[BUFSIZ];
+ int ret;
+ size_t done = 0;
+ do {
+ ret = sock_read( fd, buffer, BUFSIZ );
+ if( ret < 0 ) {
+ return ret;
+ }
+ done += ret;
+ sock_call_progress( done, length );
+ (*reader)( userdata, buffer, ret );
+ } while( (done < length) && ret );
+ return 0;
+}
+
+
+/* Send a block of data down the given fd.
+ * Returns 0 on success or SOCK_* on failure */
+int sock_fullwrite( int fd, const char *data, size_t length ) {
+ ssize_t sent, wrote;
+ const char *pnt;
+
+ sent = 0;
+ pnt = data;
+ while( sent < length ) {
+ wrote = write( fd, pnt, length-sent );
+ if( wrote < 0 ) {
+ if( errno == EINTR ) {
+ continue;
+ } else if( errno == EPIPE ) {
+ return SOCK_CLOSED;
+ } else {
+ return SOCK_ERROR;
+ }
+ }
+ sent += wrote;
+ }
+ return 0;
+}
+
+/* Sends the given string down the given socket.
+ * Returns 0 on success or -1 on failure. */
+int sock_send_string( int sock, const char *data ) {
+ return sock_fullwrite( sock, data, strlen( data ) );
+}
+
+/* This is from from Eric Raymond's fetchmail (SockRead() in socket.c)
+ * since I wouldn't have a clue how to do it properly.
+ * This function is Copyright 1999 (C) Eric Raymond.
+ * Modifications Copyright 2000 (C) Joe Orton
+ */
+int sock_readline(int sock, char *buf, int len)
+{
+ char *newline, *bp = buf;
+ int n;
+
+ do {
+ /*
+ * The reason for these gymnastics is that we want two things:
+ * (1) to read \n-terminated lines,
+ * (2) to return the true length of data read, even if the
+ * data coming in has embedded NULS.
+ */
+#ifdef SSL_ENABLE
+
+ /* joe: of course, we don't actually have SSL support here yet,
+ * but this will be handy when that day comes. */
+
+ SSL *ssl;
+
+ if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
+ /* Hack alert! */
+ /* OK... SSL_peek works a little different from MSG_PEEK
+ Problem is that SSL_peek can return 0 if there
+ is no data currently available. If, on the other
+ hand, we loose the socket, we also get a zero, but
+ the SSL_read then SEGFAULTS! To deal with this,
+ we'll check the error code any time we get a return
+ of zero from SSL_peek. If we have an error, we bail.
+ If we don't, we read one character in SSL_read and
+ loop. This should continue to work even if they
+ later change the behavior of SSL_peek
+ to "fix" this problem... :-( */
+ if ((n = SSL_peek(ssl, bp, len)) < 0) {
+ return(-1);
+ }
+ if( 0 == n ) {
+ /* SSL_peek says no data... Does he mean no data
+ or did the connection blow up? If we got an error
+ then bail! */
+ if( 0 != ( n = ERR_get_error() ) ) {
+ return -1;
+ }
+ /* We didn't get an error so read at least one
+ character at this point and loop */
+ n = 1;
+ /* Make sure newline start out NULL!
+ * We don't have a string to pass through
+ * the strchr at this point yet */
+ newline = NULL;
+ } else if ((newline = memchr(bp, '\n', n)) != NULL)
+ n = newline - bp + 1;
+ if ((n = SSL_read(ssl, bp, n)) == -1) {
+ return(-1);
+ }
+ /* Check for case where our single character turned out to
+ * be a newline... (It wasn't going to get caught by
+ * the strchr above if it came from the hack... ). */
+ if( NULL == newline && 1 == n && '\n' == *bp ) {
+ /* Got our newline - this will break
+ out of the loop now */
+ newline = bp;
+ }
+ } else {
+ if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
+ return(-1);
+ if ((newline = memchr(bp, '\n', n)) != NULL)
+ n = newline - bp + 1;
+ if ((n = read(sock, bp, n)) == -1)
+ return(-1);
+ }
+#else
+ if ((n = sock_recv(sock, bp, len, MSG_PEEK)) <= 0)
+ return n;
+ if ((newline = memchr(bp, '\n', n)) != NULL)
+ n = newline - bp + 1;
+ if ((n = sock_read(sock, bp, n)) < 0)
+ return n;
+#endif
+ bp += n;
+ len -= n;
+ if( len < 1 ) return SOCK_FULL;
+ } while (!newline && len);
+ *bp = '\0';
+ return bp - buf;
+}
+
+/*** End of ESR-copyrighted section ***/
+
+/* Reads readlen bytes from srcfd and writes to destfd.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or -1 on error.
+ */
+int sock_transfer( int srcfd, int destfd, ssize_t readlen ) {
+ char buffer[BUFSIZ], *pnt;
+ size_t curlen; /* total bytes yet to read from srcfd */
+ size_t sumwrlen; /* total bytes written to destfd */
+
+ if( readlen == -1 ) {
+ curlen = BUFSIZ; /* so the buffer size test works */
+ } else {
+ curlen = readlen; /* everything to do */
+ }
+ sumwrlen = 0; /* nowt done yet */
+
+ while( curlen > 0 ) {
+ int rdlen, len2;
+
+ /* Get a chunk... if the number of bytes that are left to read
+ * is less than the buffer size, only read that many bytes. */
+ rdlen = sock_read( srcfd, buffer,
+ (readlen==-1)?BUFSIZ:(min( BUFSIZ, curlen )) );
+ sock_call_progress( sumwrlen, readlen );
+ if( rdlen < 0 ) {
+ return -1;
+ } else if( rdlen == 0 ) {
+ /* End of file... get out of here */
+ break;
+ }
+ if( readlen != -1 )
+ curlen -= rdlen;
+ /* Otherwise, we have bytes! Write them to destfd... might
+ * only manage to write a few of them at a time, so we have
+ * to deal with that too. */
+ /* Replace this with a call to send_data? */
+
+ pnt = buffer;
+ len2 = rdlen;
+ while( len2 > 0 ) {
+ ssize_t wrlen;
+ wrlen = write( destfd, pnt, len2 );
+ if( wrlen < 0 ) {
+ return SOCK_ERROR;
+ }
+ len2 -= wrlen;
+ pnt += wrlen;
+ sumwrlen += wrlen;
+ }
+ }
+ sock_call_progress( sumwrlen, readlen );
+ return sumwrlen;
+}
+
+/* Reads buflen bytes into buffer until it's full.
+ * Returns 0 on success, -1 on error */
+int sock_fullread( int sock, char *buffer, int buflen ) {
+ char *pnt; /* current position within buffer */
+ int len;
+ pnt = buffer;
+ while( buflen > 0 ) {
+ len = sock_read( sock, pnt, buflen );
+ if( len < 0 ) return len;
+ buflen -= len;
+ pnt += len;
+ }
+ return 0;
+}
+
+/* Dump the given filename down the given socket.
+ * Returns 0 on success or non-zero on error. */
+int send_file_binary( int sock, const char *filename ) {
+ int fd, wrote;
+ struct stat fs;
+
+#if defined (__EMX__) || defined(__CYGWIN__)
+ if( (fd = open( filename, O_RDONLY | O_BINARY )) < 0 ) {
+#else
+ if( (fd = open( filename, O_RDONLY )) < 0 ) {
+#endif
+ return -1;
+ }
+ if( fstat( fd, &fs ) < 0 ) {
+ close( fd );
+ return -2;
+ }
+ /* What's the Right Thing to do? Choices:
+ * a) Let transfer send everything from fd until EOF
+ * + If the EOF pos changes, we'll know and can signal an error
+ * - Unsafe - the transfer might not end if someone does
+ * yes > file
+ * b) Tell transfer to send only the number of bytes from the stat()
+ * + Safe - the transfer WILL end.
+ * - If the EOF pos changes, we'll have a partial (corrupt) file.
+ * I'm not sure. I think (a) gets my vote but it doesn't allow
+ * nice transfer progress bars in the FE under the current API
+ * so we go with (b).
+ */
+ wrote = sock_transfer( fd, sock, fs.st_size );
+ close( fd ); /* any point in checking that one? */
+ if( wrote == fs.st_size ) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Dump the given filename down the given socket, in ASCII translation
+ * mode. Performs LF -> CRLF conversion.
+ * Returns zero on success or non-zero on error. */
+int send_file_ascii( int sock, const char *filename ) {
+ int ret;
+ char buffer[BUFSIZ], *pnt;
+ FILE *f;
+ ssize_t total = 0;
+
+ f = fopen( filename, "r" );
+ if( f == NULL ) {
+ return -1;
+ }
+
+ /* Init to success */
+ ret = 0;
+
+ while(1) {
+ if( fgets( buffer, BUFSIZ - 1, f ) == NULL ) {
+ if( ferror( f ) ) {
+ ret = -1;
+ break;
+ }
+ /* Finished upload */
+ ret = 0;
+ break;
+ }
+ /* To send in ASCII mode, we need to send CRLF as the EOL.
+ * We might or might not already have CRLF-delimited lines.
+ * So we mess about a bit to ensure that we do.
+ */
+ pnt = strchr( buffer, '\r' );
+ if( pnt == NULL ) {
+ /* We need to add the CR in */
+ pnt = strchr( buffer, '\n' );
+ if( pnt == NULL ) {
+ /* No CRLF found at all */
+ pnt = strchr( buffer, '\0' );
+ if( pnt == NULL ) /* crud in buffer */
+ pnt = buffer;
+ }
+ /* Now, pnt points to the first character after the
+ * end of the line, i.e., where we want to put the CR.
+ */
+ *pnt++ = '\r';
+ /* And lob in an LF afterwards */
+ *pnt-- = '\n';
+ }
+ /* At this point, pnt points to the CR.
+ * We send everything between pnt and the beginning of the buffer,
+ * +2 for the CRLF
+ */
+ if( sock_fullwrite( sock, buffer, (pnt - buffer) +2 ) != 0 ) {
+ ret = -1;
+ break;
+ }
+ total += (pnt - buffer) + 2;
+ sock_call_progress( total, -1 );
+ }
+ fclose( f ); /* any point in checking that one? */
+ /* Return true */
+ return ret;
+}
+
+/* Dump from given socket into given file. Reads only filesize bytes,
+ * or until EOF if filesize == -1.
+ * Returns number of bytes written on success, or -1 on error */
+int recv_file( int sock, const char *filename, ssize_t filesize ) {
+ int fd, wrote;
+#if defined (__EMX__) || defined(__CYGWIN__)
+ /* We have to set O_BINARY, thus need open(). Otherwise it should be
+ equivalent to creat(). */
+ if( (fd = open( filename, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644 )) < 0 ) {
+ return -1;
+ }
+#else
+ if( (fd = creat( filename, 0644 )) < 0 ) {
+ return -1;
+ }
+#endif
+ wrote = sock_transfer( sock, fd, filesize );
+ if( close( fd ) == -1 ) {
+ /* Close failed - file was not written correctly */
+ return -1;
+ }
+ if( filesize == -1 ) {
+ return wrote;
+ } else {
+ return (wrote==filesize);
+ }
+}
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure.
+ */
+int host_lookup( const char *hostname, struct in_addr *addr ) {
+ struct hostent *hp;
+ unsigned long laddr;
+
+ if( notify_cb )
+ (*notify_cb)( notify_ud, sock_namelookup, hostname );
+
+ laddr = (unsigned long)inet_addr(hostname);
+ if ((int)laddr == -1) {
+ /* inet_addr failed. */
+ hp = gethostbyname(hostname);
+ if( hp == NULL ) {
+ return -1;
+ }
+ memcpy( addr, hp->h_addr, hp->h_length );
+ } else {
+ addr->s_addr = laddr;
+ }
+ return 0;
+}
+
+/* Opens a socket to the given port at the given address.
+ * Returns -1 on failure, or the socket on success.
+ * portnum must be in HOST byte order */
+int sock_connect_u( const struct in_addr addr, int portnum, int call_fe ) {
+ struct sockaddr_in sa;
+ int sock;
+ /* Create the socket */
+ if( ( sock = socket(AF_INET, SOCK_STREAM, 0) ) < 0)
+ return -1;
+ /* Connect the socket */
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portnum); /* host -> net byte orders */
+ sa.sin_addr = addr;
+ if( call_fe && notify_cb ) (*notify_cb)( notify_ud, sock_connecting, NULL );
+ if( connect(sock, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0 )
+ return -1;
+ if( call_fe && notify_cb ) (*notify_cb)( notify_ud, sock_connected, NULL );
+ /* Success - return the socket */
+ return sock;
+}
+
+int sock_connect( const struct in_addr addr, int portnum ) {
+ return sock_connect_u( addr, portnum, 1 );
+}
+
+/* Closes given socket */
+int sock_close( int sock ) {
+ return close( sock );
+}
+
+/* Returns HOST byte order port of given name */
+int get_tcp_port( const char *name ) {
+ struct servent *ent;
+ ent = getservbyname( name, "tcp" );
+ if( ent == NULL ) {
+ return 0;
+ } else {
+ return ntohs( ent->s_port );
+ }
+}
diff --git a/neon/src/socket.h b/neon/src/socket.h
new file mode 100644
index 000000000..26db328dc
--- /dev/null
+++ b/neon/src/socket.h
@@ -0,0 +1,137 @@
+/*
+ socket handling routines
+ Copyright (C) 1998, Joe Orton <joe@orton.demon.co.uk>, except where
+ otherwise indicated.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+
+#define SOCK_ERROR -1
+/* Read/Write timed out */
+#define SOCK_TIMEOUT -2
+/* Passed buffer was full */
+#define SOCK_FULL -3
+/* Socket was closed */
+#define SOCK_CLOSED -4
+
+/* Socket read timeout */
+#define SOCKET_READ_TIMEOUT 30
+
+typedef enum {
+ sock_namelookup, /* Looking up hostname given by info */
+ sock_connecting, /* Connecting to server */
+ sock_connected /* Connection established */
+} sock_status;
+
+typedef void (*sock_block_reader) (
+ void *userdata, const char *buf, size_t len );
+
+typedef void (*sock_progress)( void *userdata, size_t progress, size_t total );
+typedef void (*sock_notify)( void *userdata, sock_status status, const char *info );
+
+void sock_register_progress( sock_progress cb, void *userdata );
+void sock_register_notify( sock_notify cb, void *userdata );
+
+void sock_call_progress( size_t progress, size_t total );
+
+/* sock_read is read() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ * SOCK_ERROR on error,
+ * SOCK_TIMEOUT on timeout,
+ * 0 on no data to read (due to timeout or EOF),
+ * >0 length of data read into buffer.
+ */
+int sock_read( int sock, void *buffer, size_t count );
+
+/* sock_recv is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ * -1 on error,
+ * 0 on no data to read (due to timeout or EOF),
+ * >0 length of data read into buffer.
+ */
+int sock_recv( int sock, void *buffer, size_t count, unsigned int flags);
+
+/* Blocks waiting for data on the given socket for the given time.
+ * Returns:
+ * -1 on error,
+ * 0 on no data within timeout,
+ * 1 if data arrived on the socket.
+ */
+int sock_block( int sock, int timeout );
+
+/* Dump the given filename down the given socket.
+ * Returns 0 on success, non-zero on error */
+int send_file_binary( int sock, const char *filename );
+
+/* Dump the given filename down the given socket, performing
+ * CR -> CRLF conversion.
+ * Returns 0 on success, non-zero on error */
+int send_file_ascii( int sock, const char *filename );
+
+/* Dump from given socket into given file. Reads only filesize bytes,
+ * or until EOF if filesize == -1.
+ * Returns number of bytes written on success, or -1 on error */
+int recv_file( int sock, const char *filename, ssize_t filesize );
+
+/* Reads readlen bytes from srcfd and writes to destfd.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or -1 on error.
+ * Calls fe_transfer_progress( a, b ) during transfers, where
+ * a = bytes transferred so far, and b = readlen
+ */
+int sock_transfer( int srcfd, int destfd, ssize_t readlen );
+
+/* Sends the given line to given socket, CRLF appended */
+int sock_sendline( int sock, const char *line );
+/* Sends the given block of data down the socket */
+int sock_fullwrite( int sock, const char *data, size_t length );
+/* Sends the null-terminated string down the given socket */
+int sock_send_string( int sock, const char *string );
+
+/* Reads a line from given socket */
+int sock_readline( int sock, char *line, int len );
+/* Reads a chunk of data. */
+int sock_fullread( int sock, char *buffer, int buflen );
+
+/* Creates and connects a socket */
+int sock_connect( const struct in_addr host, int portnum );
+
+int sock_connect_u( const struct in_addr addr, int portnum, int call_fe );
+
+int sock_close( int socket );
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure.
+ */
+int host_lookup( const char *hostname, struct in_addr *addr );
+
+/* Returns the standard TCP port for the given service */
+int get_tcp_port( const char *name );
+
+int sock_readfile_blocked( int fd, size_t length,
+ sock_block_reader reader, void *userdata );
+
+#endif /* SOCKET_H */
diff --git a/neon/src/sslcerts.c b/neon/src/sslcerts.c
new file mode 100644
index 000000000..b0c972bf1
--- /dev/null
+++ b/neon/src/sslcerts.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+ * Originally under GPL in Mutt, http://www.mutt.org/
+ * Relicensed under LGPL for neon, http://www.webdav.org/neon/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA
+ */
+
+/* for SSL NO_* defines */
+#include "config.h"
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#undef _
+
+#include <string.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x00904000L
+#define READ_X509_KEY(fp, key) PEM_read_X509(fp, key, NULL, NULL)
+#else
+#define READ_X509_KEY(fp, key) PEM_read_X509(fp, key, NULL)
+#endif
+
+/* This is ugly, but as RAND_status came in on OpenSSL version 0.9.5
+ * and the code has to support older versions too, this is seemed to
+ * be cleaner way compared to having even uglier #ifdefs all around.
+ */
+#ifdef HAVE_RAND_STATUS
+#define HAVE_ENTROPY() (RAND_status() == 1)
+#define GOT_ENTROPY() return 0;
+#else
+static int needentropy = 1;
+/* OpenSSL fills the entropy pool from /dev/urandom if it exists */
+#define HAVE_ENTROPY() (!access("/dev/urandom", R_OK) || !needentropy)
+#define GOT_ENTROPY() do { needentropy = 1; return 0; } while (0)
+#endif
+
+char *SslCertFile = NULL;
+char *SslEntropyFile = NULL;
+
+typedef struct _sslsockdata
+{
+ SSL_CTX *ctx;
+ SSL *ssl;
+ X509 *cert;
+}
+sslsockdata;
+
+/*
+ * OpenSSL library needs to be fed with sufficient entropy. On systems
+ * with /dev/urandom, this is done transparently by the library itself,
+ * on other systems we need to fill the entropy pool ourselves.
+ *
+ * Even though only OpenSSL 0.9.5 and later will complain about the
+ * lack of entropy, we try to our best and fill the pool with older
+ * versions also. (That's the reason for the ugly #ifdefs and macros,
+ * otherwise I could have simply #ifdef'd the whole ssl_init funcion)
+ */
+int sock_ssl_init (nsocket_ssl *ctx)
+{
+ char path[_POSIX_PATH_MAX], *file;
+
+ if (HAVE_ENTROPY()) return 0;
+
+ mutt_message (_("Filling entropy pool"));
+
+ /* try egd */
+#ifdef HAVE_RAND_EGD
+ file = SslEntropyFile;
+ if (file && RAND_egd(file) != -1)
+ GOT_ENTROPY();
+ file = getenv("EGDSOCKET");
+ if (file && RAND_egd(file) != -1)
+ GOT_ENTROPY();
+ snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir));
+ if (RAND_egd(path) != -1)
+ GOT_ENTROPY();
+ if (RAND_egd("/tmp/entropy") != -1)
+ GOT_ENTROPY();
+#endif
+
+ /* try some files */
+ file = SslEntropyFile;
+ if (!file || access(file, R_OK) == -1)
+ file = getenv("RANDFILE");
+ if (!file || access(file, R_OK) == -1) {
+ snprintf (path, sizeof(path), "%s/.rnd", NONULL(Homedir));
+ file = path;
+ }
+ if (access(file, R_OK) == 0) {
+ if (RAND_load_file(file, 10240) >= 16)
+ GOT_ENTROPY();
+ }
+
+ if (HAVE_ENTROPY()) return 0;
+
+ mutt_error (_("Failed to find enough entropy on your system"));
+ sleep (2);
+ return -1;
+}
+
+static int ssl_socket_open_err (CONNECTION *conn)
+{
+ mutt_error (_("SSL disabled due the lack of entropy"));
+ sleep (2);
+ return -1;
+}
+
+static int ssl_check_certificate (sslsockdata * data);
+
+int ssl_socket_open (CONNECTION * conn)
+{
+ sslsockdata *data;
+ int err;
+
+ if (raw_socket_open (conn) < 0)
+ return -1;
+
+ data = (sslsockdata *) safe_calloc (1, sizeof (sslsockdata));
+ conn->sockdata = data;
+
+ SSL_library_init();
+ data->ctx = SSL_CTX_new (SSLv23_client_method ());
+
+ /* disable SSL protocols as needed */
+ if (!option(OPTTLSV1))
+ {
+ SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1);
+ }
+ if (!option(OPTSSLV2))
+ {
+ SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2);
+ }
+ if (!option(OPTSSLV3))
+ {
+ SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3);
+ }
+
+ data->ssl = SSL_new (data->ctx);
+ SSL_set_fd (data->ssl, conn->fd);
+
+ if ((err = SSL_connect (data->ssl)) < 0)
+ {
+ ssl_socket_close (conn);
+ return -1;
+ }
+
+ data->cert = SSL_get_peer_certificate (data->ssl);
+ if (!data->cert)
+ {
+ mutt_error (_("Unable to get certificate from peer"));
+ sleep (1);
+ return -1;
+ }
+
+ if (!ssl_check_certificate (data))
+ {
+ ssl_socket_close (conn);
+ return -1;
+ }
+
+ mutt_message (_("SSL connection using %s"), SSL_get_cipher (data->ssl));
+ sleep (1);
+
+ return 0;
+}
+
+int ssl_socket_close (CONNECTION * conn)
+{
+ sslsockdata *data = conn->sockdata;
+ SSL_shutdown (data->ssl);
+
+ X509_free (data->cert);
+ SSL_free (data->ssl);
+ SSL_CTX_free (data->ctx);
+
+ return raw_socket_close (conn);
+}
+
+
+
+static char *x509_get_part (char *line, const char *ndx)
+{
+ static char ret[SHORT_STRING];
+ char *c, *c2;
+
+ strncpy (ret, _("Unknown"), sizeof (ret));
+
+ c = strstr (line, ndx);
+ if (c)
+ {
+ c += strlen (ndx);
+ c2 = strchr (c, '/');
+ if (c2)
+ *c2 = '\0';
+ strncpy (ret, c, sizeof (ret));
+ if (c2)
+ *c2 = '/';
+ }
+
+ return ret;
+}
+
+static void x509_fingerprint (char *s, int l, X509 * cert)
+{
+ unsigned char md[EVP_MAX_MD_SIZE];
+ unsigned int n;
+ int j;
+
+ if (!X509_digest (cert, EVP_md5 (), md, &n))
+ {
+ snprintf (s, l, _("[unable to calculate]"));
+ }
+ else
+ {
+ for (j = 0; j < (int) n; j++)
+ {
+ char ch[8];
+ snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
+ strncat (s, ch, l);
+ }
+ }
+}
+
+static char *asn1time_to_string (ASN1_UTCTIME *tm)
+{
+ static char buf[64];
+ BIO *bio;
+
+ strncpy (buf, _("[invalid date]"), sizeof (buf));
+
+ bio = BIO_new (BIO_s_mem());
+ if (bio)
+ {
+ if (ASN1_TIME_print (bio, tm))
+ (void) BIO_read (bio, buf, sizeof (buf));
+ BIO_free (bio);
+ }
+
+ return buf;
+}
+
+static int check_certificate_by_signer (X509 *peercert)
+{
+ X509_STORE_CTX xsc;
+ X509_STORE *ctx;
+ int pass;
+
+ ctx = X509_STORE_new ();
+ if (ctx == NULL) return 0;
+
+ if (option (OPTSSLSYSTEMCERTS) && !X509_STORE_set_default_paths (ctx))
+ {
+ DEBUG (DEBUG_SOCKET, "X509_STORE_set_default_paths failed\n");
+ X509_STORE_free (ctx);
+ return 0;
+ }
+
+ if (!X509_STORE_load_locations (ctx, SslCertFile, NULL))
+ {
+ DEBUG (DEBUG_SOCKET, "X509_STORE_load_locations failed\n");
+ X509_STORE_free (ctx);
+ return 0;
+ }
+
+ X509_STORE_CTX_init (&xsc, ctx, peercert, NULL);
+
+ pass = (X509_verify_cert (&xsc) > 0);
+#ifdef DEBUG
+ if (! pass)
+ {
+ char buf[SHORT_STRING];
+ int err;
+
+ err = X509_STORE_CTX_get_error (&xsc);
+ snprintf (buf, sizeof (buf), "%s (%d)",
+ X509_verify_cert_error_string(err), err);
+ DEBUG (DEBUG_SOCKET, "X509_verify_cert: %s\n", buf);
+ }
+#endif
+ X509_STORE_CTX_cleanup (&xsc);
+ X509_STORE_free (ctx);
+
+ return pass;
+}
+
+static int check_certificate_by_digest (X509 *peercert)
+{
+ unsigned char peermd[EVP_MAX_MD_SIZE];
+ unsigned int peermdlen;
+ X509 *cert = NULL;
+ int pass = 0;
+ FILE *fp;
+
+ /* expiration check */
+ if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0)
+ {
+ DEBUG (DEBUG_SCOKET, "Server certificate is not yet valid\n");
+ mutt_error (_("Server certificate is not yet valid"));
+ sleep (2);
+ return 0;
+ }
+ if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0)
+ {
+ DEBUG (DEBUG_SOCKET, "Server certificate has expired");
+ mutt_error (_("Server certificate has expired"));
+ sleep (2);
+ return 0;
+ }
+
+ if ((fp = fopen (SslCertFile, "rt")) == NULL)
+ return 0;
+
+ if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen))
+ {
+ fclose (fp);
+ return 0;
+ }
+
+ while ((cert = READ_X509_KEY (fp, &cert)) != NULL)
+ {
+ unsigned char md[EVP_MAX_MD_SIZE];
+ unsigned int mdlen;
+
+ /* Avoid CPU-intensive digest calculation if the certificates are
+ * not even remotely equal.
+ */
+ if (X509_subject_name_cmp (cert, peercert) != 0 ||
+ X509_issuer_name_cmp (cert, peercert) != 0)
+ continue;
+
+ if (!X509_digest (cert, EVP_sha1(), md, &mdlen) || peermdlen != mdlen)
+ continue;
+
+ if (memcmp(peermd, md, mdlen) != 0)
+ continue;
+
+ pass = 1;
+ break;
+ }
+ X509_free (cert);
+ fclose (fp);
+
+ return pass;
+}
+
+static int ssl_check_certificate (sslsockdata * data)
+{
+ char *part[] =
+ {"/CN=", "/Email=", "/O=", "/OU=", "/L=", "/ST=", "/C="};
+ char helpstr[SHORT_STRING];
+ char buf[SHORT_STRING];
+ MUTTMENU *menu;
+ int done, row, i;
+ FILE *fp;
+ char *name = NULL, *c;
+
+ if (check_certificate_by_signer (data->cert))
+ {
+ dprint (1, (debugfile, "ssl_check_certificate: signer check passed\n"));
+ return 1;
+ }
+
+ /* automatic check from user's database */
+ if (SslCertFile && check_certificate_by_digest (data->cert))
+ {
+ dprint (1, (debugfile, "ssl_check_certificate: digest check passed\n"));
+ return 1;
+ }
+
+ /* interactive check from user */
+ menu = mutt_new_menu ();
+ menu->max = 19;
+ menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
+ for (i = 0; i < menu->max; i++)
+ menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
+
+ row = 0;
+ strncpy (menu->dialog[row++], _("This certificate belongs to:"), SHORT_STRING);
+ name = X509_NAME_oneline (X509_get_subject_name (data->cert),
+ buf, sizeof (buf));
+ for (i = 0; i < 5; i++)
+ {
+ c = x509_get_part (name, part[i]);
+ snprintf (menu->dialog[row++], SHORT_STRING, " %s", c);
+ }
+
+ row++;
+ strncpy (menu->dialog[row++], _("This certificate was issued by:"), SHORT_STRING);
+ name = X509_NAME_oneline (X509_get_issuer_name (data->cert),
+ buf, sizeof (buf));
+ for (i = 0; i < 5; i++)
+ {
+ c = x509_get_part (name, part[i]);
+ snprintf (menu->dialog[row++], SHORT_STRING, " %s", c);
+ }
+
+ row++;
+ snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
+ snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
+ asn1time_to_string (X509_get_notBefore (data->cert)));
+ snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
+ asn1time_to_string (X509_get_notAfter (data->cert)));
+
+ row++;
+ buf[0] = '\0';
+ x509_fingerprint (buf, sizeof (buf), data->cert);
+ snprintf (menu->dialog[row++], SHORT_STRING, _("Fingerprint: %s"), buf);
+
+ menu->title = _("SSL Certificate check");
+ if (SslCertFile)
+ {
+ menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
+ menu->keys = _("roa");
+ }
+ else
+ {
+ menu->prompt = _("(r)eject, accept (o)nce");
+ menu->keys = _("ro");
+ }
+
+ helpstr[0] = '\0';
+ mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
+ strncat (helpstr, buf, sizeof (helpstr));
+ mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
+ strncat (helpstr, buf, sizeof (helpstr));
+ menu->help = helpstr;
+
+ done = 0;
+ while (!done)
+ {
+ switch (mutt_menuLoop (menu))
+ {
+ case -1: /* abort */
+ case OP_MAX + 1: /* reject */
+ case OP_EXIT:
+ done = 1;
+ break;
+ case OP_MAX + 3: /* accept always */
+ done = 0;
+ if ((fp = fopen (SslCertFile, "a")))
+ {
+ if (PEM_write_X509 (fp, data->cert))
+ done = 1;
+ fclose (fp);
+ }
+ if (!done)
+ mutt_error (_("Warning: Couldn't save certificate"));
+ else
+ mutt_message (_("Certificate saved"));
+ sleep (1);
+ /* fall through */
+ case OP_MAX + 2: /* accept once */
+ done = 2;
+ break;
+ }
+ }
+ mutt_menuDestroy (&menu);
+ return (done == 2);
+}
diff --git a/neon/src/string_utils.c b/neon/src/string_utils.c
new file mode 100644
index 000000000..6736a56c4
--- /dev/null
+++ b/neon/src/string_utils.c
@@ -0,0 +1,447 @@
+/*
+ String utility functions
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: string_utils.c,v 1.8 2000/05/09 18:32:07 joe Exp
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "xalloc.h"
+
+#include "string_utils.h"
+
+struct sbuffer_s {
+ char *data;
+ size_t used; /* used bytes in buffer */
+ size_t length; /* length of buffer */
+};
+
+/* TODO: These are both crap. Rewrite to be like strsep(). */
+
+char **split_string( const char *str, const char separator,
+ const char *quotes, const char *whitespace ) {
+ return split_string_c( str, separator, quotes, whitespace, NULL );
+}
+
+char **split_string_c( const char *str, const char separator,
+ const char *quotes, const char *whitespace,
+ int *give_count ) {
+ char **comps;
+ const char *pnt, *quot = NULL,
+ *start, *end; /* The start of the current component */
+ int count, /* The number of components */
+ iswhite, /* is it whitespace */
+ issep, /* is it the separator */
+ curr, /* current component index */
+ length, /* length of component */
+ leading_wspace; /* in leading whitespace still? */
+
+ /* Inefficient, but easier - first off, count the number of
+ * components we have. */
+ count = 1;
+ for( pnt = str; *pnt!='\0'; pnt++ ) {
+ if( quotes != NULL ) {
+ quot = strchr( quotes, *pnt );
+ }
+ if( quot != NULL ) {
+ /* We found a quote, so skip till the next quote */
+ for( pnt++; (*pnt!=*quot) && (*pnt!='\0'); pnt++ )
+ /* nullop */;
+ } else if( *pnt == separator ) {
+ count++;
+ }
+ }
+
+ if( give_count ) {
+ /* Write the count */
+ *give_count = count;
+ }
+
+ /* Now, have got the number of components.
+ * Allocate the comps array. +1 for the NULL */
+ comps = xmalloc( sizeof(char *) * (count + 1) );
+
+ comps[count] = NULL;
+
+ quot = end = start = NULL;
+ curr = 0;
+ leading_wspace = 1;
+
+ /* Now fill in the array */
+ for( pnt = str; *pnt != '\0'; pnt++ ) {
+ /* What is the current character - quote, whitespace, separator? */
+ if( quotes != NULL ) {
+ quot = strchr( quotes, *pnt );
+ }
+ iswhite = (whitespace!=NULL) &&
+ (strchr( whitespace, *pnt ) != NULL );
+ issep = (*pnt == separator);
+ /* What to do? */
+ if( leading_wspace ) {
+ if( quot!=NULL ) {
+ /* Quoted bit */
+ start = pnt;
+ length = 1;
+ leading_wspace = 0;
+ } else if( issep ) {
+ /* Zero-length component */
+ comps[curr++] = xstrdup("");
+ } else if( !iswhite ) {
+ start = end = pnt;
+ length = 1;
+ leading_wspace = 0;
+ }
+ } else {
+ if( quot!=NULL ) {
+ /* Quoted bit */
+ length++;
+ } else if( issep ) {
+ /* End of component - enter it into the array */
+ length = (end - start) + 1;
+ comps[curr] = xmalloc( length+1 );
+ memcpy( comps[curr], start, length );
+ comps[curr][length] = '\0';
+ curr++;
+ leading_wspace = 1;
+ } else if( !iswhite ) {
+ /* Not whitespace - update end marker */
+ end = pnt;
+ }
+ }
+ if( quot != NULL ) {
+ /* Skip to closing quote */
+ for( pnt++; *pnt!=*quot && *pnt != '\0'; ++pnt )
+ /* nullop */;
+ /* Last non-wspace char is closing quote */
+ end = pnt;
+ }
+ }
+ /* Handle final component */
+ if( leading_wspace ) {
+ comps[curr] = xstrdup( "" );
+ } else {
+ /* End of component - enter it into the array */
+ length = (end - start) + 1;
+ comps[curr] = xmalloc( length+1 );
+ memcpy( comps[curr], start, length );
+ comps[curr][length] = '\0';
+ }
+ return comps;
+}
+
+char **pair_string( const char *str, const char compsep, const char kvsep,
+ const char *quotes, const char *whitespace )
+{
+ char **comps, **pairs, *split;
+ int count = 0, n, length;
+ comps = split_string_c( str, compsep, quotes, whitespace, &count );
+ /* Allocate space for 2* as many components as split_string returned,
+ * +2 for the NULLS. */
+ pairs = xmalloc( (2*count+2) * sizeof(char *) );
+ if( pairs == NULL ) {
+ return NULL;
+ }
+ for( n = 0; n < count; n++ ) {
+ /* Find the split */
+ split = strchr( comps[n], kvsep );
+ if( split == NULL ) {
+ /* No seperator found */
+ length = strlen(comps[n]);
+ } else {
+ length = split-comps[n];
+ }
+ /* Enter the key into the array */
+ pairs[2*n] = comps[n];
+ /* Null-terminate the key */
+ pairs[2*n][length] = '\0';
+ pairs[2*n+1] = split?(split + 1):NULL;
+ }
+ free( comps );
+ pairs[2*count] = pairs[2*count+1] = NULL;
+ return pairs;
+}
+
+void split_string_free( char **components )
+{
+ char **pnt = components;
+ while( *pnt != NULL ) {
+ free( *pnt );
+ pnt++;
+ }
+ free( components );
+}
+
+void pair_string_free( char **pairs )
+{
+ int n;
+ for( n = 0; pairs[n] != NULL; n+=2 ) {
+ free( pairs[n] );
+ }
+ free( pairs );
+}
+
+char *shave_string( const char *str, const char ch ) {
+ size_t len = strlen( str );
+ char *ret;
+ if( str[len-1] == ch ) {
+ len--;
+ }
+ if( str[0] == ch ) {
+ len--;
+ str++;
+ }
+ ret = xmalloc( len + 1 );
+ memcpy( ret, str, len );
+ ret[len] = '\0';
+ return ret;
+}
+
+char *sbuffer_data( sbuffer buf ) {
+ return buf->data;
+}
+
+int sbuffer_size( sbuffer buf ) {
+ return buf->used;
+}
+
+void sbuffer_clear( sbuffer buf ) {
+ memset( buf->data, 0, buf->length );
+ buf->used = 0;
+}
+
+/* Grows for given size, 0 on success, -1 on error.
+ * Could make this externally accessible, but, there hasn't been
+ * a need currently. */
+int sbuffer_grow( sbuffer buf, size_t newsize ) {
+ size_t newlen, oldbuflen;
+
+#define SBUFFER_GROWTH 512
+
+ if( newsize <= buf->length ) return 0; /* big enough already */
+ /* FIXME: ah, can't remember my maths... better way to do this? */
+ newlen = ( (newsize / SBUFFER_GROWTH) + 1) * SBUFFER_GROWTH;
+
+ oldbuflen = buf->length;
+ /* Reallocate bigger buffer */
+ buf->data = realloc( buf->data, newlen );
+ if( buf->data == NULL ) return -1;
+ buf->length = newlen;
+ /* Zero-out the new bit of buffer */
+ memset( buf->data+oldbuflen, 0, newlen-oldbuflen );
+
+ return 0;
+}
+
+int sbuffer_concat( sbuffer buf, ... ) {
+ va_list ap;
+ char *next;
+ size_t totallen = 1; /* initialized to 1 for the terminating \0 */
+
+ /* Find out how much space we need for all the args */
+ va_start( ap, buf );
+ do {
+ next = va_arg( ap, char * );
+ if( next != NULL ) {
+ totallen += strlen( next );
+ }
+ } while( next != NULL );
+ va_end( ap );
+
+ /* Grow the buffer */
+ if( sbuffer_grow( buf, buf->used + totallen ) )
+ return -1;
+
+ /* Now append the arguments to the buffer */
+ va_start( ap, buf );
+ do {
+ next = va_arg( ap, char * );
+ if( next != NULL ) {
+ strcat( buf->data, next );
+ }
+ } while( next != NULL );
+ va_end( ap );
+
+ buf->used += totallen;
+ return 0;
+}
+
+/* Append zero-terminated string... returns 0 on success or -1 on
+ * realloc failure. */
+int sbuffer_zappend( sbuffer buf, const char *str ) {
+ size_t len = strlen( str );
+ if( sbuffer_grow( buf, buf->used + len + 1 ) ) {
+ return -1;
+ }
+ strcat( buf->data, str );
+ buf->used += len;
+ return 0;
+}
+
+int sbuffer_append( sbuffer buf, const char *data, size_t len ) {
+ if( sbuffer_grow( buf, buf->used + len ) ) {
+ return -1;
+ }
+ memcpy( buf->data+buf->used, data, len );
+ buf->used += len;
+ return 0;
+}
+
+sbuffer sbuffer_create( void ) {
+ return sbuffer_create_sized( 512 );
+}
+
+sbuffer sbuffer_create_sized( size_t s ) {
+ sbuffer buf = xmalloc( sizeof(struct sbuffer_s) );
+ buf->data = xmalloc( s );
+ memset( buf->data, 0, s );
+ buf->length = s;
+ buf->used = 0;
+ return buf;
+}
+
+void sbuffer_destroy( sbuffer buf ) {
+ if( buf->data ) {
+ free( buf->data );
+ }
+ free( buf );
+}
+
+char *sbuffer_finish( sbuffer buf )
+{
+ char *ret = buf->data;
+ free( buf );
+ return ret;
+}
+
+void sbuffer_altered( sbuffer buf )
+{
+ buf->used = strlen( buf->data );
+}
+
+/* Writes the ASCII representation of the MD5 digest into the
+ * given buffer, which must be at least 33 characters long. */
+void md5_to_ascii( const unsigned char md5_buf[16], char *buffer )
+{
+ int count;
+ for( count = 0; count<16; count++ ) {
+ buffer[count*2] = HEX2ASC( md5_buf[count] >> 4 );
+ buffer[count*2+1] = HEX2ASC( md5_buf[count] & 0x0f );
+ }
+ buffer[32] = '\0';
+}
+
+/* Reads the ASCII representation of an MD5 digest. The buffer must
+ * be at least 32 characters long. */
+void ascii_to_md5( const char *buffer, unsigned char md5_buf[16] )
+{
+ int count;
+ for( count = 0; count<16; count++ ) {
+ md5_buf[count] = ((ASC2HEX(buffer[count*2])) << 4) |
+ ASC2HEX(buffer[count*2+1]);
+ }
+}
+
+#ifdef SPLIT_STRING_TEST
+
+#include <stdio.h>
+
+int main( int argc, char *argv[] ) {
+ char *str, sep, **comps, *wspace, *quotes;
+ int count;
+ if( argc < 3 ) {
+ printf( "Usage: split_string <sep> <string> [whitespace] [quotes]\n" );
+ return -1;
+ }
+ sep = *argv[1];
+ str = argv[2];
+ if( argc > 3 ) {
+ wspace = argv[3];
+ } else {
+ wspace = " ";
+ }
+ if( argc > 4 ) {
+ quotes = argv[4];
+ } else {
+ quotes = "\"";
+ }
+ printf( "String: [%s] Separator: `%c' Whitespace: [%s] Quotes: [%s]\n", str, sep, wspace, quotes );
+ comps = split_string( str, sep, quotes, wspace );
+ count = 0;
+ do {
+ printf( "Component #%d: [%s]\n", count, comps[count] );
+ } while( comps[++count] != NULL );
+ return 0;
+}
+
+#endif
+
+#ifdef PAIR_STRING_TEST
+
+#include <stdio.h>
+
+int main( int argc, char *argv[] ) {
+ char *str, compsep, kvsep, **comps, *wspace, *quotes;
+ int count;
+ if( argc < 4 ) {
+ printf( "Usage: pair_string <compsep> <kvsep> <string> [whitespace] [quotes]\n" );
+ return -1;
+ }
+ compsep = *argv[1];
+ kvsep = *argv[2];
+ str = argv[3];
+ if( argc > 4 ) {
+ wspace = argv[4];
+ } else {
+ wspace = " ";
+ }
+ if( argc > 5 ) {
+ quotes = argv[5];
+ } else {
+ quotes = "\"";
+ }
+ printf( "String: [%s] CompSep: `%c' K/VSep: `%c'\nWhitespace: [%s] Quotes: [%s]\n", str, compsep, kvsep, wspace, quotes );
+ comps = pair_string( str, compsep, kvsep, quotes, wspace );
+ count = 0;
+ do {
+ printf( "Component #%d: Key [%s] Value [%s]\n", count,
+ comps[count], comps[count+1] );
+ } while( comps[(count+=2)] != NULL );
+ return 0;
+}
+
+#endif
+
+/* variables:
+ *
+ * Local variables:
+ * compile-command: "gcc -g -O2 -Wall -I.. -ansi -DHAVE_CONFIG_H -DSPLIT_STRING_TEST -o split_string string_utils.c"
+ * End:
+ */
diff --git a/neon/src/string_utils.h b/neon/src/string_utils.h
new file mode 100644
index 000000000..c7302e609
--- /dev/null
+++ b/neon/src/string_utils.h
@@ -0,0 +1,198 @@
+/*
+ String utility functions
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef STRING_UTILS_H
+#define STRING_UTILS_H
+
+#include <config.h>
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h> /* for strcat */
+#endif
+
+#include "xalloc.h"
+
+#include <ctype.h> /* for tolower */
+
+#define ASC2HEX(x) (((x) <= '9') ? ((x) - '0') : (tolower((x)) + 10 - 'a'))
+#define HEX2ASC(x) ((x) > 9 ? ((x) - 10 + 'a') : ((x) + '0'))
+
+/* Splits str into component parts using given seperator,
+ * skipping given whitespace around separators and at the
+ * beginning and end of str. Separators are ignored within
+ * any pair of characters specified as being quotes.
+ * Returns array of components followed by a NULL pointer. The
+ * components are taken from dynamically allocated memory, and
+ * the entire array can be easily freed using split_string_free.
+ *
+ * aka: What strtok should have been.
+ */
+char **split_string( const char *str, const char seperator,
+ const char *quotes, const char *whitespace );
+
+/* As above, but returns count of items as well */
+char **split_string_c( const char *str, const char seperator,
+ const char *quotes, const char *whitespace, int *count );
+
+
+/* A bit like split_string, except each component is split into a pair.
+ * Each pair is returned consecutively in the return array.
+ * e.g.:
+ * strpairs( "aa=bb,cc=dd", ',', '=', NULL, NULL )
+ * => "aa", "bb", "cc", "dd", NULL, NULL
+ * Note, that if a component is *missing* it's key/value separator,
+ * then the 'value' in the array will be a NULL pointer. But, if the
+ * value is zero-length (i.e., the component separator follows directly
+ * after the key/value separator, or with only whitespace inbetween),
+ * then the value in the array will be a zero-length string.
+ * e.g.:
+ * pair_string( "aaaa,bb=cc,dd=,ee=ff", ',', '=', NULL, NULL )
+ * => "aaaa", NULL, "bb", "cc", "dd", "", "ee", "ff"
+ * A NULL key indicates the end of the array (the value will also
+ * be NULL, for convenience).
+ */
+char **pair_string( const char *str, const char compsep, const char kvsep,
+ const char *quotes, const char *whitespace );
+
+/* Frees the array returned by split_string */
+void split_string_free( char **components );
+
+/* Frees the array returned by pair_string */
+void pair_string_free( char **pairs );
+
+/* Returns a string which is str with ch stripped from
+ * beggining and end, if present in either. The string returned
+ * is dynamically allocated using xmalloc().
+ *
+ * e.g. shave_string( "abbba", 'a' ) => "bbb". */
+char *shave_string( const char *str, const char ch );
+
+#define EOL "\r\n"
+
+#define STRIP_EOL( str ) \
+do { \
+ char *p; \
+ if( (p = strrchr( str, '\r' )) != NULL ) *p = '\0'; \
+ if( (p = strrchr( str, '\n' )) != NULL ) *p = '\0'; \
+} while(0)
+
+/* String buffer handling.
+ * A string buffer sbuffer * which grows dynamically with the string. */
+
+struct sbuffer_s;
+typedef struct sbuffer_s *sbuffer;
+
+/* Returns contents of buffer at current point in time.
+ * NOTE: if the buffer is modified with _concat, _append etc,
+ * this value may no longer be correct. */
+char *sbuffer_data( sbuffer buf );
+
+/* Returns size of data in buffer, equiv to strlen(sbuffer_data(buf)) */
+int sbuffer_size( sbuffer buf );
+
+/* Concatenate all given strings onto the end of the buffer.
+ * The strings must be null-terminated, and MUST be followed by a
+ * NULL argument marking the end of the list.
+ * Returns:
+ * 0 on success
+ * non-zero on error
+ */
+int sbuffer_concat( sbuffer buf, ... );
+
+/* Create a new sbuffer. Returns NULL on error */
+sbuffer sbuffer_create( void );
+
+/* Create a new sbuffer of given minimum size. Returns NULL on error */
+sbuffer sbuffer_create_sized( size_t size );
+
+/* Destroys (deallocates) a buffer */
+void sbuffer_destroy( sbuffer buf );
+
+/* Append a zero-terminated string 'str' to buf.
+ * Returns 0 on success, non-zero on error. */
+int sbuffer_zappend( sbuffer buf, const char *str );
+
+/* Append 'len' bytes of 'data' to buf.
+ * Returns 0 on success, non-zero on error. */
+int sbuffer_append( sbuffer buf, const char *data, size_t len );
+
+/* Empties the contents of buf; makes the buffer zero-length. */
+void sbuffer_clear( sbuffer buf );
+
+/* Grows the sbuffer to a minimum size.
+ * Returns 0 on success, non-zero on error */
+int sbuffer_grow( sbuffer buf, size_t size );
+
+void sbuffer_altered( sbuffer buf );
+
+/* MD5 ascii->binary conversion */
+void md5_to_ascii( const unsigned char md5_buf[16], char *buffer );
+void ascii_to_md5( const char *buffer, unsigned char md5_buf[16] );
+
+/* Destroys a buffer, returning the buffer contents. */
+char *sbuffer_finish( sbuffer buf );
+
+/* Handy macro to free things. */
+#define SAFE_FREE(x) \
+do { if( (x)!=NULL ) free( (x) ); (x) = NULL; } while(0)
+
+/* TODO: do these with stpcpy instead... more efficient, but means
+ * bloat on non-GNU platforms. */
+
+/* TODO: could replace with glib equiv's where available, too */
+
+#define CONCAT2( out, str1, str2 ) \
+do { \
+ out = xmalloc( strlen(str1) + strlen(str2) + 1 ); \
+ if( out != NULL ) { \
+ strcpy( out, str1 ); \
+ strcat( out, str2 ); \
+ } \
+} while(0)
+
+#define CONCAT3( out, str1, str2, str3 ) \
+do { \
+ out = xmalloc( strlen(str1) + strlen(str2) + strlen(str3) + 1 ); \
+ if( out != NULL ) { \
+ strcpy( out, str1 ); \
+ strcat( out, str2 ); \
+ strcat( out, str3 ); \
+ } \
+} while(0)
+
+#define CONCAT4( out, str1, str2, str3, str4 ) \
+do { \
+ out = xmalloc( strlen(str1) + strlen(str2) \
+ + strlen(str3) + strlen(str4) + 1 ); \
+ if( out != NULL ) { \
+ strcpy( out, str1 ); \
+ strcat( out, str2 ); \
+ strcat( out, str3 ); \
+ strcat( out, str4 ); \
+ } \
+} while(0)
+
+#endif STRING_UTILS_H
+
diff --git a/neon/src/uri.c b/neon/src/uri.c
new file mode 100644
index 000000000..622044f9b
--- /dev/null
+++ b/neon/src/uri.c
@@ -0,0 +1,303 @@
+/*
+ HTTP URI handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: uri.c,v 1.7 2000/05/10 16:47:06 joe Exp
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "http_utils.h" /* for 'min' */
+#include "string_utils.h" /* for CONCAT3 */
+#include "uri.h"
+
+char *uri_parent( const char *uri ) {
+ const char *pnt;
+ char *ret;
+ pnt = uri+strlen(uri)-1;
+ while( *(--pnt) != '/' && pnt >= uri ) /* noop */;
+ if( pnt < uri ) {
+ /* not a valid absPath */
+ return NULL;
+ }
+ /* uri
+ * V
+ * |---|
+ * /foo/bar/
+ */
+ ret = xmalloc( (pnt - uri) + 2 );
+ memcpy( ret, uri, (pnt - uri) + 1 );
+ ret[1+(pnt-uri)] = '\0';
+ pnt++;
+ return ret;
+}
+
+int uri_has_trailing_slash( const char *uri )
+{
+ return (uri[strlen(uri)-1] == '/');
+}
+
+const char *uri_abspath( const char *uri ) {
+ const char *ret;
+ /* Look for the scheme: */
+ ret = strstr( uri, "://" );
+ if( ret == NULL ) {
+ /* No scheme */
+ ret = uri;
+ } else {
+ /* Look for the abs_path */
+ ret = strchr( ret+3, '/' );
+ if( ret == NULL ) {
+ /* Uh-oh */
+ ret = uri;
+ }
+ }
+ return ret;
+}
+
+/* TODO: not a proper URI parser */
+int uri_parse( const char *uri, struct uri *parsed,
+ const struct uri *defaults )
+{
+ const char *pnt, *slash, *colon;
+
+ parsed->port = -1;
+ parsed->host = NULL;
+ parsed->path = NULL;
+ parsed->scheme = NULL;
+
+ pnt = strstr( uri, "://" );
+ if( pnt ) {
+ parsed->scheme = xstrndup( uri, pnt - uri );
+ pnt += 3; /* start of hostport segment */
+ slash = strchr( pnt, '/' );
+ colon = strchr( pnt, ':' );
+ if( slash == NULL ) {
+ parsed->path = xstrdup( "/" );
+ if( colon == NULL ) {
+ if( defaults ) parsed->port = defaults->port;
+ parsed->host = xstrdup( pnt );
+ } else {
+ parsed->port = atoi(colon+1);
+ parsed->host = xstrndup( pnt, colon - pnt );
+ }
+ } else {
+ if( colon == NULL || colon > slash ) {
+ /* No port segment */
+ if( defaults ) parsed->port = defaults->port;
+ parsed->host = xstrndup( pnt, slash - pnt );
+ } else {
+ /* Port segment */
+ parsed->port = atoi( colon + 1 );
+ parsed->host = xstrndup( pnt, colon - pnt );
+ }
+ parsed->path = xstrdup( slash );
+ }
+ } else {
+ if( defaults && defaults->scheme != NULL ) {
+ parsed->scheme = xstrdup( defaults->scheme );
+ }
+ if( defaults && defaults->host != NULL ) {
+ parsed->host = xstrdup( defaults->host );
+ }
+ if( defaults ) parsed->port = defaults->port;
+ parsed->path = xstrdup(uri);
+ }
+ return 0;
+}
+
+void uri_free( struct uri *uri )
+{
+ HTTP_FREE( uri->host );
+ HTTP_FREE( uri->path );
+ HTTP_FREE( uri->scheme );
+}
+
+/* Returns an absoluteURI */
+char *uri_absolute( const char *uri, const char *scheme,
+ const char *hostport ) {
+ char *ret;
+ /* Is it absolute already? */
+ if( strncmp( uri, scheme, strlen( scheme ) ) == 0 ) {
+ /* Yes it is */
+ ret = xstrdup( uri );
+ } else {
+ /* Oh no it isn't */
+ CONCAT3( ret, scheme, hostport, uri );
+ }
+ return ret;
+}
+
+/* Un-escapes a URI. Returns xmalloc-allocated URI */
+char *uri_unescape( const char *uri ) {
+ const char *pnt;
+ char *ret, *retpos, buf[5] = { "0x00\0" };
+ retpos = ret = xmalloc( strlen( uri ) + 1 );
+ for( pnt = uri; *pnt != '\0'; pnt++ ) {
+ if( *pnt == '%' ) {
+ if( !isxdigit((unsigned char) pnt[1]) ||
+ !isxdigit((unsigned char) pnt[2]) ) {
+ /* Invalid URI */
+ return NULL;
+ }
+ buf[2] = *++pnt; buf[3] = *++pnt; /* bit faster than memcpy */
+ *retpos++ = strtol( buf, NULL, 16 );
+ } else {
+ *retpos++ = *pnt;
+ }
+ }
+ *retpos = '\0';
+ return ret;
+}
+
+/* RFC2396 spake:
+ * "Data must be escaped if it does not have a representation
+ * using an unreserved character".
+ * ...where...
+ * unreserved = alphanum | mark
+ * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+ *
+ * We need also to skip reserved characters
+ * reserved = ";" | "/" | "?" | ":" | "@" | "&" |
+ * "=" | "+" | "$" | ","
+ */
+
+/* Lookup table:
+ * 1 marks an RESERVED character. 2 marks a UNRESERVED character.
+ * 0 marks everything else.
+ */
+
+#define RE 1
+#define UN 2
+static const short uri_chars[128] = {
+/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 */ 0, UN, 0, 0, RE, 0, RE, UN, UN, UN, UN, RE, RE, UN, UN, RE,
+/* 48 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, RE, RE, 0, RE, 0, RE,
+/* 64 */ RE, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN,
+/* 80 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, 0, 0, 0, 0, UN,
+/* 96 */ 0, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN,
+/* 112 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, 0, 0, 0, UN, 0
+};
+#undef RE
+#undef UN
+
+/* Escapes the abspath segment of a URI.
+ * Returns xmalloc-allocated string.
+ */
+char *uri_abspath_escape( const char *abs_path ) {
+ const char *pnt;
+ char *ret, *retpos;
+ /* Rather than mess about growing the buffer, allocate as much as
+ * the URI could possibly need, i.e. every character gets %XX
+ * escaped. Hence 3 times input size.
+ */
+ retpos = ret = xmalloc( strlen( abs_path ) * 3 + 1 );
+ for( pnt = abs_path; *pnt != '\0'; pnt++ ) {
+ /* Escape it:
+ * - if it isn't 7-bit
+ * - if it is a reserved character (but ignore '/')
+ * - otherwise, if it is not an unreserved character
+ * (note, there are many characters that are neither reserved
+ * nor unreserved)
+ */
+ if( *pnt<0 || (uri_chars[(int) *pnt] < 2 && *pnt!='/' )) {
+ /* Escape it - %<hex><hex> */
+ /* FIXME: Could overflow buffer with uri_abspath_escape("%") */
+ sprintf( retpos, "%%%02x", (unsigned char) *pnt );
+ retpos += 3;
+ } else {
+ /* It's cool */
+ *retpos++ = *pnt;
+ }
+ }
+ *retpos = '\0';
+ return ret;
+}
+
+/* TODO: implement properly */
+int uri_compare( const char *a, const char *b ) {
+ int ret = strcasecmp( a, b );
+ if( ret ) {
+ /* TODO: joe: I'm not 100% sure this is logically sound.
+ * It feels right, though */
+ int traila = uri_has_trailing_slash( a ),
+ trailb = uri_has_trailing_slash( b ),
+ lena = strlen(a), lenb = strlen(b);
+ if( traila != trailb && abs(lena - lenb) == 1) {
+ /* They are the same length, apart from one has a trailing
+ * slash and the other doesn't. */
+ if( strncasecmp( a, b, min(lena, lenb) ) == 0 )
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+/* Hrm, well, this is kind of not very generic over URI schemes, but wth */
+int uri_childof( const char *parent, const char *child )
+{
+ char *root = xstrdup(child);
+ int ret;
+ if( strlen(parent) >= strlen(child) ) {
+ ret = 0;
+ } else {
+ /* root is the first of child, equal to length of parent */
+ root[strlen(parent)] = '\0';
+ ret = (uri_compare( parent, root ) == 0 );
+ }
+ free( root );
+ return ret;
+}
+
+#ifdef URITEST
+
+int main( int argc, char *argv[] ) {
+ char *tmp;
+ if( argc<2 || argc>3 ) {
+ printf( "doh. usage:\nuritest uria [urib]\n"
+ "e.g. uritest \"/this/is/a silly<filename>/but/hey\"\n" );
+ exit(-1);
+ }
+ if( argv[2] ) {
+ printf( "uri_compare: %s with %s: %s\n",
+ argv[1], argv[2],
+ uri_compare(argv[1], argv[2])==0?"true":"false" );
+ } else {
+ printf( "Input URI: %s\n", argv[1] );
+ tmp = uri_abspath_escape( argv[1] );
+ printf( "Encoded: %s\n", tmp );
+ printf( "Decoded: %s\n", uri_unescape( tmp ) );
+ }
+ return 0;
+}
+
+#endif /* URITEST */
diff --git a/neon/src/uri.h b/neon/src/uri.h
new file mode 100644
index 000000000..01d7dd993
--- /dev/null
+++ b/neon/src/uri.h
@@ -0,0 +1,63 @@
+/*
+ HTTP URI handling
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef URI_H
+#define URI_H
+
+/* Un-escapes a URI. Returns malloc-allocated URI on success,
+ * or NULL on failure (malloc failure or invalid %<HEX><HEX> sequence). */
+char *uri_unescape( const char *uri );
+
+/* Escapes the abspath segment of a URI.
+ * Returns malloc-allocated string on success, or NULL on malloc failure.
+ */
+char *uri_abspath_escape( const char *abs_path );
+
+const char *uri_abspath( const char *uri );
+
+char *uri_parent( const char *uri );
+
+int uri_compare( const char *a, const char *b );
+
+/* Returns an absolute URI from a possibly-relative 'uri', using
+ * given scheme + hostport segment.
+ * Returns malloc-allocated string on success, or NULL on malloc failure. */
+char *uri_absolute( const char *uri, const char *scheme,
+ const char *hostport );
+
+/* Returns non-zero if child is a child of parent */
+int uri_childof( const char *parent, const char *child );
+
+int uri_has_trailing_slash( const char *uri );
+
+struct uri {
+ char *scheme;
+ char *host;
+ int port;
+ char *path;
+};
+
+int uri_parse( const char *uri, struct uri *parsed,
+ const struct uri *defaults );
+
+void uri_free( struct uri *parsed );
+
+#endif URI_H
diff --git a/neon/src/xalloc.c b/neon/src/xalloc.c
new file mode 100644
index 000000000..95e3081be
--- /dev/null
+++ b/neon/src/xalloc.c
@@ -0,0 +1,56 @@
+/*
+ Replacement memory allocation handling etc.
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+ Id: xalloc.c,v 1.1 2000/05/09 22:29:56 joe Exp
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "xalloc.h"
+
+void *xmalloc( size_t len )
+{
+ void *ptr = malloc(len);
+ if( !ptr ) {
+ /* uh-oh */
+ abort();
+ }
+ return ptr;
+}
+
+char *xstrdup( const char *s )
+{
+ return strcpy( xmalloc( strlen(s) + 1 ), s );
+}
+
+char *xstrndup( const char *s, size_t n )
+{
+ char *new = xmalloc( n + 1 );
+ new[n] = '\0';
+ memcpy( new, s, n );
+ return new;
+}
diff --git a/neon/src/xalloc.h b/neon/src/xalloc.h
new file mode 100644
index 000000000..51b21cb34
--- /dev/null
+++ b/neon/src/xalloc.h
@@ -0,0 +1,33 @@
+/*
+ Replacement memory allocation handling etc.
+ Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef XALLOC_H
+#define XALLOC_H
+
+#include <sys/types.h>
+
+void *xmalloc( size_t len );
+
+char *xstrdup( const char *s );
+
+char *xstrndup( const char *s, size_t n );
+
+#endif /* XALLOC_H */
diff --git a/neon/test/.cvsignore b/neon/test/.cvsignore
new file mode 100644
index 000000000..9cb7ba7dd
--- /dev/null
+++ b/neon/test/.cvsignore
@@ -0,0 +1,3 @@
+tests
+*-tests
+Makefile
diff --git a/neon/test/COPYING b/neon/test/COPYING
new file mode 100644
index 000000000..a43ea2126
--- /dev/null
+++ b/neon/test/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/neon/test/ChangeLog b/neon/test/ChangeLog
new file mode 100644
index 000000000..0ab69b845
--- /dev/null
+++ b/neon/test/ChangeLog
@@ -0,0 +1,51 @@
+Sun Apr 29 14:57:59 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * uri-tests.c (slash): Check behaviour of passing zero-length URI.
+
+Sun Apr 29 13:43:59 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * Makefile.in (clean): New target. (libtest.a): Depend on libneon
+ to force rebuilds when necessary. (all): Build but don't test.
+
+Sun Apr 29 13:41:13 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c: Add status line with leading garbage.
+
+Sun Apr 29 13:39:53 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (status_lines): Add some tests for invalid status
+ lines too.
+
+Sun Apr 29 13:38:31 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (main): Use basename(argv[0]) as suite name. Fail if no
+ tests are in the functions vector.
+
+Sun Apr 29 11:06:45 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (segv): New function. (main): Add SIGSEGV handler.
+
+Fri Apr 27 00:00:12 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (base64): New test.
+
+Thu Apr 26 22:39:44 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * uri-tests.c (just_hostname, just_path, null_uri): New tests.
+
+Thu Apr 26 22:03:58 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * util-tests.c (md5): Test of MD5 functions.
+
+Mon Apr 23 23:08:02 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * http-tests.c (simple_head): Add HEAD test.
+
+Mon Apr 23 22:49:52 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * http-tests.c (simple_get): Check for EOF after reading response
+ body of HTTP/1.0 GET request.
+
+ (null_resource): New function, test for 404 on null resource.
+
+
diff --git a/neon/test/Makefile.in b/neon/test/Makefile.in
new file mode 100644
index 000000000..7a514574e
--- /dev/null
+++ b/neon/test/Makefile.in
@@ -0,0 +1,55 @@
+
+SHELL = @SHELL@
+CFLAGS = @CFLAGS@ -I. -I../src
+LDFLAGS = -L. @LDFLAGS@ -L../src -L../src/.libs
+DEFS = @DEFS@
+top_builddir = ..
+top_srcdir = @top_srcdir@
+
+VPATH = @srcdir@
+
+AR = ar
+
+RANLIB = @RANLIB@
+LIBS = @LIBS@ @NEON_LIBS@
+CC = @CC@
+
+TESTS = uri-tests util-tests string-tests sock-tests http-tests
+
+LIBTEST = libtest.a
+
+all: $(TESTS)
+
+clean:
+ rm -f $(TESTS) *.o
+
+test: $(TESTS)
+ $(SHELL) run.sh $(TESTS)
+
+Makefile: Makefile.in
+ (cd .. && CONFIG_FILES=test/Makefile ./config.status)
+
+libtest.a: tests.o tests.h ../src/@NEON_TARGET@
+ $(AR) cru $@ tests.o
+ $(RANLIB) $@
+
+uri-tests: uri-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ uri-tests.o -ltest $(LIBS)
+
+util-tests: util-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ util-tests.o -ltest $(LIBS)
+
+string-tests: string-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ string-tests.o -ltest $(LIBS)
+
+sock-tests: sock-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ sock-tests.o -ltest $(LIBS)
+
+http-tests: http-tests.o $(LIBTEST)
+ $(CC) $(LDFLAGS) -o $@ http-tests.o -ltest $(LIBS)
+
+uri-tests.o: uri-tests.c tests.h
+util-tests.o: util-tests.c tests.h
+string-tests: string-tests.c tests.h
+sock-tests.o: sock-tests.c tests.h
+http-tests.o: http-tests.c tests.h
diff --git a/neon/test/README b/neon/test/README
new file mode 100644
index 000000000..02b632cd3
--- /dev/null
+++ b/neon/test/README
@@ -0,0 +1,14 @@
+
+Stupidly Simple Test Suite for neon
+-----------------------------------
+
+http-tests requires that you have a running HTTP server on localhost
+port 80, and you have copied htdocs/* to server-htdocs-root/test/*
+
+The test code is licensed under the GPL.
+
+Credits
+-------
+
+This test suite is inspired by the Subversion project and discussion
+on the subversion mailing list.
diff --git a/neon/test/STATUS b/neon/test/STATUS
new file mode 100644
index 000000000..efb5c34b6
--- /dev/null
+++ b/neon/test/STATUS
@@ -0,0 +1,79 @@
+ -*- text -*-
+
+This document attempts to list RFC requirements and determine whether
+neon meets them, or where they do not apply, etc.
+
+ Yes: test written, succeeds
+ No: test written, but currently fails
+ ???: no test written
+ ---: feature not supported
+ App: this is an application issue not a neon issue
+
+ RFC2616
+ =======
+
+3.1: MUST treat major/minor as separate digits Yes
+3.1: MUST ignore leading zeros Yes
+3.1: MUST only send HTTP/1.1 when appropriate ???
+
+3.2.2: MUST use abs_path of "/" in Request-URI App
+3.2.3: comparisons of host names MUST be case-insensitive --- [1]
+ comparisons of scheme names MUST be ... ---
+ comparison of empty abs_path equivalent to "/" No/---
+
+3.3.1: MUST accept three date formats App/Yes [2]
+ MUST only generate RFC1123-style dates App
+
+3.3.1: MUST use GMT for http-dates ???
+ MUST assume GMT when parsing asctime dates ???
+
+3.4*: character set handling App/??? [3]
+
+3.5*: content codings App
+
+3.6: MUST requirements for multiple transfer-codings --- [4]
+
+3.6.1: parsing of chunked transfer coding ???
+ TODO: translate that section into requirements
+
+3.6.1: MUST be able to handle "chunked" transfer-coding Yes
+ MUST ignore unknown chunk-extension extensions ???
+
+3.7: parsing of Content-Type headers ???
+ TODO: translate section into requirements
+3.7: MUST NOT have LWS between type/subtype in C-T hdr App
+ SHOULD only send parameters to "new HTTP apps" (>1.0?) App
+
+3.7.1: MUST represent HTTP message in canonical form App
+ MUST accept CRLF/CR/LF as line-breaks in text/* media App
+ MUST NOT use only CR or LF in HTTP control structures ???
+ MUST specify charset if not ISO-8859-1 App
+
+3.7.2: multipart types ---
+
+3.8: SHOULD have short product token Yes/App [5]
+ SHOULD use product-version for version identifier Yes/App
+ only product-version differs between versions Yes/App
+
+3.9: Content Negotiation ---/App
+
+3.10: Language Tags ---/App
+
+3.11: Entity Tags ---/App
+
+
+
+
+
+[1]: these are only applicable if neon had a full URI comparison
+suite.
+
+[2]: date parser is provided which handles all three formats, but no
+handling of the Date header is present within neon.
+
+[3]: not sure if neon should be handling of this internally.
+
+[4]: neon only supports using just chunked Transfer-Coding or none.
+
+[5]: these reflect that applications may add their own product tokens
+ alongside neon's.
diff --git a/neon/test/acl.c b/neon/test/acl.c
new file mode 100644
index 000000000..1b1efa76c
--- /dev/null
+++ b/neon/test/acl.c
@@ -0,0 +1,101 @@
+/*
+ Dummy ACL tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "ne_acl.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+/**** DUMMY TESTS: just makes sure the stuff doesn't dump core. */
+
+static int test_acl(const char *uri, ne_acl_entry *es, int nume)
+{
+ ne_session *sess = ne_session_create();
+
+ ne_session_server(sess, "localhost", 7777);
+ ON(spawn_server(7777, single_serve_string,
+ "HTTP/1.1 200 OK\r\n"
+ "Connection: close\r\n\r\n"));
+
+ ON(ne_acl_set(sess, uri, es, nume));
+
+ CALL(await_server());
+
+ return OK;
+}
+
+static int grant_all(void)
+{
+ ne_acl_entry e;
+
+ e.apply = ne_acl_all;
+ e.type = ne_acl_grant;
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+static int deny_all(void)
+{
+ ne_acl_entry e;
+
+ e.apply = ne_acl_all;
+ e.type = ne_acl_deny;
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+static int deny_one(void)
+{
+ ne_acl_entry e;
+
+ e.apply = ne_acl_href;
+ e.type = ne_acl_deny;
+ e.principal = "http://webdav.org/users/joe";
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+static int deny_byprop(void)
+{
+ ne_acl_entry e;
+
+ e.apply = ne_acl_property;
+ e.type = ne_acl_deny;
+ e.principal = "owner";
+
+ CALL(test_acl("/foo", &e, 1));
+
+ return OK;
+}
+
+ne_test tests[] = {
+ T(grant_all),
+ T(deny_all),
+ T(deny_one),
+ T(deny_byprop),
+ T(NULL)
+};
diff --git a/neon/test/auth.c b/neon/test/auth.c
new file mode 100644
index 000000000..729f55bf2
--- /dev/null
+++ b/neon/test/auth.c
@@ -0,0 +1,258 @@
+/*
+ Authentication tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_auth.h"
+#include "ne_basic.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+char username[BUFSIZ], password[BUFSIZ];
+static int auth_failed;
+
+static const char www_wally[] = "WWW-Authenticate: Basic realm=WallyWorld";
+
+static int auth_cb(void *userdata, const char *realm, int tries,
+ char *un, char *pw)
+{
+ strcpy(un, username);
+ strcpy(pw, password);
+ return tries;
+}
+
+static void auth_hdr(char *value)
+{
+#define B "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
+ NE_DEBUG(NE_DBG_HTTP, "Got auth header: [%s]\n", value);
+ NE_DEBUG(NE_DBG_HTTP, "Wanted header: [%s]\n", B);
+ auth_failed = strcmp(value, B);
+#undef B
+}
+
+/* Sends a response with given response-code. If hdr is not NULL,
+ * sends that header string too (appending an EOL). If eoc is
+ * non-zero, request must be last sent down a connection; otherwise,
+ * clength 0 is sent to maintain a persistent connection. */
+static int send_response(nsocket *sock, const char *hdr, int code, int eoc)
+{
+ char buffer[BUFSIZ];
+
+ sprintf(buffer, "HTTP/1.1 %d Blah Blah" EOL, code);
+
+ if (hdr) {
+ strcat(buffer, hdr);
+ strcat(buffer, EOL);
+ }
+
+ if (eoc) {
+ strcat(buffer, "Connection: close" EOL EOL);
+ } else {
+ strcat(buffer, "Content-Length: 0" EOL EOL);
+ }
+
+ return sock_send_string(sock, buffer);
+}
+
+static int auth_serve(nsocket *sock, void *userdata)
+{
+ auth_failed = 0;
+
+ /* Register globals for discard_request. */
+ got_header = auth_hdr;
+ want_header = "Authorization";
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, auth_failed?500:200, 1);
+
+ return 0;
+}
+
+static int basic(void)
+{
+ int ret;
+ ne_session *sess = ne_session_create();
+
+ ne_session_server(sess, "localhost", 7777);
+
+ ne_set_server_auth(sess, auth_cb, NULL);
+
+ strcpy(username, "Aladdin");
+ strcpy(password, "open sesame");
+
+ CALL(spawn_server(7777, auth_serve, NULL));
+
+ ret = any_request(sess, "/norman");
+
+ ne_session_destroy(sess);
+
+ CALL(await_server());
+
+ ONREQ(ret);
+ return OK;
+}
+
+static int retry_serve(nsocket *sock, void *ud)
+{
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ discard_request(sock);
+ send_response(sock, www_wally, 401, 0);
+
+ discard_request(sock);
+ send_response(sock, NULL, 200, 0);
+
+ return OK;
+}
+
+static int retry_cb(void *userdata, const char *realm, int tries,
+ char *un, char *pw)
+{
+ int *count = userdata;
+
+ /* dummy creds; server ignores them anyway. */
+ strcpy(un, "a");
+ strcpy(pw, "b");
+
+ switch (*count) {
+ case 0:
+ case 1:
+ if (tries == *count) {
+ *count += 1;
+ return 0;
+ } else {
+ t_context("On request #%d, got attempt #%d", *count, tries);
+ *count = -1;
+ return 1;
+ }
+ break;
+ case 2:
+ case 3:
+ /* server fails a subsequent request, check that tries has
+ * reset to zero. */
+ if (tries == 0) {
+ *count += 1;
+ return 0;
+ } else {
+ t_context("On retry after failure #%d, tries was %d",
+ *count, tries);
+ *count = -1;
+ return 1;
+ }
+ break;
+ default:
+ t_context("Count reached %d!?", *count);
+ *count = -1;
+ }
+ return 1;
+}
+
+/* Test that auth retries are working correctly. */
+static int retries(void)
+{
+ ne_session *sess = ne_session_create();
+ int count = 0;
+
+ ne_session_server(sess, "localhost", 7777);
+ ne_set_server_auth(sess, retry_cb, &count);
+
+ CALL(spawn_server(7777, retry_serve, NULL));
+
+ /* This request will be 401'ed twice, then succeed. */
+ ONREQ(any_request(sess, "/foo"));
+
+ /* auth_cb will have set up context. */
+ CALL(count != 2);
+
+ /* this request will be 401'ed once, then succeed. */
+ ONREQ(any_request(sess, "/foo"));
+
+ /* auth_cb will have set up context. */
+ CALL(count != 3);
+
+ /* some 20x requests. */
+ ONREQ(any_request(sess, "/foo"));
+ ONREQ(any_request(sess, "/foo"));
+
+ /* this request will be 401'ed once, then succeed. */
+ ONREQ(any_request(sess, "/foo"));
+
+ /* auth_cb will have set up context. */
+ CALL(count != 4);
+
+ ne_session_destroy(sess);
+
+ CALL(await_server());
+
+ return OK;
+}
+
+/* test digest auth 2068-style. */
+
+/* test digest auth 2617-style. */
+
+/* negotiation: within a single header, multiple headers.
+ * check digest has precedence */
+
+/* test auth-int, auth-int FAILURE. chunk trailers/non-trailer */
+
+/* test logout */
+
+/* proxy auth, proxy AND origin */
+
+ne_test tests[] = {
+ T(basic),
+ T(retries),
+ T(NULL)
+};
diff --git a/neon/test/basic.c b/neon/test/basic.c
new file mode 100644
index 000000000..84be1ae6f
--- /dev/null
+++ b/neon/test/basic.c
@@ -0,0 +1,101 @@
+/*
+ neon test suite
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_basic.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int content_type(void)
+{
+ int n;
+ struct {
+ const char *value, *type, *subtype, *charset;
+ } ctypes[] = {
+ { "text/plain", "text", "plain", NULL },
+ { "text/plain ", "text", "plain", NULL },
+ { "application/xml", "application", "xml", NULL },
+#undef TXU
+#define TXU "text", "xml", "utf-8"
+ /* 2616 doesn't *say* that charset can be quoted, but bets are
+ * that some servers do it anyway. */
+ { "text/xml; charset=utf-8", TXU },
+ { "text/xml; charset=utf-8; foo=bar", TXU },
+ { "text/xml;charset=utf-8", TXU },
+ { "text/xml ;charset=utf-8", TXU },
+ { "text/xml;charset=utf-8;foo=bar", TXU },
+ { "text/xml; foo=bar; charset=utf-8", TXU },
+ { "text/xml; foo=bar; charset=utf-8; bar=foo", TXU },
+ { "text/xml; charset=\"utf-8\"", TXU },
+ { "text/xml; charset='utf-8'", TXU },
+ { "text/xml; foo=bar; charset=\"utf-8\"; bar=foo", TXU },
+#undef TXU
+ /* badly quoted charset should come out as NULL */
+ { "text/xml; charset=\"utf-8", "text", "xml", NULL },
+ { NULL }
+ };
+
+ for (n = 0; ctypes[n].value != NULL; n++) {
+ ne_content_type ct = {0};
+
+ ne_content_type_handler(&ct, ctypes[n].value);
+
+ ONV(strcmp(ct.type, ctypes[n].type),
+ ("for `%s': type was `%s'", ctypes[n].value, ct.type));
+
+ ONV(strcmp(ct.subtype, ctypes[n].subtype),
+ ("for `%s': subtype was `%s'", ctypes[n].value, ct.subtype));
+
+ ONV(ctypes[n].charset && ct.charset == NULL,
+ ("for `%s': charset unset", ctypes[n].value));
+
+ ONV(ctypes[n].charset == NULL && ct.charset != NULL,
+ ("for `%s': unexpected charset `%s'", ctypes[n].value,
+ ct.charset));
+
+ ONV(ctypes[n].charset && ct.charset &&
+ strcmp(ctypes[n].charset, ct.charset),
+ ("for `%s': charset was `%s'", ctypes[n].value, ct.charset));
+
+ NE_FREE(ct.value);
+ }
+
+ return OK;
+}
+
+ne_test tests[] = {
+ T(content_type), /* test functions here */
+
+ /* end of test functions. */
+ T(NULL)
+};
+
diff --git a/neon/test/child.c b/neon/test/child.c
new file mode 100644
index 000000000..1014ef06d
--- /dev/null
+++ b/neon/test/child.c
@@ -0,0 +1,160 @@
+/*
+ Framework for testing with a server process
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <signal.h>
+
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+
+static pid_t child = 0;
+
+static struct in_addr lh_addr;
+
+int lookup_localhost(void)
+{
+ if (sock_name_lookup("localhost", &lh_addr))
+ return FAILHARD;
+
+ return OK;
+}
+
+static nsocket *server_socket(int port)
+{
+ int ls = socket(PF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr;
+ nsocket *s;
+ int val = 1;
+
+ setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int));
+
+ addr.sin_addr = lh_addr;
+ addr.sin_port = htons(port);
+
+ if (bind(ls, (struct sockaddr *)&addr, sizeof(addr))) {
+ printf("bind failed: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (listen(ls, 5)) {
+ printf("listen failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ s = sock_accept(ls);
+ close(ls);
+
+ return s;
+}
+
+static void minisleep(void)
+{
+ sleep(1);
+}
+
+/* This runs as the child process. */
+static int server_child(int port, server_fn callback, void *userdata)
+{
+ nsocket *s;
+ int ret;
+
+ in_child();
+
+ s = server_socket(port);
+
+ if (s == NULL)
+ return FAIL;
+
+ ret = callback(s, userdata);
+
+ sock_close(s);
+
+ return ret;
+}
+
+
+int spawn_server(int port, server_fn fn, void *ud)
+{
+ child = fork();
+
+ ONN("fork server", child == -1);
+
+ if (child == 0) {
+ /* this is the child. */
+ int ret = server_child(port, fn, ud);
+
+ /* print the error out otherwise it gets lost. */
+ if (ret) {
+ printf("server child failed: %s\n", name);
+ }
+
+ /* and quit the child. */
+ exit(ret);
+ } else {
+ /* this is the parent... sleep for a bit to let the parent
+ * get itself into gear. */
+ minisleep();
+ return OK;
+ }
+}
+
+int await_server(void)
+{
+ int status;
+
+ (void) wait(&status);
+
+ i_am("error from server process");
+
+ /* so that we aren't reaped by mistake. */
+ child = 0;
+
+ return WEXITSTATUS(status);
+}
+
+int reap_server(void)
+{
+ int status;
+
+ if (child != 0) {
+ (void) kill(child, SIGTERM);
+ minisleep();
+ (void) wait(&status);
+ }
+
+ return OK;
+}
diff --git a/neon/test/child.h b/neon/test/child.h
new file mode 100644
index 000000000..24121f1c3
--- /dev/null
+++ b/neon/test/child.h
@@ -0,0 +1,47 @@
+/*
+ Framework for testing with a server process
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CHILD_H
+#define CHILD_H 1
+
+/* Test which does DNS lookup on "localhost": this must be the first
+ * named test. */
+int lookup_localhost(void);
+
+/* Callback for spawn_server. */
+typedef int (*server_fn)(nsocket *sock, void *userdata);
+
+/* Spawns server child process:
+ * - forks child process.
+ * - child process listens on localhost at given port.
+ * - when you connect to it, 'fn' is run...
+ * fn is passed the client/server socket as first argument,
+ * and userdata as second.
+ * - the socket is closed when 'fn' returns, so don't close in in 'fn'.
+ */
+int spawn_server(int port, server_fn fn, void *userdata);
+
+/* Blocks until child process exits, and gives return code of 'fn'. */
+int await_server(void);
+
+/* Kills child process. */
+int reap_server(void);
+
+#endif /* CHILD_H */
diff --git a/neon/test/common/ChangeLog b/neon/test/common/ChangeLog
new file mode 100644
index 000000000..a7ca54d38
--- /dev/null
+++ b/neon/test/common/ChangeLog
@@ -0,0 +1,31 @@
+Mon Aug 27 00:31:09 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (test_num): Expose test counter. (segv): Handle
+ segfault nicely.
+
+Mon Aug 27 00:30:20 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * child.c (discard_request): New function, from request.c in
+ neon/test.
+
+Wed Aug 8 22:09:21 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c, test.h: Only define test_argc/argv once.
+
+Mon Jul 16 16:30:28 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (warning): Take printf-style arguments list.
+
+Mon Jul 16 16:16:08 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (main): Cope with skipped tests properly.
+
+Mon Jul 16 16:00:59 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (warning): New function. (main): Cope with warnings.
+
+Sun Jul 8 16:09:33 2001 Joe Orton <joe@manyfish.co.uk>
+
+ * tests.c (main): Export argc/argv as test_argc, test_argv.
+
+
diff --git a/neon/test/common/README b/neon/test/common/README
new file mode 100644
index 000000000..24c661483
--- /dev/null
+++ b/neon/test/common/README
@@ -0,0 +1,3 @@
+
+Common test code directory for neon.
+
diff --git a/neon/test/common/child.c b/neon/test/common/child.c
new file mode 100644
index 000000000..5331548d4
--- /dev/null
+++ b/neon/test/common/child.c
@@ -0,0 +1,202 @@
+/*
+ Framework for testing with a server process
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <signal.h>
+
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+
+static pid_t child = 0;
+
+static struct in_addr lh_addr;
+
+/* If we have pipe(), then use a pipe between the parent and child to
+ * know when the child is ready to accept incoming connections.
+ * Otherwise use boring sleep()s trying to avoid the race condition
+ * between listen() and connect() in the two processes. */
+#ifdef HAVE_PIPE
+#define USE_PIPE 1
+#endif
+
+int lookup_localhost(void)
+{
+ if (sock_name_lookup("localhost", &lh_addr))
+ return FAILHARD;
+
+ return OK;
+}
+
+static nsocket *server_socket(int readyfd, int port)
+{
+ int ls = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr;
+ nsocket *s;
+ int val = 1;
+
+ setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int));
+
+ addr.sin_addr = lh_addr;
+ addr.sin_port = htons(port);
+ addr.sin_family = AF_INET;
+
+ if (bind(ls, (struct sockaddr *)&addr, sizeof(addr))) {
+ printf("bind failed: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (listen(ls, 5)) {
+ printf("listen failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+#ifdef USE_PIPE
+ /* wakey wakey. */
+ write(readyfd, "a", 1);
+#endif
+
+ s = sock_accept(ls);
+ close(ls);
+
+ return s;
+}
+
+static void minisleep(void)
+{
+ sleep(1);
+}
+
+/* This runs as the child process. */
+static int server_child(int readyfd, int port,
+ server_fn callback, void *userdata)
+{
+ nsocket *s;
+ int ret;
+
+ in_child();
+
+ s = server_socket(readyfd, port);
+
+ if (s == NULL)
+ return FAIL;
+
+ ret = callback(s, userdata);
+
+ sock_close(s);
+
+ return ret;
+}
+
+int spawn_server(int port, server_fn fn, void *ud)
+{
+ int fds[2];
+
+#ifdef USE_PIPE
+ if (pipe(fds)) {
+ perror("spawn_server: pipe");
+ return FAIL;
+ }
+#else
+ /* avoid using uninitialized variable. */
+ fds[0] = fds[1] = 0;
+#endif
+
+ child = fork();
+
+ ONN("fork server", child == -1);
+
+ if (child == 0) {
+ /* this is the child. */
+ int ret;
+
+ ret = server_child(fds[1], port, fn, ud);
+
+#ifdef USE_PIPE
+ close(fds[0]);
+ close(fds[1]);
+#endif
+
+ /* print the error out otherwise it gets lost. */
+ if (ret) {
+ printf("server child failed: %s\n", name);
+ }
+
+ /* and quit the child. */
+ exit(ret);
+ } else {
+ char ch;
+
+#ifdef USE_PIPE
+ if (read(fds[0], &ch, 1) < 0)
+ perror("parent read");
+
+ close(fds[0]);
+ close(fds[1]);
+#else
+ minisleep();
+#endif
+
+ return OK;
+ }
+}
+
+int await_server(void)
+{
+ int status;
+
+ (void) wait(&status);
+
+ i_am("error from server process");
+
+ /* so that we aren't reaped by mistake. */
+ child = 0;
+
+ return WEXITSTATUS(status);
+}
+
+int reap_server(void)
+{
+ int status;
+
+ if (child != 0) {
+ (void) kill(child, SIGTERM);
+ minisleep();
+ (void) wait(&status);
+ }
+
+ return OK;
+}
diff --git a/neon/test/common/child.h b/neon/test/common/child.h
new file mode 100644
index 000000000..24121f1c3
--- /dev/null
+++ b/neon/test/common/child.h
@@ -0,0 +1,47 @@
+/*
+ Framework for testing with a server process
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CHILD_H
+#define CHILD_H 1
+
+/* Test which does DNS lookup on "localhost": this must be the first
+ * named test. */
+int lookup_localhost(void);
+
+/* Callback for spawn_server. */
+typedef int (*server_fn)(nsocket *sock, void *userdata);
+
+/* Spawns server child process:
+ * - forks child process.
+ * - child process listens on localhost at given port.
+ * - when you connect to it, 'fn' is run...
+ * fn is passed the client/server socket as first argument,
+ * and userdata as second.
+ * - the socket is closed when 'fn' returns, so don't close in in 'fn'.
+ */
+int spawn_server(int port, server_fn fn, void *userdata);
+
+/* Blocks until child process exits, and gives return code of 'fn'. */
+int await_server(void);
+
+/* Kills child process. */
+int reap_server(void);
+
+#endif /* CHILD_H */
diff --git a/neon/test/common/run.sh b/neon/test/common/run.sh
new file mode 100755
index 000000000..01ed5a9c7
--- /dev/null
+++ b/neon/test/common/run.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+rm -f debug.log
+rm -f child.log
+
+# for shared builds.
+LD_LIBRARY_PATH=../src/.libs:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+
+for f in $*; do
+ if ./$f; then
+ :
+ else
+ echo FAILURE
+ exit 1
+ fi
+done
+
+exit 0
diff --git a/neon/test/common/tests.c b/neon/test/common/tests.c
new file mode 100644
index 000000000..b24e6d34f
--- /dev/null
+++ b/neon/test/common/tests.c
@@ -0,0 +1,152 @@
+/*
+ Stupidly simple test framework
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/signal.h>
+
+#include <stdio.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ne_utils.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+
+const char *name = NULL;
+static FILE *child_debug;
+
+static int passes = 0, fails = 0, aborted = 0;
+static const char *suite;
+
+void i_am(const char *testname)
+{
+ name = testname;
+}
+
+#define TEST_DEBUG (NE_DBG_HTTP | NE_DBG_SOCKET)
+
+#define W(m) write(0, m, strlen(m))
+
+static void child_segv(int signo)
+{
+ signal(SIGSEGV, SIG_DFL);
+ W("(possible segfault in child, not dumping core)\n");
+ exit(-1);
+}
+
+void in_child(void)
+{
+ ne_debug_init(child_debug, TEST_DEBUG);
+ NE_DEBUG(TEST_DEBUG, "**** Child forked ****\n");
+ signal(SIGSEGV, child_segv);
+}
+
+int main(int argc, char *argv[])
+{
+ int n;
+ FILE *debug;
+
+ /* get basename(argv[0]) */
+ suite = strrchr(argv[0], '/');
+ if (suite == NULL) {
+ suite = argv[0];
+ } else {
+ suite++;
+ }
+
+ sock_init();
+
+ debug = fopen("debug.log", "a");
+ if (debug == NULL) {
+ fprintf(stderr, "%s: Could not open debug.log: %s\n", suite,
+ strerror(errno));
+ return -1;
+ }
+ child_debug = fopen("child.log", "a");
+ if (child_debug == NULL) {
+ fprintf(stderr, "%s: Could not open child.log: %s\n", suite,
+ strerror(errno));
+ fclose(debug);
+ return -1;
+ }
+
+ ne_debug_init(debug, TEST_DEBUG);
+
+ if (tests[0] == NULL) {
+ printf("-> no tests found in %s\n", suite);
+ return -1;
+ }
+
+ printf("-> running %s:\n", suite);
+
+ for (n = 0; !aborted && tests[n] != NULL; n++) {
+ printf("%d: ", n);
+ name = NULL;
+ fflush(stdout);
+ NE_DEBUG(TEST_DEBUG, "******* Running test %d ********\n", n);
+ switch (tests[n]()) {
+ case OK:
+ printf("pass.\n");
+ passes++;
+ break;
+ case FAILHARD:
+ aborted = 1;
+ /* fall-through */
+ case FAIL:
+ if (name != NULL) {
+ printf("%s - ", name);
+ }
+ printf("FAIL.\n");
+ fails++;
+ break;
+ }
+ }
+
+ printf("-> summary for %s: of %d tests: %d passed, %d failed. %.1f%%\n",
+ suite, n, passes, fails, 100*(float)passes/n);
+
+ if (fclose(debug)) {
+ fprintf(stderr, "Error closing debug.log: %s\n", strerror(errno));
+ fails = 1;
+ }
+
+ if (fclose(child_debug)) {
+ fprintf(stderr, "Error closing child.log: %s\n", strerror(errno));
+ fails = 1;
+ }
+
+ return fails;
+}
+
diff --git a/neon/test/common/tests.h b/neon/test/common/tests.h
new file mode 100644
index 000000000..a299148f4
--- /dev/null
+++ b/neon/test/common/tests.h
@@ -0,0 +1,60 @@
+/*
+ Stupidly simple test framework
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef TESTS_H
+#define TESTS_H 1
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <stdio.h>
+
+/* prototype of test function. */
+typedef int (*test_func)(void);
+
+/* array of test functions to call. */
+extern test_func tests[];
+
+void i_am(const char *testname);
+
+#define OK 0
+#define FAIL 1
+#define FAILHARD 2 /* fail and skip succeeding tests. */
+
+extern const char *name;
+
+/* are __FUNCTION__ and __LINE__ gcc-isms? Probably. */
+
+static char on_err_buf[BUFSIZ];
+
+/* child process should call this. */
+void in_child(void);
+
+#define ON(x) do { sprintf(on_err_buf, "%s line %d", __FUNCTION__, __LINE__ ); i_am(on_err_buf); if ((x)) return FAIL; } while(0)
+
+#define TESTFUNC do { i_am(__FUNCTION__); } while(0)
+
+#define ONN(n,x) do { i_am(n); if ((x)) return FAIL; } while(0)
+
+#define CALL(x) do { int ret = (x); if (ret != OK) return ret; } while (0)
+
+#endif /* TESTS_H */
diff --git a/neon/test/compress.c b/neon/test/compress.c
new file mode 100644
index 000000000..1e33d3f1e
--- /dev/null
+++ b/neon/test/compress.c
@@ -0,0 +1,183 @@
+/*
+ tests for compressed response handling.
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+
+#include "tests.h"
+
+#ifndef NEON_ZLIB
+
+static int skip_compress(void)
+{
+ return SKIP;
+}
+
+test_func tests[] = {
+ skip_compress,
+ NULL
+};
+
+#else
+
+#include "ne_compress.h"
+
+#include "child.h"
+#include "utils.h"
+
+static int failed = 0;
+
+static void reader(void *ud, const char *block, size_t len)
+{
+ const char **pnt = ud;
+
+ if (memcmp(*pnt, block, len) != 0)
+ failed = 1;
+
+ *pnt += len;
+}
+
+static int file2buf(int fd, ne_buffer *buf)
+{
+ char buffer[BUFSIZ];
+ ssize_t n;
+
+ while ((n = read(fd, buffer, fd)) > 0) {
+ ne_buffer_append(buf, buffer, n);
+ }
+
+ return 0;
+}
+
+static int fetch(const char *realfn, const char *gzipfn, int chunked)
+{
+ ne_session *sess = ne_session_create();
+ ne_request *req;
+ int fd;
+ ne_buffer *buf = ne_buffer_create();
+ const char *pnt;
+ struct serve_file_args sfargs;
+ ne_decompress *dc;
+
+ fd = open(realfn, O_RDONLY);
+ ONN("failed to open file", fd < 0);
+ file2buf(fd, buf);
+ (void) close(fd);
+ pnt = buf->data;
+
+ ne_session_server(sess, "localhost", 7777);
+
+ if (gzipfn) {
+ sfargs.fname = gzipfn;
+ sfargs.headers = "Content-Encoding: gzip\r\n";
+ } else {
+ sfargs.fname = realfn;
+ sfargs.headers = NULL;
+ }
+ sfargs.chunks = chunked;
+
+ CALL(spawn_server(7777, serve_file, &sfargs));
+
+ req = ne_request_create(sess, "GET", "/");
+ dc = ne_decompress_reader(req, ne_accept_2xx, reader, &pnt);
+
+ ONREQ(ne_request_dispatch(req));
+
+ ONN("error during decompress", ne_decompress_destroy(dc));
+
+ ne_request_destroy(req);
+ ne_session_destroy(sess);
+ ne_buffer_destroy(buf);
+
+ CALL(await_server());
+
+ ONN("inflated response compare", failed);
+
+ return OK;
+}
+
+/* Test the no-compression case. */
+static int compress_0(void)
+{
+ return fetch("../NEWS", NULL, 0);
+}
+
+static int compress_1(void)
+{
+ return fetch("../NEWS", "file1.gz", 0);
+}
+
+/* file1.gz has an embedded filename. */
+static int compress_2(void)
+{
+ return fetch("../NEWS", "file2.gz", 0);
+}
+
+/* deliver various different sizes of chunks: tests the various
+ * decoding cases. */
+static int compress_3(void)
+{
+ return fetch("../NEWS", "file2.gz", 1);
+}
+
+static int compress_4(void)
+{
+ return fetch("../NEWS", "file1.gz", 1);
+}
+
+static int compress_5(void)
+{
+ return fetch("../NEWS", "file2.gz", 12);
+}
+
+static int compress_6(void)
+{
+ return fetch("../NEWS", "file2.gz", 20);
+}
+
+static int compress_7(void)
+{
+ return fetch("../NEWS", "file1.gz", 10);
+}
+
+static int compress_8(void)
+{
+ return fetch("../NEWS", "file2.gz", 10);
+}
+
+test_func tests[] = {
+ compress_0,
+ compress_1,
+ compress_2,
+ compress_3,
+ compress_4,
+ compress_5,
+ compress_6,
+ compress_7,
+ compress_8,
+ NULL,
+};
+
+#endif /* NEON_ZLIB */
diff --git a/neon/test/cookies.c b/neon/test/cookies.c
new file mode 100644
index 000000000..a0ce3ed89
--- /dev/null
+++ b/neon/test/cookies.c
@@ -0,0 +1,140 @@
+/*
+ Test for cookies interface (ne_cookies.h)
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_cookies.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int serve_cookie(ne_socket *sock, void *ud)
+{
+ const char *hdr = ud;
+ char buf[BUFSIZ];
+
+ CALL(discard_request(sock));
+
+ ne_snprintf(buf, BUFSIZ, "HTTP/1.0 200 Okey Dokey\r\n"
+ "Connection: close\r\n" "%s\r\n\r\n", hdr);
+
+ SEND_STRING(sock, buf);
+
+ return OK;
+}
+
+static int fetch_cookie(const char *hdr, const char *path,
+ ne_cookie_cache *jar)
+{
+ ne_session *sess;
+
+ CALL(make_session(&sess, serve_cookie, (void *)hdr));
+
+ ne_cookie_register(sess, jar);
+
+ CALL(any_request(sess, path));
+
+ ne_session_destroy(sess);
+ CALL(await_server());
+
+ return OK;
+}
+
+static int parsing(void)
+{
+ static const struct {
+ const char *hdr, *name, *value;
+ } cookies[] = {
+ { "Set-Cookie: alpha=bar", "alpha", "bar" },
+ { "Set-Cookie2: alpha=bar", "alpha", "bar" },
+ { "Set-Cookie: beta = bar", "beta", "bar" },
+ { "Set-Cookie: delta = bar; norman=fish", "delta", "bar" },
+ { NULL, NULL, NULL }
+ };
+ int n;
+
+ for (n = 0; cookies[n].hdr != NULL; n++) {
+ ne_cookie_cache jar = {0};
+ ne_cookie *ck;
+
+ CALL(fetch_cookie(cookies[n].hdr, "/foo", &jar));
+
+ ck = jar.cookies;
+ ONV(ck == NULL, ("%d: cookie jar was empty!", n));
+
+ ONV(strcmp(ck->name, cookies[n].name) ||
+ strcmp(ck->value, cookies[n].value),
+ ("%d: was [%s]=[%s]!", n, ck->name, ck->value));
+ }
+
+ return OK;
+}
+
+static int rejects(void)
+{
+ static const struct {
+ const char *hdr, *path;
+ } resps[] = {
+ /* names prefixed with $ are illegal */
+ { "Set-Cookie2: $foo=bar, Version=1", "/foo" },
+#define FOOBAR "Set-Cookie2: foo=bar, Version=1"
+ /* Path is not prefix of Request-URI */
+ { FOOBAR ", Path=/bar", "/foo" },
+ /* Domain must have embedded dots. */
+ { FOOBAR ", Domain=fish", "/foo" },
+ /* Domain must domain-match request-host */
+ { FOOBAR ", Domain=other.host.com", "/foo" },
+ /* Port not named in Port list */
+ { FOOBAR ", Port=\"12\"", "/foo" },
+ { NULL, NULL }
+ };
+ int n;
+
+ for (n = 0; resps[n].hdr != NULL; n++) {
+ ne_cookie_cache jar = {0};
+
+ CALL(fetch_cookie(resps[n].hdr, resps[n].path, &jar));
+
+ ONV(jar.cookies != NULL,
+ ("cookie was returned for `%s'", resps[n].hdr));
+ }
+
+ return OK;
+}
+
+ne_test tests[] = {
+ T(lookup_localhost),
+ T(parsing),
+ T(rejects),
+ T(NULL)
+};
+
diff --git a/neon/test/expired.pem b/neon/test/expired.pem
new file mode 100644
index 000000000..0062cc8c3
--- /dev/null
+++ b/neon/test/expired.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMB4XDTAyMDEyMTIwMzkwNFoXDTAyMDEzMTIwMzkwNFowgaExCzAJBgNVBAYT
+AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
+MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
+ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
+ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
+oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
+AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
+VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
+Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
+IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
+ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQBDSFbe
+9EjP+IyZ4vhJSk66gLSN8CnafoGm5JHpNOHy5gWLh7j0a/dxWRd4gpoBYBB6Y9rO
+YV6Eq3njdj0gu+NN
+-----END CERTIFICATE-----
diff --git a/neon/test/htdocs/plain b/neon/test/htdocs/plain
new file mode 100644
index 000000000..bce1946c2
--- /dev/null
+++ b/neon/test/htdocs/plain
@@ -0,0 +1 @@
+Test file.
diff --git a/neon/test/http-tests.c b/neon/test/http-tests.c
new file mode 100644
index 000000000..6ae4a43bd
--- /dev/null
+++ b/neon/test/http-tests.c
@@ -0,0 +1,195 @@
+/*
+ HTTP server tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* These don't really test neon, they test an HTTP server. */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "nsocket.h"
+#include "http_utils.h"
+
+#include "tests.h"
+
+struct in_addr addr;
+nsocket *sock;
+
+char buffer[BUFSIZ];
+int clength;
+
+#define HOSTNAME "localhost"
+
+static int hostname_lookup(void) {
+ if (sock_name_lookup(HOSTNAME, &addr))
+ return FAILHARD;
+ return OK;
+}
+
+static int connect_to_server(void) {
+ sock = sock_connect(addr, 80);
+ if (sock == NULL)
+ return FAILHARD;
+ return OK;
+}
+
+static int close_conn(void) {
+ ON(sock_close(sock));
+ return OK;
+}
+
+#define EOL "\r\n"
+
+#define TRACE_REQ "TRACE / HTTP/1.0\r\n" "Host: " HOSTNAME "\r\n\r\n"
+
+#define ROOT_URL "/test/"
+
+#define GET_REQ(url) do { ON(sock_send_string(sock, "GET " url " HTTP/1.0" EOL "Host: " HOSTNAME EOL EOL)); } while (0)
+
+static int trace(void) {
+ CALL(connect_to_server());
+
+ ON(sock_send_string(sock, TRACE_REQ));
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ /* this is true for any Apache server. */
+ ON(strcasecmp(buffer, "HTTP/1.1 200 OK\r\n"));
+ /* read hdrs */
+ do {
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ } while (strcmp(buffer, "\r\n") != 0);
+ /* read body */
+ ON(sock_read(sock, buffer, 1024) < 0);
+ /* this will fail if you have a transparent proxy. */
+ ONN("pristine TRACE loopback",
+ strncmp(buffer, TRACE_REQ, strlen(TRACE_REQ)) != 0);
+
+ CALL(close_conn());
+
+ return OK;
+}
+
+static int basic_syntax(void) {
+ char *value;
+
+ CALL(connect_to_server());
+ GET_REQ(ROOT_URL);
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ ONN("CRLF on status-line", strstr(buffer, EOL) == NULL);
+
+ ONN("HTTP-Version is 1.0 or 1.1",
+ strncasecmp(buffer, "HTTP/1.1", 8) != 0 &&
+ strncasecmp(buffer, "HTTP/1.0", 8));
+
+ ONN("Status-Line syntax", buffer[8] != ' ' || buffer[12] != ' ');
+
+ /* should be a warning if this fails. */
+ ONN("Status-Code is 200",
+ buffer[9] != '2' || buffer[10] != '0' || buffer[11] != '0');
+
+ ONN("Reason-Phrase is present", strlen(buffer) < strlen("HTTP/x.y nnn X" EOL));
+
+ do {
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ ONN("CRLF on request-header", strstr(buffer, EOL) == NULL);
+ if (strcmp(buffer, EOL) != 0) {
+ value = strchr(buffer, ':');
+ ONN("colon in request-header", value == NULL);
+ ONN("field-name in request-header", value == buffer);
+ }
+ } while (strcmp(buffer, EOL) != 0);
+
+ CALL(close_conn());
+
+ return OK;
+}
+
+static int skip_header(void)
+{
+ do {
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ if (strncasecmp(buffer, "content-length:", 15) == 0) {
+ clength = atoi(buffer + 16);
+ }
+ } while (strcmp(buffer, EOL) != 0);
+ return OK;
+}
+
+static int simple_get(void)
+{
+ CALL(connect_to_server());
+ GET_REQ(ROOT_URL "plain");
+ clength = -1;
+ CALL(skip_header());
+ ONN("Content-Length header present", clength == -1);
+ ONN("Content-Length of 'plain'", clength != 11);
+ ONN("read response body", sock_read(sock, buffer, BUFSIZ) != 11);
+ ONN("content of 'plain'", strncmp(buffer, "Test file.\n", 11) != 0);
+
+ /* FIXME: I'm not sure if this is right, actually. */
+ ONN("connection close after GET",
+ sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+
+ CALL(close_conn());
+ return OK;
+}
+
+static int simple_head(void)
+{
+ CALL(connect_to_server());
+ ON(sock_send_string(sock, "HEAD " ROOT_URL "plain HTTP/1.0" EOL
+ "Host: " HOSTNAME EOL EOL));
+ clength = -1;
+ CALL(skip_header());
+ ONN("Content-Length header present", clength == -1);
+ ONN("Content-Length of 'plain'", clength != 11);
+
+ ONN("connection close after HEAD",
+ sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+
+ CALL(close_conn());
+ return OK;
+}
+
+static int null_resource(void)
+{
+ http_status s = {0};
+ CALL(connect_to_server());
+ GET_REQ(ROOT_URL "nothing-here");
+ ON(sock_readline(sock, buffer, BUFSIZ) < 0);
+ ON(http_parse_statusline(buffer, &s));
+ ONN("null resource gives 404", s.code != 404);
+ CALL(close_conn());
+ return OK;
+}
+
+test_func tests[] = {
+ hostname_lookup,
+ basic_syntax,
+ trace,
+ simple_get,
+ simple_head,
+ null_resource,
+ NULL
+};
diff --git a/neon/test/lock.c b/neon/test/lock.c
new file mode 100644
index 000000000..94e75b527
--- /dev/null
+++ b/neon/test/lock.c
@@ -0,0 +1,101 @@
+/*
+ lock tests
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_locks.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+/* return body of LOCK response for given lock. */
+static char *lock_response(enum ne_lock_scope scope,
+ const char *depth,
+ const char *owner,
+ const char *timeout,
+ const char *token_href)
+{
+ static char buf[BUFSIZ];
+ sprintf(buf,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock>\n"
+ "<D:locktype><D:write/></D:locktype>\n"
+ "<D:lockscope><D:%s/></D:lockscope>\n"
+ "<D:depth>%s</D:depth>\n"
+ "<D:owner>%s</D:owner>\n"
+ "<D:timeout>%s</D:timeout>\n"
+ "<D:locktoken><D:href>%s</D:href></D:locktoken>\n"
+ "</D:activelock></D:lockdiscovery></D:prop>\n",
+ scope==ne_lockscope_exclusive?"exclusive":"shared",
+ depth, owner, timeout, token_href);
+ return buf;
+}
+
+/* regression test for <= 0.18.2, where timeout field was not parsed correctly. */
+static int lock_timeout(void)
+{
+ ne_session *sess = ne_session_create();
+ char *resp, *rbody = lock_response(ne_lockscope_exclusive, "0", "me",
+ "Second-6500", "opaquelocktoken:foo");
+ struct ne_lock lock = {0};
+
+ ON(ne_session_server(sess, "localhost", 7777));
+
+ CONCAT2(resp,
+ "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
+ "Connection: close\r\n\r\n", rbody);
+
+ CALL(spawn_server(7777, single_serve_string, resp));
+
+ lock.uri = "/foo";
+ lock.depth = 0;
+ lock.scope = ne_lockscope_exclusive;
+ lock.type = ne_locktype_write;
+ lock.timeout = 5;
+
+ ONREQ(ne_lock(sess, &lock));
+
+ ONN("lock timeout ignored in response",
+ lock.timeout != 6500);
+
+ ne_session_destroy(sess);
+
+ CALL(await_server());
+
+ return OK;
+}
+
+ne_test tests[] = {
+ T(lock_timeout),
+ T(NULL)
+};
+
diff --git a/neon/test/makekeys.sh b/neon/test/makekeys.sh
new file mode 100755
index 000000000..e66dabf81
--- /dev/null
+++ b/neon/test/makekeys.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+# Helper script to create CA and server certificates.
+
+srcdir=${1-.}
+
+set -ex
+
+mkdir ca
+
+openssl genrsa -rand ${srcdir}/../configure > ca/key.pem
+openssl genrsa -rand ${srcdir}/../configure > client.key
+
+openssl req -x509 -new -key ca/key.pem -out ca/cert.pem <<EOF
+US
+California
+Oakland
+Neosign
+Random Dept
+nowhere.example.com
+neon@webdav.org
+EOF
+
+openssl req -new -key ${srcdir}/server.key -out server.csr <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon QA Dept
+localhost
+neon@webdav.org
+.
+.
+EOF
+
+openssl req -new -x509 -key ${srcdir}/server.key -out ssigned.pem <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon Self-Signed Dept
+localhost
+neon@webdav.org
+.
+.
+EOF
+
+# Only works with a Linuxy hostname command
+hostname=`hostname -s 2>/dev/null`
+domain=`hostname -d 2>/dev/null`
+fqdn=`hostname -f 2>/dev/null`
+if [ "x${hostname}.${domain}" = "x${fqdn}" ]; then
+ openssl req -new -key ${srcdir}/server.key -out wildcard.csr <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon Wildcard Cert
+*.${domain}
+neon@webdav.org
+.
+.
+EOF
+fi
+
+openssl req -new -key client.key -out client.csr <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon Client Cert
+ignored.example.com
+neon@webdav.org
+.
+.
+EOF
+
+touch ca/index.txt
+echo 01 > ca/serial
+
+openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in server.csr \
+ -out server.cert
+
+openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in wildcard.csr \
+ -out wildcard.cert
+
+openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in client.csr \
+ -out client.cert
+
+# generate a PKCS12 cert from the client cert: -passOUT because it's the
+# passphrase on the OUTPUT cert, confusing...
+echo foobar | openssl pkcs12 -export -passout stdin -name "Neon Client Cert" \
+ -in client.cert -inkey client.key -out client.p12
diff --git a/neon/test/notvalid.pem b/neon/test/notvalid.pem
new file mode 100644
index 000000000..42008b3aa
--- /dev/null
+++ b/neon/test/notvalid.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMB4XDTIzMTIyNzIwNDAyOVoXDTIzMTIyODIwNDAyOVowgaExCzAJBgNVBAYT
+AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
+MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
+ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
+ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
+oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
+AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
+VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
+Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
+IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
+ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQA80TYV
+2F4QLveuldmxGoIOq5hHGxCR6aVsdtm4PGY49R5/ObCAgdWw/JV/Tc448JAz5QvU
+ahr1x9kA4Vo5NZ4q
+-----END CERTIFICATE-----
diff --git a/neon/test/openssl.conf b/neon/test/openssl.conf
new file mode 100644
index 000000000..32dfa61c3
--- /dev/null
+++ b/neon/test/openssl.conf
@@ -0,0 +1,21 @@
+[ca]
+default_ca = neonca
+
+[neonca]
+dir = ./ca
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/cert.pem
+serial = $dir/serial
+private_key = $dir/key.pem
+policy = policy_any
+default_md = md5
+
+[policy_any]
+countryName = supplied
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
diff --git a/neon/test/props.c b/neon/test/props.c
new file mode 100644
index 000000000..7c2a635aa
--- /dev/null
+++ b/neon/test/props.c
@@ -0,0 +1,64 @@
+/*
+ Tests for property handling
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_props.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static const ne_propname p_alpha = {"DAV:", "alpha"},
+ p_beta = {"http://webdav.org/random/namespace", "beta"},
+ p_delta = {NULL, "delta"};
+
+/* Tests little except that ne_proppatch() doesn't segfault. */
+static int patch_simple(void)
+{
+ ne_session *sess;
+ ne_proppatch_operation ops[] = {
+ { &p_alpha, ne_propset, "fish" },
+ { &p_beta, ne_propremove, NULL },
+ { NULL, ne_propset, NULL }
+ };
+
+ CALL(make_session(&sess, single_serve_string,
+ "HTTP/1.1 200 Goferit\r\n"
+ "Connection: close\r\n\r\n"));
+ ONREQ(ne_proppatch(sess, "/fish", ops));
+ ne_session_destroy(sess);
+ return await_server();
+}
+
+ne_test tests[] = {
+ T(patch_simple),
+ T(NULL)
+};
+
diff --git a/neon/test/redirect.c b/neon/test/redirect.c
new file mode 100644
index 000000000..582462ede
--- /dev/null
+++ b/neon/test/redirect.c
@@ -0,0 +1,135 @@
+/*
+ Tests for 3xx redirect interface (ne_redirect.h)
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_redirect.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+struct redir_args {
+ int code;
+ const char *dest;
+};
+
+static int serve_redir(ne_socket *sock, void *ud)
+{
+ struct redir_args *args = ud;
+ char buf[BUFSIZ];
+
+ CALL(discard_request(sock));
+
+ ne_snprintf(buf, BUFSIZ,
+ "HTTP/1.0 %d Get Ye Away\r\n"
+ "Content-Length: 0\r\n"
+ "Location: %s\r\n\n",
+ args->code, args->dest);
+
+ SEND_STRING(sock, buf);
+
+ return OK;
+}
+
+static int check_redir(struct redir_args *args, const char *expect)
+{
+ ne_session *sess;
+ int ret;
+ const ne_uri *loc;
+
+ CALL(make_session(&sess, serve_redir, args));
+
+ ne_redirect_register(sess);
+
+ ret = any_request(sess, "/redir/me");
+
+ ONN("did not get NE_REDIRECT", ret != NE_REDIRECT);
+
+ loc = ne_redirect_location(sess);
+
+ ONN("redirect location was NULL", loc == NULL);
+
+ ONV(strcmp(ne_uri_unparse(loc), expect),
+ ("redirected to `%s' not `%s'", ne_uri_unparse(loc), expect));
+
+ ne_session_destroy(sess);
+
+ return OK;
+}
+
+#define DEST "http://foo.com/blah/blah/bar"
+
+static int simple(void)
+{
+ struct redir_args args = {302, DEST};
+ return check_redir(&args, DEST);
+}
+
+static int redir_303(void)
+{
+ struct redir_args args = {303, DEST};
+ return check_redir(&args, DEST);
+}
+
+/* check that a non-absoluteURI is qualified properly */
+static int non_absolute(void)
+{
+ struct redir_args args = {302, "/foo/bar/blah"};
+ return check_redir(&args, "http://localhost:7777/foo/bar/blah");
+}
+
+#if 0
+/* could implement failure on self-referential redirects, but
+ * realistically, the application must implement a max-redirs count
+ * check, so it's kind of redundant. Mozilla takes this approach. */
+static int fail_loop(void)
+{
+ ne_session *sess;
+
+ CALL(make_session(&sess, serve_redir, "http://localhost:7777/foo/bar"));
+
+ ne_redirect_register(sess);
+
+ ONN("followed looping redirect",
+ any_request(sess, "/foo/bar") != NE_ERROR);
+
+ ne_session_destroy(sess);
+ return OK;
+}
+#endif
+
+ne_test tests[] = {
+ T(lookup_localhost),
+ T(simple),
+ T(redir_303),
+ T(non_absolute),
+ T(NULL)
+};
+
diff --git a/neon/test/regress.c b/neon/test/regress.c
new file mode 100644
index 000000000..a2d70e5ba
--- /dev/null
+++ b/neon/test/regress.c
@@ -0,0 +1,87 @@
+/*
+ Some miscellenaneous regression tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_props.h"
+
+#include "tests.h"
+#include "child.h"
+
+/* This caused a segfault in 0.15.3 and earlier. */
+static int serve_dodgy_xml(nsocket *sock, void *ud)
+{
+ char buffer[BUFSIZ];
+
+ CALL(discard_request(sock));
+
+ sock_read(sock, buffer, clength);
+
+ sock_send_string(sock,
+ "HTTP/1.0 207 OK\r\n"
+ "Server: foo\r\n"
+ "Connection: close\r\n"
+ "\r\n"
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<multistatus xmlns=\"DAV:\">"
+ "<response><propstat><prop><href>"
+ "</href></prop></propstat></response>"
+ "</multistatus>");
+
+ return 0;
+}
+
+static void dummy_results(void *ud, const char *href,
+ const ne_prop_result_set *rset)
+{
+
+}
+
+static int propfind_segv(void)
+{
+ ne_session *sess = ne_session_create();
+
+ ne_session_server(sess, "localhost", 7777);
+
+ CALL(spawn_server(7777, serve_dodgy_xml, NULL));
+
+ ne_simple_propfind(sess, "/", 0, NULL, dummy_results, NULL);
+
+ ne_session_destroy(sess);
+
+ await_server();
+
+ return OK;
+}
+
+test_func tests[] = {
+ propfind_segv,
+ NULL
+};
diff --git a/neon/test/request.c b/neon/test/request.c
new file mode 100644
index 000000000..7d4180cb9
--- /dev/null
+++ b/neon/test/request.c
@@ -0,0 +1,423 @@
+/*
+ HTTP request handling tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+
+static char buffer[BUFSIZ];
+static int clength;
+
+static int discard_request(nsocket *sock)
+{
+ NE_DEBUG(NE_DBG_HTTP, "Discarding request...\n");
+ do {
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ if (strncasecmp(buffer, "content-length:", 15) == 0) {
+ clength = atoi(buffer + 16);
+ }
+ NE_DEBUG(NE_DBG_HTTP, "[req] %s", buffer);
+ } while (strcmp(buffer, EOL) != 0);
+
+ return OK;
+}
+
+#define RESP200 "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
+#define TE_CHUNKED "Transfer-Encoding: chunked\r\n"
+
+static int single_serve_string(nsocket *s, void *userdata)
+{
+ const char *str = userdata;
+
+ ON(discard_request(s));
+ ON(sock_send_string(s, str));
+
+ return OK;
+}
+
+/* takes response body chunks and appends them to a buffer. */
+static void collector(void *ud, const char *data, size_t len)
+{
+ ne_buffer *buf = ud;
+ ne_buffer_append(buf, data, len);
+}
+
+#define ONREQ(x) do { int _ret = (x); if (_ret) { snprintf(on_err_buf, 500, "%s line %d: HTTP error:\n%s", __FUNCTION__, __LINE__, ne_get_error(sess)); i_am(on_err_buf); return FAIL; } } while (0);
+
+
+typedef ne_request *(*construct_request)(ne_session *sess, void *userdata);
+
+static ne_request *construct_get(ne_session *sess, void *userdata)
+{
+ ne_request *r = ne_request_create(sess, "GET", "/");
+ ne_buffer *buf = userdata;
+
+ ne_add_response_body_reader(r, ne_accept_2xx, collector, buf);
+
+ return r;
+}
+
+static int run_request(ne_session *sess, int status,
+ construct_request cb, void *userdata)
+{
+ ne_request *req = cb(sess, userdata);
+
+ ON(req == NULL);
+
+ ONREQ(ne_request_dispatch(req));
+
+ ONN("response status", ne_get_status(req)->code != status);
+
+ ne_request_destroy(req);
+
+ return OK;
+}
+
+static int expect_response(const char *expect, server_fn fn, void *userdata)
+{
+ ne_session *sess = ne_session_create();
+ ne_buffer *buf = ne_buffer_create();
+ int ret;
+
+ ON(sess == NULL || buf == NULL);
+ ON(ne_session_server(sess, "localhost", 7777));
+ ON(spawn_server(7777, fn, userdata));
+
+ ret = run_request(sess, 200, construct_get, buf);
+ if (ret) {
+ reap_server();
+ return ret;
+ }
+
+ ON(await_server());
+
+ ONN("response body match", strcmp(buf->data, expect));
+
+ ne_session_destroy(sess);
+
+ return OK;
+}
+
+static int single_get_eof(void)
+{
+ return expect_response("a", single_serve_string,
+ RESP200
+ "Connection: close\r\n"
+ "\r\n"
+ "a");
+}
+
+static int single_get_clength(void)
+{
+ return expect_response("a", single_serve_string,
+ RESP200
+ "Content-Length: 1\r\n"
+ "\r\n"
+ "a"
+ "bbbbbbbbasdasd");
+}
+
+static int single_get_chunked(void)
+{
+ return expect_response("a", single_serve_string,
+ RESP200 TE_CHUNKED
+ "\r\n"
+ "1\r\n"
+ "a\r\n"
+ "0\r\n" "\r\n"
+ "g;lkjalskdjalksjd");
+}
+
+#define CHUNK(len, data) #len "\r\n" data "\r\n"
+
+#define ABCDE_CHUNKS CHUNK(1, "a") CHUNK(1, "b") \
+ CHUNK(1, "c") CHUNK(1, "d") \
+ CHUNK(1, "e") CHUNK(0, "")
+
+static int chunk_syntax_1(void)
+{
+ /* lots of little chunks. */
+ ON(expect_response("abcde", single_serve_string,
+ RESP200 TE_CHUNKED
+ "\r\n"
+ ABCDE_CHUNKS));
+ return OK;
+}
+
+static int chunk_syntax_2(void)
+{
+ /* leading zero's */
+ ON(expect_response("0123456789abcdef", single_serve_string,
+ RESP200 TE_CHUNKED
+ "\r\n"
+ "000000010\r\n" "0123456789abcdef\r\n"
+ "000000000\r\n" "\r\n"));
+ return OK;
+}
+
+static int chunk_syntax_3(void)
+{
+ /* chunk-extensions. */
+ ON(expect_response("0123456789abcdef", single_serve_string,
+ RESP200 TE_CHUNKED
+ "\r\n"
+ "000000010; foo=bar; norm=fish\r\n"
+ "0123456789abcdef\r\n"
+ "000000000\r\n" "\r\n"));
+ return OK;
+}
+
+static int chunk_syntax_4(void)
+{
+ /* trailers. */
+ ON(expect_response("abcde", single_serve_string,
+ RESP200 TE_CHUNKED
+ "\r\n"
+ "00000005; foo=bar; norm=fish\r\n"
+ "abcde\r\n"
+ "000000000\r\n"
+ "X-Hello: world\r\n"
+ "X-Another: header\r\n"
+ "\r\n"));
+ return OK;
+}
+
+static int chunk_syntax_5(void)
+{
+ /* T-E dominates over C-L. */
+ ON(expect_response("abcde", single_serve_string,
+ RESP200 TE_CHUNKED
+ "Content-Length: 300\r\n"
+ "\r\n"
+ ABCDE_CHUNKS));
+ return OK;
+}
+
+static int fold_headers(void)
+{
+ ON(expect_response("abcde", single_serve_string,
+ RESP200 "Content-Length: \r\n 5\r\n"
+ "\r\n"
+ "abcde"));
+ return OK;
+}
+
+static int fold_many_headers(void)
+{
+ ON(expect_response("abcde", single_serve_string,
+ RESP200 "Content-Length: \r\n \r\n \r\n \r\n 5\r\n"
+ "\r\n"
+ "abcde"));
+ return 0;
+}
+
+static void mh_header(void *ctx, const char *value)
+{
+ int *state = ctx;
+ static const char *hdrs[] = { "jim", "jab", "jar" };
+
+ if (*state < 0 || *state > 2) {
+ /* already failed. */
+ return;
+ }
+
+ if (strcmp(value, hdrs[*state]))
+ *state = -*state;
+ else
+ (*state)++;
+}
+
+/* check headers callbacks are working correctly. */
+static int multi_header(void)
+{
+ ne_session *sess = ne_session_create();
+ ne_request *req;
+ int ret, state = 0;
+
+ ON(sess == NULL);
+ ON(ne_session_server(sess, "localhost", 7777));
+ ON(spawn_server(7777, single_serve_string,
+ RESP200
+ "X-Header: jim\r\n"
+ "x-header: jab\r\n"
+ "x-Header: jar\r\n"
+ "Content-Length: 0\r\n\r\n"));
+
+ req = ne_request_create(sess, "GET", "/");
+ ON(req == NULL);
+
+ ne_add_response_header_handler(req, "x-header", mh_header, &state);
+
+ ret = ne_request_dispatch(req);
+ if (ret) {
+ reap_server();
+ ONREQ(ret);
+ ONN("shouldn't get here.", 1);
+ }
+
+ ON(await_server());
+
+ ON(state != 3);
+
+ ne_request_destroy(req);
+ ne_session_destroy(sess);
+
+ return OK;
+}
+
+struct body {
+ char *body;
+ size_t size;
+};
+
+static int want_body(nsocket *sock, void *userdata)
+{
+ struct body *b = userdata;
+ char *buf = ne_malloc(b->size);
+
+ clength = 0;
+ CALL(discard_request(sock));
+ ONN("request has c-l header", clength == 0);
+
+ ONN("request length", clength != (int)b->size);
+
+ NE_DEBUG(NE_DBG_HTTP, "reading body of %d bytes...\n", b->size);
+
+ ON(sock_fullread(sock, buf, b->size));
+
+ ON(sock_send_string(sock, RESP200 "Content-Length: 0\r\n\r\n"));
+
+ ON(memcmp(buf, b->body, b->size));
+
+ return OK;
+}
+
+static ssize_t provide_body(void *userdata, char *buf, size_t buflen)
+{
+ static const char *pnt;
+ static size_t left;
+ struct body *b = userdata;
+
+ if (buflen == 0) {
+ pnt = b->body;
+ left = b->size;
+ } else {
+ if (left < buflen) buflen = left;
+ memcpy(buf, pnt, buflen);
+ left -= buflen;
+ }
+
+ return buflen;
+}
+
+static int send_bodies(void)
+{
+ int n, m;
+
+#define B(x) { x, strlen(x) }
+ struct body bodies[] = {
+ B("abcde"),
+ { "\0\0\0\0\0\0", 6 },
+ { NULL, 50000 },
+ { NULL }
+ };
+
+#define BIG 2
+ /* make the body with some cruft. */
+ bodies[BIG].body = ne_malloc(bodies[BIG].size);
+ for (n = 0; n < bodies[BIG].size; n++) {
+ bodies[BIG].body[n] = (char)n%80;
+ }
+
+ for (m = 0; m < 2; m++) {
+ for (n = 0; bodies[n].body != NULL; n++) {
+ ne_session *sess = ne_session_create();
+ ne_request *req;
+
+ ON(sess == NULL);
+ ON(ne_session_server(sess, "localhost", 7777));
+ ON(spawn_server(7777, want_body, &(bodies[n])));
+
+ req = ne_request_create(sess, "PUT", "/");
+ ON(req == NULL);
+
+ if (m == 0) {
+ ne_set_request_body_buffer(req, bodies[n].body, bodies[n].size);
+ } else {
+ ne_set_request_body_provider(req, bodies[n].size,
+ provide_body, &bodies[n]);
+ }
+
+ ONREQ(ne_request_dispatch(req));
+
+ CALL(await_server());
+
+ ne_request_destroy(req);
+ ne_session_destroy(sess);
+ }
+ }
+
+ return OK;
+}
+
+/* TODO: */
+
+/* test that header folding is bounded, e.g. have the server process do:
+ * send("Foo: norman");
+ * while(1) send(" Jim\r\n");
+ */
+
+/* test sending request body. */
+static int send_body(void)
+{
+ return FAIL;
+}
+
+test_func tests[] = {
+ lookup_localhost,
+ single_get_clength,
+ single_get_eof,
+ single_get_chunked,
+ chunk_syntax_1,
+ chunk_syntax_2,
+ chunk_syntax_3,
+ chunk_syntax_4,
+ chunk_syntax_5,
+ fold_headers,
+ fold_many_headers,
+ multi_header,
+ send_bodies,
+ NULL,
+ send_body,
+ NULL
+};
diff --git a/neon/test/resolve.c b/neon/test/resolve.c
new file mode 100644
index 000000000..be3cd68f5
--- /dev/null
+++ b/neon/test/resolve.c
@@ -0,0 +1,57 @@
+/*
+ Test program for the neon resolver interface
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "ne_socket.h"
+
+int main(int argc, char **argv)
+{
+ ne_sock_addr *addr;
+ char buf[256];
+ int ret = 0;
+
+ if (argc < 2) {
+ printf("Usage: %s hostname\n", argv[0]);
+ return -1;
+ }
+
+ if (ne_sock_init())
+ printf("%s: Failed to initialize socket library.\n", argv[0]);
+
+ addr = ne_addr_resolve(argv[1], 0);
+ if (ne_addr_result(addr)) {
+ printf("Could not resolve `%s': %s\n", argv[1],
+ ne_addr_error(addr, buf, sizeof buf));
+ ret = 1;
+ } else {
+ ne_inet_addr *ia;
+ printf("Resolved `%s' OK:", argv[1]);
+ for (ia = ne_addr_first(addr); ia; ia = ne_addr_next(addr)) {
+ printf(" <%s>", ne_addr_print(ia, buf, sizeof buf));
+ }
+ putchar('\n');
+ }
+ ne_addr_destroy(addr);
+
+ return 0;
+}
diff --git a/neon/test/run.sh b/neon/test/run.sh
new file mode 100755
index 000000000..2e0b7f125
--- /dev/null
+++ b/neon/test/run.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+for f in $*; do
+ if ./$f; then
+ :
+ else
+ echo FAILURE
+ exit -1
+ fi
+done
+
+exit 0
diff --git a/neon/test/server.c b/neon/test/server.c
new file mode 100644
index 000000000..1466638a0
--- /dev/null
+++ b/neon/test/server.c
@@ -0,0 +1,195 @@
+/*
+ HTTP server tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* These don't really test neon, they test an HTTP server. */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "nsocket.h"
+#include "ne_utils.h"
+
+#include "tests.h"
+
+struct in_addr addr;
+nsocket *sock;
+
+char buffer[BUFSIZ];
+int clength;
+
+#define HOSTNAME "localhost"
+
+static int hostname_lookup(void) {
+ if (sock_name_lookup(HOSTNAME, &addr))
+ return FAILHARD;
+ return OK;
+}
+
+static int connect_to_server(void) {
+ sock = sock_connect(addr, 80);
+ if (sock == NULL)
+ return FAILHARD;
+ return OK;
+}
+
+static int close_conn(void) {
+ ON(sock_close(sock));
+ return OK;
+}
+
+#define EOL "\r\n"
+
+#define TRACE_REQ "TRACE / HTTP/1.0\r\n" "Host: " HOSTNAME "\r\n\r\n"
+
+#define ROOT_URL "/test/"
+
+#define GET_REQ(url) do { ON(sock_send_string(sock, "GET " url " HTTP/1.0" EOL "Host: " HOSTNAME EOL EOL)); } while (0)
+
+static int trace(void) {
+ CALL(connect_to_server());
+
+ ON(sock_send_string(sock, TRACE_REQ));
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ /* this is true for any Apache server. */
+ ON(strcasecmp(buffer, "HTTP/1.1 200 OK\r\n"));
+ /* read hdrs */
+ do {
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ } while (strcmp(buffer, "\r\n") != 0);
+ /* read body */
+ ON(sock_read(sock, buffer, 1024) < 0);
+ /* this will fail if you have a transparent proxy. */
+ ONN("pristine TRACE loopback",
+ strncmp(buffer, TRACE_REQ, strlen(TRACE_REQ)) != 0);
+
+ CALL(close_conn());
+
+ return OK;
+}
+
+static int basic_syntax(void) {
+ char *value;
+
+ CALL(connect_to_server());
+ GET_REQ(ROOT_URL);
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ ONN("CRLF on status-line", strstr(buffer, EOL) == NULL);
+
+ ONN("HTTP-Version is 1.0 or 1.1",
+ strncasecmp(buffer, "HTTP/1.1", 8) != 0 &&
+ strncasecmp(buffer, "HTTP/1.0", 8));
+
+ ONN("Status-Line syntax", buffer[8] != ' ' || buffer[12] != ' ');
+
+ /* should be a warning if this fails. */
+ ONN("Status-Code is 200",
+ buffer[9] != '2' || buffer[10] != '0' || buffer[11] != '0');
+
+ ONN("Reason-Phrase is present", strlen(buffer) < strlen("HTTP/x.y nnn X" EOL));
+
+ do {
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ ONN("CRLF on request-header", strstr(buffer, EOL) == NULL);
+ if (strcmp(buffer, EOL) != 0) {
+ value = strchr(buffer, ':');
+ ONN("colon in request-header", value == NULL);
+ ONN("field-name in request-header", value == buffer);
+ }
+ } while (strcmp(buffer, EOL) != 0);
+
+ CALL(close_conn());
+
+ return OK;
+}
+
+static int skip_header(void)
+{
+ do {
+ ON(sock_readline(sock, buffer, 1024) < 0);
+ if (strncasecmp(buffer, "content-length:", 15) == 0) {
+ clength = atoi(buffer + 16);
+ }
+ } while (strcmp(buffer, EOL) != 0);
+ return OK;
+}
+
+static int simple_get(void)
+{
+ CALL(connect_to_server());
+ GET_REQ(ROOT_URL "plain");
+ clength = -1;
+ CALL(skip_header());
+ ONN("Content-Length header present", clength == -1);
+ ONN("Content-Length of 'plain'", clength != 11);
+ ONN("read response body", sock_read(sock, buffer, BUFSIZ) != 11);
+ ONN("content of 'plain'", strncmp(buffer, "Test file.\n", 11) != 0);
+
+ /* FIXME: I'm not sure if this is right, actually. */
+ ONN("connection close after GET",
+ sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+
+ CALL(close_conn());
+ return OK;
+}
+
+static int simple_head(void)
+{
+ CALL(connect_to_server());
+ ON(sock_send_string(sock, "HEAD " ROOT_URL "plain HTTP/1.0" EOL
+ "Host: " HOSTNAME EOL EOL));
+ clength = -1;
+ CALL(skip_header());
+ ONN("Content-Length header present", clength == -1);
+ ONN("Content-Length of 'plain'", clength != 11);
+
+ ONN("connection close after HEAD",
+ sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+
+ CALL(close_conn());
+ return OK;
+}
+
+static int null_resource(void)
+{
+ ne_status s = {0};
+ CALL(connect_to_server());
+ GET_REQ(ROOT_URL "nothing-here");
+ ON(sock_readline(sock, buffer, BUFSIZ) < 0);
+ ON(ne_parse_statusline(buffer, &s));
+ ONN("null resource gives 404", s.code != 404);
+ CALL(close_conn());
+ return OK;
+}
+
+test_func tests[] = {
+ hostname_lookup,
+ basic_syntax,
+ trace,
+ simple_get,
+ simple_head,
+ null_resource,
+ NULL
+};
diff --git a/neon/test/server.key b/neon/test/server.key
new file mode 100644
index 000000000..cdfb91b97
--- /dev/null
+++ b/neon/test/server.key
@@ -0,0 +1,9 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP+psm5EGZGmGJGvSD
+sk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAQJAJBhYdoVAqNqEVu8rKB3C
+F4kcqLUlYBDVAL+ZM4QlwgWncAKk2C53BwH4PVWIIfyysleyt3bTAtqg/tgMNM06
+AQIhAP1HKbuppa+UY4rNP4Xcyj5BrCU4wVz77sg/ygW+mWIhAiEA9eKcUnnaIpig
+hlWtx9qz++85/JtahA85j6T48v0hBM0CIQCa8ByUg2wq45CdSX+xiOZjfVMslfKb
+yjZBY9xW9UjpYQIgdy9j5JqKANEIpnTran95VLot2mMXagHTPeySe331PlUCIQD0
+rL1AXeIR3Vd4D8dgab/FVbg4i94qBiY0731nyPJRoQ==
+-----END RSA PRIVATE KEY-----
diff --git a/neon/test/session.c b/neon/test/session.c
new file mode 100644
index 000000000..d183ebff0
--- /dev/null
+++ b/neon/test/session.c
@@ -0,0 +1,115 @@
+/*
+ Tests for session handling
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_session.h"
+
+#include "tests.h"
+
+/* should be in a 'session.c'? */
+static int fill_uri(void)
+{
+ ne_uri uri = {0};
+ ne_session *sess = ne_session_create("http", "localhost", 7777);
+
+ ne_fill_server_uri(sess, &uri);
+
+ ONN("hostname mis-match", strcmp(uri.host, "localhost"));
+ ONN("port mis-match", uri.port != 7777);
+ ONN("scheme mis-match", strcmp(uri.scheme, "http"));
+
+ ne_session_destroy(sess);
+ ne_uri_free(&uri);
+
+ return OK;
+}
+
+static int match_hostport(const char *scheme, const char *hostname, int port,
+ const char *hostport)
+{
+ ne_session *sess = ne_session_create(scheme, hostname, port);
+ const char *hp = ne_get_server_hostport(sess);
+ ONV(strcmp(hp, hostport),
+ ("hostport incorrect for %s: `%s' not `%s'", scheme, hp, hostport));
+ ne_session_destroy(sess);
+ return OK;
+}
+
+static int hostports(void)
+{
+ const static struct {
+ const char *scheme, *hostname;
+ int port;
+ const char *hostport;
+ } hps[] = {
+ { "http", "host.name", 80, "host.name" },
+ { "http", "host.name", 555, "host.name:555" },
+ { "http", "host.name", 443, "host.name:443" },
+ { "https", "host.name", 80, "host.name:80" },
+ { "https", "host.name", 443, "host.name" },
+ { "https", "host.name", 700, "host.name:700" },
+ { NULL }
+ };
+ int n;
+
+ for (n = 0; hps[n].scheme; n++) {
+ CALL(match_hostport(hps[n].scheme, hps[n].hostname,
+ hps[n].port, hps[n].hostport));
+ }
+
+ return OK;
+}
+
+
+/* Check that ne_set_error is passing through to printf correctly. */
+static int errors(void)
+{
+ ne_session *sess = ne_session_create("http", "foo.com", 80);
+
+#define EXPECT "foo, hello world, 100, bar!"
+
+ ne_set_error(sess, "foo, %s, %d, bar!", "hello world", 100);
+
+ ONV(strcmp(ne_get_error(sess), EXPECT),
+ ("session error was `%s' not `%s'",
+ ne_get_error(sess), EXPECT));
+#undef EXPECT
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+ne_test tests[] = {
+ T(fill_uri),
+ T(hostports),
+ T(errors),
+ T(NULL)
+};
+
diff --git a/neon/test/skeleton.c b/neon/test/skeleton.c
new file mode 100644
index 000000000..9260c852c
--- /dev/null
+++ b/neon/test/skeleton.c
@@ -0,0 +1,51 @@
+/*
+ neon test suite
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int foo(void)
+{
+ /* This is a skeleton test suite file. */
+ return OK;
+}
+
+ne_test tests[] = {
+ T(foo), /* test functions here */
+
+ /* end of test functions. */
+ T(NULL)
+};
+
diff --git a/neon/test/sock-tests.c b/neon/test/sock-tests.c
new file mode 100644
index 000000000..05a3f1e0e
--- /dev/null
+++ b/neon/test/sock-tests.c
@@ -0,0 +1,70 @@
+/*
+ Socket handling tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "nsocket.h"
+
+#include "tests.h"
+
+static int namelookup(void) {
+ struct in_addr a;
+ ON(sock_name_lookup("www.apache.org", &a));
+ ON(sock_name_lookup("asdkajhd.webdav.org", &a) == 0);
+ return OK;
+}
+
+static int svclookup(void) {
+ ON(sock_service_lookup("http") != 80);
+ ON(sock_service_lookup("https") != 443);
+ return OK;
+}
+
+static int http(void) {
+ struct in_addr a;
+ nsocket *s;
+ char buffer[1024];
+
+ ON(sock_name_lookup("www.apache.org", &a));
+ s = sock_connect(a, 80);
+ ON(s == NULL);
+ ON(sock_send_string(s,
+ "GET / HTTP/1.0\r\n"
+ "Host: www.apache.org\r\n\r\n"));
+ ON(sock_readline(s, buffer, 1024) < 0);
+ ON(strcasecmp(buffer, "HTTP/1.1 200 OK\r\n"));
+ ON(sock_close(s));
+ return OK;
+}
+
+test_func tests[] = {
+ namelookup,
+ svclookup,
+ http,
+ NULL
+};
+
diff --git a/neon/test/socket.c b/neon/test/socket.c
new file mode 100644
index 000000000..5d85f78f8
--- /dev/null
+++ b/neon/test/socket.c
@@ -0,0 +1,257 @@
+/*
+ Socket handling tests
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_socket.h"
+
+#include "child.h"
+#include "tests.h"
+
+static struct in_addr localhost;
+static char buffer[BUFSIZ];
+
+static int resolve(void)
+{
+ ONN("could not resolve `localhost'",
+ ne_name_lookup("localhost", &localhost));
+ /* and again for child.c */
+ CALL(lookup_localhost());
+ return OK;
+}
+
+static int serve_close(ne_socket *sock, void *ud)
+{
+ return 0;
+}
+
+static int begin(ne_socket **sock, server_fn fn, void *ud)
+{
+ CALL(spawn_server(7777, fn, ud));
+ *sock = ne_sock_connect(localhost, 7777);
+ ONN("could not connect to localhost:7777", sock == NULL);
+ return OK;
+}
+
+static int just_connect(void)
+{
+ ne_socket *sock;
+
+ CALL(begin(&sock, serve_close, NULL));
+ ne_sock_close(sock);
+ CALL(await_server());
+
+ return OK;
+}
+
+/* Exect a read() to return EOF */
+static int expect_close(ne_socket *sock)
+{
+ ssize_t n = ne_sock_read(sock, buffer, 1);
+ ONV(n != NE_SOCK_CLOSED, ("read gave %d not closure", n));
+ return OK;
+}
+
+/* Exect a ne_sock_peek() to return EOF */
+static int expect_peek_close(ne_socket *sock)
+{
+ ssize_t n = ne_sock_read(sock, buffer, 1);
+ ONV(n != NE_SOCK_CLOSED, ("peek gave %d not closure", n));
+ return OK;
+}
+
+/* Test that just does a connect then a close. */
+static int read_close(void)
+{
+ ne_socket *sock;
+
+ CALL(begin(&sock, serve_close, NULL));
+ CALL(expect_close(sock));
+ ONN("close failed", ne_sock_close(sock));
+ CALL(await_server());
+ return OK;
+}
+
+/* Test that just does a connect then a close (but gets the close via
+ * ne_sock_peek). */
+static int peek_close(void)
+{
+ ne_socket *sock;
+
+ CALL(begin(&sock, serve_close, NULL));
+ CALL(expect_peek_close(sock));
+ ONN("close failed", ne_sock_close(sock));
+ CALL(await_server());
+ return OK;
+}
+
+
+struct string {
+ char *data;
+ size_t len;
+};
+
+static int serve_string(ne_socket *sock, void *ud)
+{
+ struct string *str = ud;
+
+ ONN("write failed", ne_sock_fullwrite(sock, str->data, str->len));
+
+ return 0;
+}
+
+/* Don't change this string. */
+#define STR "Hello, World."
+
+/* do a sock_peek() on sock for 'len' bytes, and expect 'str'. */
+static int peek_expect(ne_socket *sock, const char *str, size_t len)
+{
+ ssize_t ret = ne_sock_peek(sock, buffer, len);
+ ONV((ssize_t)len != ret, ("peek got %d bytes not %d", ret, len));
+ ONV(memcmp(str, buffer, len),
+ ("peek mismatch: `%.*s' not `%.*s'", len, buffer, len, str));
+ return OK;
+}
+
+/* do a sock_read() on sock for 'len' bytes, and expect 'str'. */
+static int read_expect(ne_socket *sock, const char *str, size_t len)
+{
+ ssize_t ret = ne_sock_read(sock, buffer, len);
+ ONV((ssize_t)len != ret, ("peek got %d bytes not %d", ret, len));
+ ONV(memcmp(str, buffer, len),
+ ("read mismatch: `%.*s' not `%.*s'", len, buffer, len, str));
+ return OK;
+}
+
+/* Test a simple read. */
+static int single_read(void)
+{
+ ne_socket *sock;
+ struct string hello = { STR, strlen(STR) };
+
+ CALL(begin(&sock, serve_string, &hello));
+ CALL(read_expect(sock, STR, strlen(STR)));
+ CALL(expect_close(sock));
+
+ CALL(await_server());
+ return OK;
+}
+
+/* Test a simple peek. */
+static int single_peek(void)
+{
+ ne_socket *sock;
+ struct string hello = { STR, strlen(STR) };
+
+ CALL(begin(&sock, serve_string, &hello));
+ CALL(peek_expect(sock, STR, strlen(STR)));
+ ONN("close failed", ne_sock_close(sock));
+
+ CALL(await_server());
+ return OK;
+}
+
+/* Test lots of 1-byte reads. */
+static int small_reads(void)
+{
+ ne_socket *sock;
+ struct string hello = { STR, strlen(STR) };
+ char *pnt;
+
+ CALL(begin(&sock, serve_string, &hello));
+
+ /* read the string byte-by-byte. */
+ for (pnt = hello.data; *pnt; pnt++) {
+ CALL(read_expect(sock, pnt, 1));
+ }
+
+ ONN("close failed", ne_sock_close(sock));
+ CALL(await_server());
+ return OK;
+}
+
+/* Stress out the read buffer handling a little. */
+static int read_and_peek(void)
+{
+ ne_socket *sock;
+ struct string hello = { STR, strlen(STR) };
+
+#define READ(str) CALL(read_expect(sock, str, strlen(str)))
+#define PEEK(str) CALL(peek_expect(sock, str, strlen(str)))
+
+ CALL(begin(&sock, serve_string, &hello));
+
+ PEEK("Hello");
+ PEEK("Hell");
+ PEEK(STR);
+ READ("He");
+ PEEK("llo, ");
+ READ("l");
+ PEEK("lo, World.");
+ READ("lo, Worl");
+ PEEK("d."); PEEK("d");
+ READ("d.");
+
+ CALL(expect_close(sock));
+
+ ONN("close failed", ne_sock_close(sock));
+ CALL(await_server());
+ return OK;
+}
+
+/* Read more bytes than were written. */
+static int larger_read(void)
+{
+ ne_socket *sock;
+ struct string hello = { STR, strlen(STR) };
+ ssize_t nb;
+
+ CALL(begin(&sock, serve_string, &hello));
+
+ nb = ne_sock_read(sock, buffer, hello.len + 10);
+ ONV(nb != (ssize_t)hello.len, ("read gave too many bytes (%d)", nb));
+ ONN("read gave wrong data", memcmp(buffer, hello.data, hello.len));
+
+ CALL(expect_close(sock));
+
+ ONN("close failed", ne_sock_close(sock));
+ CALL(await_server());
+ return OK;
+}
+
+ne_test tests[] = {
+ T(resolve),
+ T(just_connect),
+ T(read_close),
+ T(peek_close),
+ T(single_read),
+ T(single_peek),
+ T(small_reads),
+ T(read_and_peek),
+ T(larger_read),
+ T(NULL)
+};
diff --git a/neon/test/ssl.c b/neon/test/ssl.c
new file mode 100644
index 000000000..7d1b22259
--- /dev/null
+++ b/neon/test/ssl.c
@@ -0,0 +1,131 @@
+/*
+ neon test suite
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+#ifndef NEON_SSL
+/* this file shouldn't be built if SSL is not enabled. */
+#error SSL not supported
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))
+
+SSL_CTX *server_ctx;
+
+static int s_strwrite(SSL *s, const char *buf)
+{
+ size_t len = strlen(buf);
+
+ ONV(SSL_write(s, buf, len) != (int)len,
+ ("SSL_write failed: %s", ERROR_SSL_STRING));
+
+ return OK;
+}
+
+static int serve_ssl(nsocket *sock, void *ud)
+{
+ int fd = sock_get_fd(sock);
+ /* we don't want OpenSSL to close this socket for us. */
+ BIO *bio = BIO_new_socket(fd, BIO_NOCLOSE);
+ SSL *ssl = SSL_new(server_ctx);
+ char buf[BUFSIZ];
+
+ ONN("SSL_new failed", ssl == NULL);
+
+ SSL_set_bio(ssl, bio, bio);
+
+ ONV(SSL_accept(ssl) != 1,
+ ("SSL_accept failed: %s", ERROR_SSL_STRING));
+
+ SSL_read(ssl, buf, BUFSIZ);
+
+ CALL(s_strwrite(ssl, "HTTP/1.0 200 OK\r\n"
+ "Content-Length: 0\r\n"
+ "Connection: close\r\n\r\n"));
+
+ /* Erk, shutdown is messy! See Eric Rescorla's article:
+ * http://www.linuxjournal.com/article.php?sid=4822 ; we'll just
+ * hide our heads in the sand here. */
+ SSL_shutdown(ssl);
+ SSL_free(ssl);
+
+ return OK;
+}
+
+static int init(void)
+{
+ ONN("sock_init failed.\n", sock_init());
+ server_ctx = SSL_CTX_new(SSLv23_server_method());
+ ONN("SSL_CTX_new failed", server_ctx == NULL);
+ ONN("failed to load private key",
+ !SSL_CTX_use_PrivateKey_file(server_ctx,
+ "server.key", SSL_FILETYPE_PEM));
+ ONN("failed to load certificate",
+ !SSL_CTX_use_certificate_file(server_ctx,
+ "server.pem", SSL_FILETYPE_PEM));
+ return OK;
+}
+
+static int simple(void)
+{
+ ne_session *sess;
+ int ret;
+
+ CALL(make_session(&sess, serve_ssl, NULL));
+
+ ne_set_secure(sess, 1);
+
+ ret = any_request(sess, "/foo");
+
+ CALL(await_server());
+
+ ONREQ(ret);
+
+ ne_session_destroy(sess);
+ return OK;
+}
+
+ne_test tests[] = {
+ T(init),
+
+ T(simple),
+
+ T(NULL)
+};
diff --git a/neon/test/string-tests.c b/neon/test/string-tests.c
new file mode 100644
index 000000000..c288d1e23
--- /dev/null
+++ b/neon/test/string-tests.c
@@ -0,0 +1,89 @@
+/*
+ String handling tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "string_utils.h"
+
+#include "tests.h"
+
+static int simple(void) {
+ sbuffer s = sbuffer_create();
+ ON(s == NULL);
+ ON(sbuffer_zappend(s, "abcde"));
+ ON(strcmp(sbuffer_data(s), "abcde"));
+ ON(sbuffer_size(s) != 5);
+ sbuffer_destroy(s);
+ return OK;
+}
+
+static int concat(void) {
+ sbuffer s = sbuffer_create();
+ ON(s == NULL);
+ ON(sbuffer_concat(s, "a", "b", "c", "d", "e", "f", "g", NULL));
+ ON(strcmp(sbuffer_data(s), "abcdefg"));
+ ON(sbuffer_size(s) != 7);
+ sbuffer_destroy(s);
+ return OK;
+}
+
+static int append(void) {
+ sbuffer s = sbuffer_create();
+ ON(s == NULL);
+ ON(sbuffer_append(s, "a", 1));
+ ON(sbuffer_append(s, "b", 1));
+ ON(sbuffer_append(s, "c", 1));
+ ON(strcmp(sbuffer_data(s), "abc"));
+ ON(sbuffer_size(s) != 3);
+ sbuffer_destroy(s);
+ return OK;
+}
+
+static int alter(void) {
+ sbuffer s = sbuffer_create();
+ char *d;
+ ON(s == NULL);
+ ON(sbuffer_zappend(s, "abcdefg"));
+ d = sbuffer_data(s);
+ ON(d == NULL);
+ d[2] = '\0';
+ sbuffer_altered(s);
+ ON(strcmp(sbuffer_data(s), "ab"));
+ ON(sbuffer_size(s) != 2);
+ ON(sbuffer_zappend(s, "hijkl"));
+ ON(strcmp(sbuffer_data(s), "abhijkl"));
+ return OK;
+}
+
+test_func tests[] = {
+ simple,
+ concat,
+ append,
+ alter,
+ NULL
+};
+
diff --git a/neon/test/stubs.c b/neon/test/stubs.c
new file mode 100644
index 000000000..5c6151398
--- /dev/null
+++ b/neon/test/stubs.c
@@ -0,0 +1,126 @@
+/*
+ neon test suite
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** These tests show that the stub functions produce appropriate
+ * results to provide ABI-compatibility when a particular feature is
+ * not supported by the library.
+ **/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_compress.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+#if defined(NEON_ZLIB) && defined(NEON_SSL)
+#define NO_TESTS 1
+#endif
+
+#ifndef NEON_ZLIB
+static int sd_result = OK;
+
+static void sd_reader(void *ud, const char *block, size_t len)
+{
+ const char *expect = ud;
+ if (strncmp(expect, block, len) != 0) {
+ sd_result = FAIL;
+ t_context("decompress reader got bad data");
+ }
+}
+
+static int stub_decompress(void)
+{
+ ne_session *sess;
+ ne_decompress *dc;
+ ne_request *req;
+ int ret;
+
+ CALL(make_session(&sess, single_serve_string,
+ "HTTP/1.1 200 OK" EOL
+ "Connection: close" EOL EOL
+ "abcde"));
+
+ req = ne_request_create(sess, "GET", "/foo");
+
+ dc = ne_decompress_reader(req, ne_accept_2xx, sd_reader, "abcde");
+
+ ret = ne_request_dispatch(req);
+
+ CALL(await_server());
+
+ ONREQ(ret);
+
+ ONN("decompress_destroy failed", ne_decompress_destroy(dc));
+
+ ne_request_destroy(req);
+ ne_session_destroy(sess);
+
+ /* This is a skeleton test suite file. */
+ return sd_result;
+}
+#endif
+
+#ifndef NEON_SSL
+static int stub_ssl(void)
+{
+ ne_session *sess = ne_session_create("https", "localhost", 7777);
+
+ /* these should all fail when SSL is not supported. */
+ ONN("load default CA succeeded", ne_ssl_load_default_ca(sess) == 0);
+ ONN("load CA succeeded", ne_ssl_load_ca(sess, "Makefile") == 0);
+ ONN("load PKCS12 succeeded", ne_ssl_load_pkcs12(sess, "Makefile") == 0);
+ ONN("load PEM succeeded", ne_ssl_load_pem(sess, "Makefile", NULL) == 0);
+
+ ne_session_destroy(sess);
+ return OK;
+}
+#endif
+
+#ifdef NO_TESTS
+static int null_test(void) { return OK; }
+#endif
+
+ne_test tests[] = {
+#ifndef NEON_ZLIB
+ T(stub_decompress),
+#endif
+#ifndef NEON_SSL
+ T(stub_ssl),
+#endif
+/* to prevent failure when SSL and zlib are supported. */
+#ifdef NO_TESTS
+ T(null_test),
+#endif
+ T(NULL)
+};
+
diff --git a/neon/test/tests.c b/neon/test/tests.c
new file mode 100644
index 000000000..7d25a12fc
--- /dev/null
+++ b/neon/test/tests.c
@@ -0,0 +1,103 @@
+/*
+ Stupidly simple test framework
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/signal.h>
+
+#include <stdio.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "tests.h"
+
+static const char *name = NULL;
+static int passes = 0, fails = 0, aborted = 0;
+static const char *suite;
+
+void i_am(const char *testname)
+{
+ name = testname;
+}
+
+static void segv(int signo)
+{
+ write(0, "SEGFAULT.\n", 10);
+ exit(-1);
+}
+
+int main(int argc, char *argv[]) {
+ int n;
+
+ /* get basename(argv[0]) */
+ suite = strrchr(argv[0], '/');
+ if (suite == NULL) {
+ suite = argv[0];
+ } else {
+ suite++;
+ }
+
+ (void) signal(SIGSEGV, segv);
+
+ if (tests[0] == NULL) {
+ printf("-> no tests found in %s\n", suite);
+ return -1;
+ }
+
+ printf("-> running %s:\n", suite);
+
+ for (n = 0; !aborted && tests[n] != NULL; n++) {
+ printf("%d: ", n);
+ name = NULL;
+ fflush(stdout);
+ switch (tests[n]()) {
+ case OK:
+ printf("pass.\n");
+ passes++;
+ break;
+ case FAILHARD:
+ aborted = 1;
+ /* fall-through */
+ case FAIL:
+ if (name != NULL) {
+ printf("%s - ", name);
+ }
+ printf("fail.\n");
+ fails++;
+ break;
+ }
+ }
+
+ printf("-> summary for %s: of %d tests: %d passed, %d failed. %.1f%%\n",
+ suite, n, passes, fails, 100*(float)passes/n);
+
+ return fails;
+}
+
diff --git a/neon/test/tests.h b/neon/test/tests.h
new file mode 100644
index 000000000..c68c6273c
--- /dev/null
+++ b/neon/test/tests.h
@@ -0,0 +1,49 @@
+/*
+ Stupidly simple test framework
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef TESTS_H
+#define TESTS_H 1
+
+#include <stdio.h>
+
+/* prototype of test function. */
+typedef int (*test_func)(void);
+
+/* array of test functions to call. */
+extern test_func tests[];
+
+void i_am(const char *testname);
+
+#define OK 0
+#define FAIL 1
+#define FAILHARD 2 /* fail and skip succeeding tests. */
+
+/* are __FUNCTION__ and __LINE__ gcc-isms? Probably. */
+
+#define ON(x) do { char _buf[50]; sprintf(_buf, "%s line %d", __FUNCTION__, __LINE__ ); i_am(_buf); if ((x)) return FAIL; } while(0)
+
+#define TESTFUNC do { i_am(__FUNCTION__); } while(0)
+
+#define ONN(n,x) do { i_am(n); if ((x)) return FAIL; } while(0)
+
+#define CALL(x) do { int ret = (x); if (ret != OK) return ret; } while (0)
+
+#endif /* TESTS_H */
diff --git a/neon/test/uri-tests.c b/neon/test/uri-tests.c
new file mode 100644
index 000000000..761c477ba
--- /dev/null
+++ b/neon/test/uri-tests.c
@@ -0,0 +1,188 @@
+/*
+ URI tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <uri.h>
+
+#include "tests.h"
+
+struct test_uris {
+ const char *uri;
+ const char *scheme, *hostname;
+ int port;
+ const char *path;
+} uritests[] = {
+ { "http://webdav.org/norman", "http:", "webdav.org", 80, "/norman" },
+ { "https://webdav.org/foo", "https:", "webdav.org", 443, "/foo" },
+ { "http://a/b", "a:", "a", 80, "/b" },
+ { NULL }
+};
+
+static int simple(void)
+{
+ struct uri p = {0};
+ ON(uri_parse("http://www.webdav.org/foo", &p, NULL));
+ ON(strcmp(p.host, "www.webdav.org"));
+ ON(strcmp(p.path, "/foo"));
+ ON(strcmp(p.scheme, "http"));
+ ON(p.port != -1);
+ ON(p.authinfo != NULL);
+ return 0;
+}
+
+static int simple_ssl(void)
+{
+ struct uri p = {0};
+ ON(uri_parse("https://webdav.org/", &p, NULL));
+ ON(strcmp(p.scheme, "https"));
+ ON(p.port != -1);
+ return OK;
+}
+
+static int no_path(void)
+{
+ struct uri p = {0};
+ ON(uri_parse("https://webdav.org", &p, NULL));
+ ON(strcmp(p.path, "/"));
+ return OK;
+}
+
+static int authinfo(void)
+{
+ struct uri p = {0};
+ ON(uri_parse("ftp://jim:bob@jim.com", &p, NULL));
+ ON(strcmp(p.host, "jim.com"));
+ ON(strcmp(p.authinfo, "jim:bob"));
+ return OK;
+}
+
+#define STR "/a¹²³¼½/"
+static int escapes(void)
+{
+ char *un, *esc;
+ esc = uri_abspath_escape(STR);
+ ON(esc == NULL);
+ un = uri_unescape(esc);
+ ON(un == NULL);
+ ON(strcmp(un, STR));
+ free(un);
+ free(esc);
+ return OK;
+}
+
+static int parents(void)
+{
+ char *p = uri_parent("/a/b/c");
+ ON(p == NULL);
+ ON(strcmp(p, "/a/b/"));
+ free(p);
+ p = uri_parent("/a/b/c/");
+ ON(p == NULL);
+ ON(strcmp(p, "/a/b/"));
+ free(p);
+ return OK;
+}
+
+static int abspath(void)
+{
+ const char *p = uri_abspath("http://norman:1234/a/b");
+ ON(p == NULL);
+ ON(strcmp(p, "/a/b"));
+ return OK;
+}
+
+static int compares(void)
+{
+ ON(uri_compare("/a", "/a/") != 0);
+ ON(uri_compare("/a/", "/a") != 0);
+ ON(uri_compare("/ab", "/a/") == 0);
+ ON(uri_compare("/a/", "/ab") == 0);
+ ON(uri_compare("/a/", "/a/") != 0);
+ ON(uri_compare("/a/b/c/d", "/a/b/c/") == 0);
+ return OK;
+}
+
+static int children(void)
+{
+ ON(uri_childof("/a", "/a/b") == 0);
+ ON(uri_childof("/a/", "/a/b") == 0);
+ ON(uri_childof("/aa/b/c", "/a/b/c/d/e") != 0);
+ ON(uri_childof("////", "/a") != 0);
+ return OK;
+}
+
+static int slash(void)
+{
+ ON(uri_has_trailing_slash("/a/") == 0);
+ ON(uri_has_trailing_slash("/a") != 0);
+ {
+ /* check the uri == "" case. */
+ char *foo = "/";
+ ON(uri_has_trailing_slash(&foo[1]));
+ }
+ return OK;
+}
+
+static int just_hostname(void)
+{
+ struct uri p = {0};
+ ON(uri_parse("host.name.com", &p, NULL));
+ ON(strcmp(p.host, "host.name.com"));
+ return 0;
+}
+
+static int just_path(void)
+{
+ struct uri p = {0};
+ ON(uri_parse("/argh", &p, NULL));
+ ON(strcmp(p.path, "/argh"));
+ return 0;
+}
+
+static int null_uri(void) {
+ struct uri p = {0};
+ ON(uri_parse("", &p, NULL) == 0);
+ return 0;
+}
+
+test_func tests[] = {
+ simple,
+ simple_ssl,
+ authinfo,
+ no_path,
+ escapes,
+ parents,
+ abspath,
+ compares,
+ children,
+ slash,
+ just_hostname,
+ just_path,
+ null_uri,
+ NULL
+};
diff --git a/neon/test/util-tests.c b/neon/test/util-tests.c
new file mode 100644
index 000000000..284f46b9e
--- /dev/null
+++ b/neon/test/util-tests.c
@@ -0,0 +1,139 @@
+/*
+ utils tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "http_utils.h"
+#include "neon_md5.h"
+#include "base64.h"
+#include "tests.h"
+
+static const struct {
+ const char *status;
+ int major, minor, code;
+ const char *rp;
+} accept_sl[] = {
+ /* These are really valid. */
+ { "HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+ { "HTTP/1.1000 200 OK", 1, 1000, 200, "OK" },
+ { "HTTP/1000.1000 200 OK", 1000, 1000, 200, "OK" },
+ { "HTTP/00001.1 200 OK", 1, 1, 200, "OK" },
+ { "HTTP/1.00001 200 OK", 1, 1, 200, "OK" },
+ { "HTTP/99.99 999 99999", 99, 99, 999, "99999" },
+ /* these aren't really valid but we should be able to parse them. */
+ { "HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+ { " HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+ { "Norman is a dog HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+ { NULL }
+};
+
+static const char *bad_sl[] = {
+ "",
+ "HTTP/1.1 1000 OK",
+ "HTTP/1.1 1000",
+ "HTTP/1.1 100",
+ "HTTP/ 200 OK",
+ NULL
+};
+
+static int status_lines(void)
+{
+ http_status s;
+ int n;
+ char buf[50], *pnt;
+
+ for (n = 0; accept_sl[n].status != NULL; n++) {
+ sprintf(buf, "valid %d: ", n);
+ pnt = buf + strlen(buf);
+ strcpy(pnt, "parse");
+ ONN(buf, http_parse_statusline(accept_sl[n].status, &s));
+ strcpy(pnt, "major");
+ ONN(buf, accept_sl[n].major != s.major_version);
+ strcpy(pnt, "minor");
+ ONN(buf, accept_sl[n].minor != s.minor_version);
+ strcpy(pnt, "code");
+ ONN(buf, accept_sl[n].code != s.code);
+ strcpy(pnt, "reason-phrase");
+ ONN(buf, strcmp(accept_sl[n].rp, s.reason_phrase));
+ }
+
+ for (n = 0; bad_sl[n] != NULL; n++) {
+ sprintf(buf, "invalid %d", n);
+ ONN(buf, http_parse_statusline(bad_sl[n], &s) == 0);
+ }
+
+ return OK;
+}
+
+static int md5(void)
+{
+ unsigned char buf[17] = {0}, buf2[17] = {0};
+ char ascii[33] = {0};
+
+ ne_md5_to_ascii(ne_md5_buffer("", 0, buf), ascii);
+ ONN("MD5(null)", strcmp(ascii, "d41d8cd98f00b204e9800998ecf8427e"));
+
+ ne_md5_to_ascii(ne_md5_buffer("foobar", 7, buf), ascii);
+ ONN("MD5(foobar)", strcmp(ascii, "b4258860eea29e875e2ee4019763b2bb"));
+
+ ne_ascii_to_md5(ascii, buf2);
+
+ ON(memcmp(buf, buf2, 16));
+
+ return 0;
+}
+
+static int base64(void)
+{
+#define B64(x, y) \
+do { char *_b = ne_base64(x); ON(strcmp(_b, y)); free(_b); } while (0)
+
+ /* invent these with
+ * $ printf "string" | uuencode -m blah
+ */
+ B64("a", "YQ==");
+ B64("bb", "YmI=");
+ B64("ccc", "Y2Nj");
+ B64("Hello, world", "SGVsbG8sIHdvcmxk");
+ B64("I once saw a dog called norman.\n",
+ "SSBvbmNlIHNhdyBhIGRvZyBjYWxsZWQgbm9ybWFuLgo=");
+#if 0
+ /* duh, base64() doesn't handle binary data. which moron wrote
+ * that then? add a length argument. */
+ B64("\0\0\0\0\0", "AAAAAAAK");
+#endif
+
+#undef B64
+ return OK;
+}
+
+test_func tests[] = {
+ status_lines,
+ md5,
+ base64,
+ NULL
+};
diff --git a/neon/test/utils.c b/neon/test/utils.c
new file mode 100644
index 000000000..50dbdbefa
--- /dev/null
+++ b/neon/test/utils.c
@@ -0,0 +1,40 @@
+/*
+ Utility functions for HTTP client tests
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "child.h"
+#include "tests.h"
+#include "utils.h"
+
+int single_serve_string(nsocket *s, void *userdata)
+{
+ const char *str = userdata;
+ char buf[1024];
+
+ ON(discard_request(s));
+
+ while (clength > 0) {
+ clength -= sock_read(s, buf, clength);
+ }
+
+ ON(sock_send_string(s, str));
+
+ return OK;
+}
+
diff --git a/neon/test/utils.h b/neon/test/utils.h
new file mode 100644
index 000000000..6484cf119
--- /dev/null
+++ b/neon/test/utils.h
@@ -0,0 +1,29 @@
+/*
+ neon-specific test utils
+ Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef UTILS_H
+#define UTILS_H 1
+
+#include "ne_request.h"
+
+#define ONREQ(x) do { int _ret = (x); if (_ret) { snprintf(on_err_buf, 500, "%s line %d: HTTP error:\n%s", __FUNCTION__, __LINE__, ne_get_error(sess)); i_am(on_err_buf); return FAIL; } } while (0);
+
+
+#endif /* UTILS_H */
diff --git a/neon/test/wrongcn.pem b/neon/test/wrongcn.pem
new file mode 100644
index 000000000..846ed7a41
--- /dev/null
+++ b/neon/test/wrongcn.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAv6gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBqjELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+GzAZBgNVBAMTEmhvaG9zdC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVv
+bkB3ZWJkYXYub3JnMB4XDTAyMDEzMTIwNDExNFoXDTAyMDMwMjIwNDExNFowgaox
+CzAJBgNVBAYTAkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJ
+Q2FtYnJpZGdlMRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMM
+TmVvbiBRQSBEZXB0MRswGQYDVQQDExJob2hvc3QuZXhhbXBsZS5jb20xHjAcBgkq
+hkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC
+QQDzRU5sZ8+CWQPvPkqJw9KloEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXa
+KShedFLhGidutyKKwIZJnRhtAgMBAAGjggELMIIBBzAdBgNVHQ4EFgQURQN5Lcx0
+rg/bgepiTlqBJZjrZJ8wgdcGA1UdIwSBzzCBzIAURQN5Lcx0rg/bgepiTlqBJZjr
+ZJ+hgbCkga0wgaoxCzAJBgNVBAYTAkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGly
+ZTESMBAGA1UEBxMJQ2FtYnJpZGdlMRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRk
+LjEVMBMGA1UECxMMTmVvbiBRQSBEZXB0MRswGQYDVQQDExJob2hvc3QuZXhhbXBs
+ZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZ4IBADAMBgNVHRME
+BTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAaFru+DGQ3JiV+jEoZF68PIolHwOAHBM6
+4wfO/lHI7Hb8Tn9SQh1jgIa5/7LRue4hCo9GvIrlA5DHXnZX64DEUQ==
+-----END CERTIFICATE-----
diff --git a/neon/test/xml.c b/neon/test/xml.c
new file mode 100644
index 000000000..10a2d8838
--- /dev/null
+++ b/neon/test/xml.c
@@ -0,0 +1,137 @@
+/*
+ neon test suite
+ Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_xml.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+/* validelm, startelm, endelm re-create an XML-like representation of
+ * the original document, to verify namespace handling etc works
+ * correctly. */
+static int validelm(void *userdata, ne_xml_elmid parent, ne_xml_elmid child)
+{
+ return NE_XML_VALID;
+}
+
+static int startelm(void *userdata, const struct ne_xml_elm *s,
+ const char **atts)
+{
+ ne_buffer *buf = userdata;
+ int n;
+
+ ne_buffer_concat(buf, "<", "{", s->nspace, "}", s->name, NULL);
+ for (n = 0; atts && atts[n] != NULL; n+=2) {
+ ne_buffer_concat(buf, " ", atts[n], "='", atts[n+1], "'", NULL);
+ }
+ ne_buffer_zappend(buf, ">");
+ return 0;
+}
+
+static int endelm(void *userdata, const struct ne_xml_elm *s,
+ const char *cdata)
+{
+ ne_buffer *buf = userdata;
+ ne_buffer_concat(buf, cdata?cdata:"NOCDATA", "</{", s->nspace, "}",
+ s->name, ">", NULL);
+ return 0;
+}
+
+static int parse_match(struct ne_xml_elm *elms,
+ const char *doc, const char *result)
+{
+ ne_xml_parser *p = ne_xml_create();
+ ne_buffer *buf = ne_buffer_create();
+
+ ne_xml_push_handler(p, elms, validelm, startelm, endelm, buf);
+
+ ne_xml_parse(p, doc, strlen(doc));
+
+ ONN("parse failed", !ne_xml_valid(p));
+
+ ONV(strcmp(result, buf->data),
+ ("result mismatch: %s not %s", buf->data, result));
+
+ ne_xml_destroy(p);
+ ne_buffer_destroy(buf);
+
+ return OK;
+}
+
+static int matches(void)
+{
+#define PFX "<?xml version='1.0'?>\r\n"
+#define E(ns, n) "<{" ns "}" n "></{" ns "}" n ">"
+ static const struct {
+ const char *in, *out;
+ int flags;
+ } ms[] = {
+ { PFX "<hello/>", "<{}hello></{}hello>", 0 },
+ { PFX "<hello foo='bar'/>",
+ "<{}hello foo='bar'></{}hello>", 0 },
+ /*** CDATA handling. ***/
+ { PFX "<hello> world</hello>", "<{}hello> world</{}hello>", 0 },
+ { PFX "<hello> world</hello>", "<{}hello>world</{}hello>",
+ NE_XML_STRIPWS },
+ /* test that the XML interface is truly borked. */
+ { PFX "<hello>\r\n<wide> world</wide></hello>",
+ "<{}hello><{}wide> world</{}wide></{}hello>", 0 },
+ /*** namespace handling. */
+#define NSA "xmlns:foo='bar'"
+ { PFX "<foo:widget " NSA "/>",
+ "<{bar}widget " NSA ">"
+ "</{bar}widget>" },
+ /* inherited namespace expansion. */
+ { PFX "<widget " NSA "><foo:norman/></widget>",
+ "<{}widget " NSA ">" E("bar", "norman") "</{}widget>", 0 },
+ { NULL, NULL }
+ };
+ struct ne_xml_elm elms[] = {
+ { "", "", NE_ELM_unknown, 0 },
+ { NULL }
+ };
+ int n;
+
+ for (n = 0; ms[n].in != NULL; n++) {
+ elms[0].flags = NE_XML_CDATA | ms[n].flags;
+ CALL(parse_match(elms, ms[n].in, ms[n].out));
+ }
+
+ return OK;
+}
+
+ne_test tests[] = {
+ T(matches),
+
+ T(NULL)
+};
+
diff --git a/neon/tools/ChangeLog b/neon/tools/ChangeLog
new file mode 100644
index 000000000..c85b59c16
--- /dev/null
+++ b/neon/tools/ChangeLog
@@ -0,0 +1,26 @@
+Wed May 10 14:45:55 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * cvsdist: Run autoheader if necessary.
+
+Wed May 10 14:36:17 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * reconf: New file.
+
+Sun Apr 30 21:59:56 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * cvsdist: Handle version numbers with a '-' in like 0.10.0-beta.
+
+Sun Apr 30 19:53:06 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * cvsdist: Run aclocal if there is a configure.in but no
+ aclocal.m4. Pass "-I macros" if there is a macros directory.
+
+Sun Mar 26 11:58:26 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * mk: Only display new cwd if it has changed.
+
+Tue Mar 7 20:30:13 2000 Joe Orton <joe@orton.demon.co.uk>
+
+ * cvsdist: Updated for optional arguments. Fixed specfile
+ creation.
+
diff --git a/neon/tools/README b/neon/tools/README
new file mode 100644
index 000000000..725f5c367
--- /dev/null
+++ b/neon/tools/README
@@ -0,0 +1,4 @@
+
+This directory contains scripts which may or may not be useful
+to the maintainer.
+
diff --git a/neon/tools/cvsdist b/neon/tools/cvsdist
new file mode 100755
index 000000000..2ef58a37d
--- /dev/null
+++ b/neon/tools/cvsdist
@@ -0,0 +1,143 @@
+#!/bin/sh
+# Usage: cvsdist module [tag] [version]
+# Creates a tarball from the CVS archive, generating a 'configure'
+# script and .spec file as necessary.
+# HEAD is the default tag, and mmmyy is the default version string.
+#
+# Copyright (C) 1999 Joe Orton <joe@orton.demon.co.uk>
+#
+# Id: cvsdist,v 1.8 2000/05/10 13:51:27 joe Exp
+
+# Usage, redistribution, modification under the terms of the GNU GPL,
+# see COPYING for full details.
+
+replace_version() {
+if [ -e $1.in ]; then
+ echo Creating $1 from $1.in for release $rel
+ if ! sed -e s/@VERSION@/$rel/ < $1.in > $1; then
+ echo sed failed
+ exit -5
+ fi
+fi
+}
+
+if [ $# -eq 0 -o $# -gt 3 -o "$1" = "--help" -o "$1" = "-h" ]; then
+ cat<<EOF
+Usage:
+ cvsdist foobar-x.y.z
+ => Distribute package 'foobar', at version 'x.y.z', at tag 'foobar_x-y-z'
+ cvsdist foobar
+ => Distribute package 'foobar', at version 'mmmyy', at tag 'HEAD'
+ cvsdist foobar random_tag
+ => Distribute package 'foobar', at version 'mmmyy', at tag 'random_tag'
+ cvsdist foobar random_tag x.y.z
+ => Distribute package 'foobar', at version 'x.y.z', at tag 'random_tag'
+EOF
+ exit -1
+fi
+
+# mod is the package name (e.g. cadaver)
+# rel is the version name (e.g. 0.10.0, mar12)
+# tag is the CVS tag
+
+if [ -z "$3" ]; then
+ # No version argument
+ if [ -z "$2" ]; then
+ # No tag argument either
+ # Look for a version in the package name, like cadaver-0.10.0
+ if echo $1 | grep '-' > /dev/null; then
+ # Found one, convert it from 0.10.0 to 0-10-0 for the tag
+ # and strip the package name from the beginning
+ mod=`echo $1 | sed "s/-.*$//g"`
+ rel=`echo $1 | sed "s/^[^-]*-//g"`
+ tag=${mod}_`echo $rel | sed "s/\./-/g"`
+ else
+ # No version given, use HEAD
+ mod=$1
+ rel=`date +%b%d | dd conv=lcase 2>/dev/null`
+ tag=HEAD
+ fi
+ else
+ # Got a tag, but no release name
+ mod=$1
+ tag=$2
+ rel=`date +%b%d | dd conv=lcase 2>/dev/null`
+ fi
+else
+ # Got all the info we need
+ mod=$1
+ tag=$2
+ rel=$3
+fi
+
+echo "Distributing \`$mod' for release \`$rel' at tag \`$tag'"
+mname=$mod-$rel
+tname=/tmp/$mname
+ball=/tmp/${mname}.tar.gz
+if [ -d $tname ]; then
+ echo $tname exists, cannot proceed
+ exit -1
+fi
+if [ -r $ball ]; then
+ echo $ball exists, cannot proceed
+ exit -2
+fi
+echo Exporting $mod from CVS at $tag...
+if ! cvs -Q export -d $tname -r $tag $mod; then
+ echo cvs export failed
+ exit -3
+fi
+cd $tname
+
+# Do we need to generate a configure script?
+if [ -e configure.in ]; then
+ if [ ! -e aclocal.m4 ]; then
+ # We need to run aclocal
+ ACLARGS=""
+ if [ -d macros ]; then
+ ACLARGS="-I macros"
+ fi
+ echo Running aclocal...
+ if ! aclocal $ACLARGS; then
+ echo aclocal failed
+ exit -4
+ fi
+ fi
+ echo Running `autoconf --version`...
+ if ! autoconf; then
+ echo autoconf failed
+ exit -4
+ fi
+ if [ ! -r config.h.in ]; then
+ AUHARGS=""
+ if [ -r macros/acconfig.h ]; then
+ AUHARGS="-l macros"
+ fi
+ echo Running autoheader...
+ if ! autoheader $AUHARGS; then
+ echo autoheader failed
+ exit -4
+ fi
+ fi
+fi
+
+# Replace @VERSION@ in the following files:
+replace_version $mod.spec
+replace_version Makefile.emx
+replace_version config.h.emx
+
+### It would be nice to be able to generate the po files here too,
+### but it's a bit complex, since normally you have to run configure
+### to have po/Makefile exist
+
+cd /tmp
+echo Creating tarball...
+tar czf $ball $mname
+ls -l $ball
+
+if [ -e $mname/$mod.lsm.in ]; then
+ echo Creating .lsm file for release $rel...
+ mklsm $mod $rel $ball < $mname/$mod.lsm.in > $mod.lsm
+fi
+
+rm -r $mname
diff --git a/neon/tools/mk b/neon/tools/mk
new file mode 100755
index 000000000..e09f6efdc
--- /dev/null
+++ b/neon/tools/mk
@@ -0,0 +1,19 @@
+#!/bin/sh
+showpwd=
+until [ -f Makefile ]; do
+ pre=`basename \`pwd\``/$pre
+ cd ..
+ showpwd=1
+done
+if [ ! -z $showpwd ]; then
+ echo cd `pwd`
+fi
+if [ $# -gt 0 ]; then
+ until [ $# -eq 0 ]; do
+ tar="$tar $pre$1"
+ shift
+ done
+ exec make $tar
+else
+ exec make
+fi
diff --git a/neon/tools/mklsm b/neon/tools/mklsm
new file mode 100755
index 000000000..a73f705d2
--- /dev/null
+++ b/neon/tools/mklsm
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Usage:
+# cat lsmtemplate.in | mklsm target version [tarball] > mylsmfile
+# Copyright (C) 1999 Joe Orton
+#
+# Usage, redistribution, modification under the terms of the GNU GPL,
+# see COPYING for full details.
+#
+# Id: mklsm,v 1.1.1.1 2000/03/23 19:17:11 joe Exp
+
+# You need a 'filesize' command, which does the obvious thing.
+
+#### need to configure this ####
+
+# Where the archive files are stored
+archroot=$HOME/store/archive
+
+#### that should be all ####
+
+archive=${3-$archroot/$1-$2.tar.gz}
+
+if [ ! -e $archive ]; then
+ echo Could not find archive file $archive
+ exit -1
+fi
+bytes=`filesize $archive`
+size=`expr $bytes \/ 1024`
+case `date -r $archive +%m` in
+01) month=JAN;; 02) month=FEB;; 03) month=MAR;; 04) month=APR;;
+05) month=MAY;; 06) month=JUN;; 07) month=JUL;; 08) month=AUG;;
+09) month=SEP;; 10) month=OCT;; 11) month=NOV;; 12) month=DEC;;
+esac
+date=`date -r $archive +%d`${month}`date -r $archive +%y`
+version=$2
+target=$1
+sedscr="s/@VERSION@/${version}/;s/@DATE@/${date}/;s/@SIZE@/${size}kb/"
+exec sed -e $sedscr -
diff --git a/neon/tools/reconf b/neon/tools/reconf
new file mode 100755
index 000000000..9cd28e513
--- /dev/null
+++ b/neon/tools/reconf
@@ -0,0 +1,6 @@
+#!/bin/sh
+rm -f config.cache
+([ ! -d macros ] || aclocal -I macros) && \
+([ ! -r acconfig.h ] || autoheader) && \
+([ ! -r macros/acconfig.h ] || autoheader -l macros) && \
+autoconf && CONFIG_SITE= ./configure $*
diff --git a/neon/tools/update-pot.sh b/neon/tools/update-pot.sh
new file mode 100755
index 000000000..b8ef81613
--- /dev/null
+++ b/neon/tools/update-pot.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Nicked from gnome-vfs/po, modified to be package-independant
+# -joe
+
+xgettext --default-domain=$1 --directory=.. \
+ --add-comments --keyword=_ --keyword=N_ \
+ --files-from=./POTFILES.in \
+&& test ! -f $1.po \
+ || ( rm -f ./$1.pot \
+ && mv $1.po ./$1.pot )