summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip M. Gollucci <pgollucci@apache.org>2011-11-10 19:08:04 +0100
committerPhilip M. Gollucci <pgollucci@apache.org>2011-11-10 19:08:04 +0100
commite9806ab9d920ffa7eea1572c94189bb557ff8c52 (patch)
treee57a032c63b07d6b261dd4f69bce2bab3db3bacd
parentimport apache 2.x module portion of apreq (diff)
downloadapache2-e9806ab9d920ffa7eea1572c94189bb557ff8c52.tar.xz
apache2-e9806ab9d920ffa7eea1572c94189bb557ff8c52.zip
import apreq include files
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1200458 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--include/apreq.h308
-rw-r--r--include/apreq_cookie.h237
-rw-r--r--include/apreq_error.h97
-rw-r--r--include/apreq_module.h457
-rw-r--r--include/apreq_param.h209
-rw-r--r--include/apreq_parser.h300
-rw-r--r--include/apreq_util.h443
-rw-r--r--include/apreq_version.h105
8 files changed, 2156 insertions, 0 deletions
diff --git a/include/apreq.h b/include/apreq.h
new file mode 100644
index 0000000000..2d1ba5eba0
--- /dev/null
+++ b/include/apreq.h
@@ -0,0 +1,308 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_H
+#define APREQ_H
+
+#ifdef APREQ_DEBUG
+#include <assert.h>
+#endif
+
+#include "apr_tables.h"
+#include <stddef.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ * @file apreq.h
+ * @brief Main header file...
+ * @ingroup libapreq2
+ *
+ * Define the generic APREQ_ macros and common data structures.
+ */
+
+#ifndef WIN32
+/**
+ * The public APREQ functions are declared with APREQ_DECLARE(), so they may
+ * use the most appropriate calling convention. Public APR functions with
+ * variable arguments must use APR_DECLARE_NONSTD().
+ *
+ * @remark Both the declaration and implementations must use the same macro.
+ */
+/** APREQ_DECLARE(rettype) apeq_func(args)
+ */
+#define APREQ_DECLARE(d) APR_DECLARE(d)
+/**
+ * The public APEQ functions using variable arguments are declared with
+ * APEQ_DECLARE_NONSTD(), as they must follow the C language calling convention.
+ * @see APEQ_DECLARE @see APEQ_DECLARE_DATA
+ * @remark Both the declaration and implementations must use the same macro.
+ * @example
+ */
+/** APEQ_DECLARE_NONSTD(rettype) apr_func(args, ...);
+ */
+#define APREQ_DECLARE_NONSTD(d) APR_DECLARE_NONSTD(d)
+/**
+ * The public APREQ variables are declared with APREQ_DECLARE_DATA.
+ * This assures the appropriate indirection is invoked at compile time.
+ * @see APREQ_DECLARE @see APREQ_DECLARE_NONSTD
+ * @remark Note that the declaration and implementations use different forms,
+ * but both must include the macro.
+ */
+/** extern APREQ_DECLARE_DATA type apr_variable;\n
+ * APREQ_DECLARE_DATA type apr_variable = value;
+ */
+#define APREQ_DECLARE_DATA
+#elif defined (APREQ_DECLARE_STATIC)
+#define APREQ_DECLARE(type) type __stdcall
+#define APREQ_DECLARE_NONSTD(type) type
+#define APREQ_DECLARE_DATA
+#elif defined (APREQ_DECLARE_EXPORT)
+#define APREQ_DECLARE(type) __declspec(dllexport) type __stdcall
+#define APREQ_DECLARE_NONSTD(type) __declspec(dllexport) type
+#define APREQ_DECLARE_DATA __declspec(dllexport)
+#else
+#define APREQ_DECLARE(type) __declspec(dllimport) type __stdcall
+#define APREQ_DECLARE_NONSTD(type) __declspec(dllimport) type
+#define APREQ_DECLARE_DATA __declspec(dllimport)
+#endif
+
+/**
+ * Read chucks of data in 64k blocks from the request
+ */
+
+#define APREQ_DEFAULT_READ_BLOCK_SIZE (64 * 1024)
+
+/**
+ * Maximum number of bytes mod_apreq2 will send off to libapreq2 for parsing.
+ * mod_apreq2 will log this event and subsequently remove itself
+ * from the filter chain.
+ * @see ap_set_read_limit
+ */
+#define APREQ_DEFAULT_READ_LIMIT (64 * 1024 * 1024)
+/**
+ * Maximum number of bytes mod_apreq2 will let accumulate within the
+ * heap-buckets in a brigade. Excess data will be spooled to an
+ * appended file bucket
+ * @see ap_set_brigade_read_limit
+ */
+#define APREQ_DEFAULT_BRIGADE_LIMIT (256 * 1024)
+
+/**
+ * Number of elements in the initial apr_table
+ * @see apr_table_make
+ */
+#define APREQ_DEFAULT_NELTS 8
+
+
+
+/**
+ * Check to see if specified bit f is off in bitfield name
+ */
+#define APREQ_FLAGS_OFF(f, name) ((f) &= ~(name##_MASK << name##_BIT))
+/**
+ * Check to see if specified bit f is on in bitfield name
+ */
+#define APREQ_FLAGS_ON(f, name) ((f) |= (name##_MASK << name##_BIT))
+/**
+ * Get specified bit f in bitfield name
+ */
+#define APREQ_FLAGS_GET(f, name) (((f) >> name##_BIT) & name##_MASK)
+/**
+ * Set specified bit f in bitfield name to value
+ * Note the below BIT/Mask defines are used sans the
+ * _BIT, _MASK because of the this define's \#\#_MASK, \#\#_BIT usage.
+ * Each come in a pair
+ */
+#define APREQ_FLAGS_SET(f, name, value) \
+ ((f) = (((f) & ~(name##_MASK << name##_BIT)) \
+ | ((name##_MASK & (value)) << name##_BIT)))
+
+/**
+ * Charset Bit
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_CHARSET_BIT 0
+
+/**
+ * Charset Mask
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_CHARSET_MASK 255
+
+/**
+ * Tainted Bit
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_TAINTED_BIT 8
+/**
+ * Tainted Mask
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_TAINTED_MASK 1
+
+/**
+ * Cookier Version Bit
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+
+#define APREQ_COOKIE_VERSION_BIT 11
+/**
+ * Cookie Version Mask
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_COOKIE_VERSION_MASK 3
+
+/**
+ * Cookie's Secure Bit
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_COOKIE_SECURE_BIT 13
+/**
+ * Cookie's Secure Mask
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_COOKIE_SECURE_MASK 1
+
+/**
+ * Cookie's HttpOnly Bit
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_COOKIE_HTTPONLY_BIT 14
+/**
+ * Cookie's HttpOnly Mask
+ * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON
+ * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET
+ */
+#define APREQ_COOKIE_HTTPONLY_MASK 1
+
+/** Character encodings. */
+typedef enum {
+ APREQ_CHARSET_ASCII =0,
+ APREQ_CHARSET_LATIN1 =1, /* ISO-8859-1 */
+ APREQ_CHARSET_CP1252 =2, /* Windows-1252 */
+ APREQ_CHARSET_UTF8 =8
+} apreq_charset_t;
+
+
+/** @enum apreq_join_t Join type */
+typedef enum {
+ APREQ_JOIN_AS_IS, /**< Join the strings without modification */
+ APREQ_JOIN_ENCODE, /**< Url-encode the strings before joining them */
+ APREQ_JOIN_DECODE, /**< Url-decode the strings before joining them */
+ APREQ_JOIN_QUOTE /**< Quote the strings, backslashing existing quote marks. */
+} apreq_join_t;
+
+/** @enum apreq_match_t Match type */
+typedef enum {
+ APREQ_MATCH_FULL, /**< Full match only. */
+ APREQ_MATCH_PARTIAL /**< Partial matches are ok. */
+} apreq_match_t;
+
+/** @enum apreq_expires_t Expiration date format */
+typedef enum {
+ APREQ_EXPIRES_HTTP, /**< Use date formatting consistent with RFC 2616 */
+ APREQ_EXPIRES_NSCOOKIE /**< Use format consistent with Netscape's Cookie Spec */
+} apreq_expires_t;
+
+
+/** @brief libapreq's pre-extensible string type */
+typedef struct apreq_value_t {
+ char *name; /**< value name */
+ apr_size_t nlen; /**< length of name */
+ apr_size_t dlen; /**< length of data */
+ char data[1]; /**< value data */
+} apreq_value_t;
+
+/**
+ * Adds the specified apreq_value_t to the apr_table_t.
+ *
+ * @param v value to add
+ * @param t add v to this table
+ *
+ * @return void
+ *
+ * @ see apr_table_t @see apr_value_t
+ */
+
+static APR_INLINE
+void apreq_value_table_add(const apreq_value_t *v, apr_table_t *t) {
+ apr_table_addn(t, v->name, v->data);
+}
+
+/**
+ * @param T type
+ * @param A attribute
+ * @param P
+ *
+ * XXX
+ */
+#define apreq_attr_to_type(T,A,P) ( (T*) ((char*)(P)-offsetof(T,A)) )
+
+/**
+ * Initialize libapreq2. Applications (except apache modules using
+ * mod_apreq) should call this exactly once before they use any
+ * libapreq2 modules. If you want to modify the list of default parsers
+ * with apreq_register_parser(), please use apreq_pre_initialize()
+ * and apreq_post_initialize() instead.
+ *
+ * @param pool a base pool persisting while libapreq2 is used
+ * @remarks after you detroy the pool, you have to call this function again
+ * with a new pool if you still plan to use libapreq2
+ */
+APREQ_DECLARE(apr_status_t) apreq_initialize(apr_pool_t *pool);
+
+
+/**
+ * Pre-initialize libapreq2. Applications (except apache modules using
+ * mod_apreq2) should call this exactly once before they register custom
+ * parsers with libapreq2. mod_apreq2 does this automatically during the
+ * post-config phase, so modules that need call apreq_register_parser should
+ * create a post-config hook using APR_HOOK_MIDDLE.
+ *
+ * @param pool a base pool persisting while libapreq2 is used
+ * @remarks after you detroyed the pool, you have to call this function again
+ * with a new pool if you still plan to use libapreq2
+ */
+APREQ_DECLARE(apr_status_t) apreq_pre_initialize(apr_pool_t *pool);
+
+/**
+ * Post-initialize libapreq2. Applications (except apache modules using
+ * mod_apreq2) should this exactly once before they use any
+ * libapreq2 modules for parsing.
+ *
+ * @param pool the same pool that was used in apreq_pre_initialize().
+ */
+APREQ_DECLARE(apr_status_t) apreq_post_initialize(apr_pool_t *pool);
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* APREQ_H */
diff --git a/include/apreq_cookie.h b/include/apreq_cookie.h
new file mode 100644
index 0000000000..ee887a181d
--- /dev/null
+++ b/include/apreq_cookie.h
@@ -0,0 +1,237 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_COOKIE_H
+#define APREQ_COOKIE_H
+
+#include "apreq.h"
+#include "apr_time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file apreq_cookie.h
+ * @brief Cookies and Jars.
+ * @ingroup libapreq2
+ *
+ * apreq_cookie.h describes a common server-side API for request (incoming)
+ * and response (outgoing) cookies. It aims towards compliance with the
+ * standard cookie specifications listed below.
+ *
+ * @see http://wp.netscape.com/newsref/std/cookie_spec.html
+ * @see http://www.ietf.org/rfc/rfc2109.txt
+ * @see http://www.ietf.org/rfc/rfc2964.txt
+ * @see http://www.ietf.org/rfc/rfc2965.txt
+ *
+ */
+
+/** This macro is deprecated.
+ *
+ * Maximum length of a single Set-Cookie(2) header.
+ */
+#define APREQ_COOKIE_MAX_LENGTH 4096
+
+/** @brief Cookie type, supporting both Netscape and RFC cookie specifications.
+ */
+
+typedef struct apreq_cookie_t {
+
+ char *path; /**< Restricts url path */
+ char *domain; /**< Restricts server domain */
+ char *port; /**< Restricts server port */
+ char *comment; /**< RFC cookies may send a comment */
+ char *commentURL; /**< RFC cookies may place an URL here */
+ apr_time_t max_age; /**< total duration of cookie: -1 == session */
+ unsigned flags; /**< charsets, taint marks, app-specific bits */
+ const apreq_value_t v; /**< "raw" cookie value */
+
+} apreq_cookie_t;
+
+
+/** Upgrades a jar's table values to apreq_cookie_t structs. */
+static APR_INLINE
+apreq_cookie_t *apreq_value_to_cookie(const char *val)
+{
+ union { const char *in; char *out; } deconst;
+
+ deconst.in = val;
+ return apreq_attr_to_type(apreq_cookie_t, v,
+ apreq_attr_to_type(apreq_value_t, data, deconst.out));
+}
+
+/**@return 1 if this is an RFC cookie, 0 if its a Netscape cookie. */
+static APR_INLINE
+unsigned apreq_cookie_version(const apreq_cookie_t *c) {
+ return APREQ_FLAGS_GET(c->flags, APREQ_COOKIE_VERSION);
+}
+
+/** Sets the cookie's protocol version. */
+static APR_INLINE
+void apreq_cookie_version_set(apreq_cookie_t *c, unsigned v) {
+ APREQ_FLAGS_SET(c->flags, APREQ_COOKIE_VERSION, v);
+}
+
+/** @return 1 if the secure flag is set, 0 otherwise. */
+static APR_INLINE
+unsigned apreq_cookie_is_secure(const apreq_cookie_t *c) {
+ return APREQ_FLAGS_GET(c->flags, APREQ_COOKIE_SECURE);
+}
+
+/** Sets the cookie's secure flag, meaning it only
+ * comes back over an SSL-encrypted connction.
+ */
+static APR_INLINE
+void apreq_cookie_secure_on(apreq_cookie_t *c) {
+ APREQ_FLAGS_ON(c->flags, APREQ_COOKIE_SECURE);
+}
+
+/** Turns off the cookie's secure flag. */
+static APR_INLINE
+void apreq_cookie_secure_off(apreq_cookie_t *c) {
+ APREQ_FLAGS_OFF(c->flags, APREQ_COOKIE_SECURE);
+}
+
+/** @return 1 if the HttpOnly flag is set, 0 otherwise. */
+static APR_INLINE
+unsigned apreq_cookie_is_httponly(const apreq_cookie_t *c) {
+ return APREQ_FLAGS_GET(c->flags, APREQ_COOKIE_HTTPONLY);
+}
+
+/** Sets the cookie's HttpOnly flag, meaning it is not
+ * accessible through client-side script in supported
+ * browsers.
+ */
+static APR_INLINE
+void apreq_cookie_httponly_on(apreq_cookie_t *c) {
+ APREQ_FLAGS_ON(c->flags, APREQ_COOKIE_HTTPONLY);
+}
+
+/** Turns off the cookie's HttpOnly flag. */
+static APR_INLINE
+void apreq_cookie_httponly_off(apreq_cookie_t *c) {
+ APREQ_FLAGS_OFF(c->flags, APREQ_COOKIE_HTTPONLY);
+}
+
+
+/** @return 1 if the taint flag is set, 0 otherwise. */
+static APR_INLINE
+unsigned apreq_cookie_is_tainted(const apreq_cookie_t *c) {
+ return APREQ_FLAGS_GET(c->flags, APREQ_TAINTED);
+}
+
+/** Sets the cookie's tainted flag. */
+static APR_INLINE
+void apreq_cookie_tainted_on(apreq_cookie_t *c) {
+ APREQ_FLAGS_ON(c->flags, APREQ_TAINTED);
+}
+
+/** Turns off the cookie's tainted flag. */
+static APR_INLINE
+void apreq_cookie_tainted_off(apreq_cookie_t *c) {
+ APREQ_FLAGS_OFF(c->flags, APREQ_TAINTED);
+}
+
+/**
+ * Parse a cookie header and store the cookies in an apr_table_t.
+ *
+ * @param pool pool which allocates the cookies
+ * @param jar table where parsed cookies are stored
+ * @param header the header value
+ *
+ * @return APR_SUCCESS.
+ * @return ::APREQ_ERROR_BADSEQ if an unparseable character sequence appears.
+ * @return ::APREQ_ERROR_MISMATCH if an rfc-cookie attribute appears in a
+ * netscape cookie header.
+ * @return ::APR_ENOTIMPL if an unrecognized rfc-cookie attribute appears.
+ * @return ::APREQ_ERROR_NOTOKEN if a required token was not present.
+ * @return ::APREQ_ERROR_BADCHAR if an unexpected token was present.
+ */
+APREQ_DECLARE(apr_status_t) apreq_parse_cookie_header(apr_pool_t *pool,
+ apr_table_t *jar,
+ const char *header);
+
+/**
+ * Returns a new cookie, made from the argument list.
+ *
+ * @param pool Pool which allocates the cookie.
+ * @param name The cookie's name.
+ * @param nlen Length of name.
+ * @param value The cookie's value.
+ * @param vlen Length of value.
+ *
+ * @return the new cookie
+ */
+APREQ_DECLARE(apreq_cookie_t *) apreq_cookie_make(apr_pool_t *pool,
+ const char *name,
+ const apr_size_t nlen,
+ const char *value,
+ const apr_size_t vlen);
+
+/**
+ * Returns a string that represents the cookie as it would appear
+ * in a valid "Set-Cookie*" header.
+ *
+ * @param c cookie.
+ * @param p pool which allocates the returned string.
+ *
+ * @return header string.
+ */
+APREQ_DECLARE(char*) apreq_cookie_as_string(const apreq_cookie_t *c,
+ apr_pool_t *p);
+
+
+/**
+ * Same functionality as apreq_cookie_as_string. Stores the string
+ * representation in buf, using up to len bytes in buf as storage.
+ * The return value has the same semantics as that of apr_snprintf,
+ * including the special behavior for a "len = 0" argument.
+ *
+ * @param c cookie.
+ * @param buf storage location for the result.
+ * @param len size of buf's storage area.
+ *
+ * @return size of resulting header string.
+ */
+APREQ_DECLARE(int) apreq_cookie_serialize(const apreq_cookie_t *c,
+ char *buf, apr_size_t len);
+
+/**
+ * Set the Cookie's expiration date.
+ *
+ * @param c The cookie.
+ * @param time_str If NULL, the Cookie's expiration date is unset,
+ * making it a session cookie. This means no "expires" or "max-age"
+ * attribute will appear in the cookie's serialized form. If time_str
+ * is not NULL, the expiration date will be reset to the offset (from now)
+ * represented by time_str. The time_str should be in a format that
+ * apreq_atoi64t() can understand, namely /[+-]?\\d+\\s*[YMDhms]/.
+ *
+ * @remarks Now time_str may also be a fixed date; see apr_date_parse_rfc()
+ * for admissible formats.
+ */
+APREQ_DECLARE(void) apreq_cookie_expires(apreq_cookie_t *c,
+ const char *time_str);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /*APREQ_COOKIE_H*/
+
+
diff --git a/include/apreq_error.h b/include/apreq_error.h
new file mode 100644
index 0000000000..b2de1fd2be
--- /dev/null
+++ b/include/apreq_error.h
@@ -0,0 +1,97 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_ERROR_H
+#define APREQ_ERROR_H
+
+#include "apr_errno.h"
+#include "apreq.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ * apreq's wrapper around apr_strerror();
+ * recognizes APREQ_ERROR_* status codes.
+ */
+APREQ_DECLARE(char *)
+apreq_strerror(apr_status_t s, char *buf, apr_size_t bufsize);
+
+/**
+ * @file apreq_error.h
+ * @brief Error status codes.
+ * @ingroup libapreq2
+ *
+ * Define the APREQ_ error codes.
+ */
+
+#ifndef APR_EBADARG
+/**
+ * Bad Arguments return value
+ * @see APR_BADARG
+ */
+#define APR_EBADARG APR_BADARG /* XXX: don't use APR_BADARG */
+#endif
+
+/** Internal apreq error. */
+#define APREQ_ERROR_GENERAL APR_OS_START_USERERR
+/** Attempted to perform unsafe action with tainted data. */
+#define APREQ_ERROR_TAINTED (APREQ_ERROR_GENERAL + 1)
+/** Parsing interrupted. */
+#define APREQ_ERROR_INTERRUPT (APREQ_ERROR_GENERAL + 2)
+
+/** Invalid input data. */
+#define APREQ_ERROR_BADDATA (APREQ_ERROR_GENERAL + 10)
+/** Invalid character. */
+#define APREQ_ERROR_BADCHAR (APREQ_ERROR_BADDATA + 1)
+/** Invalid byte sequence. */
+#define APREQ_ERROR_BADSEQ (APREQ_ERROR_BADDATA + 2)
+/** Invalid attribute. */
+#define APREQ_ERROR_BADATTR (APREQ_ERROR_BADDATA + 3)
+/** Invalid header. */
+#define APREQ_ERROR_BADHEADER (APREQ_ERROR_BADDATA + 4)
+/** Invalid utf8 encoding. */
+#define APREQ_ERROR_BADUTF8 (APREQ_ERROR_BADDATA + 5)
+
+/** Missing input data. */
+#define APREQ_ERROR_NODATA (APREQ_ERROR_GENERAL + 20)
+/** Missing required token. */
+#define APREQ_ERROR_NOTOKEN (APREQ_ERROR_NODATA + 1)
+/** Missing attribute. */
+#define APREQ_ERROR_NOATTR (APREQ_ERROR_NODATA + 2)
+/** Missing header. */
+#define APREQ_ERROR_NOHEADER (APREQ_ERROR_NODATA + 3)
+/** Missing parser. */
+#define APREQ_ERROR_NOPARSER (APREQ_ERROR_NODATA + 4)
+
+
+/** Conflicting information. */
+#define APREQ_ERROR_MISMATCH (APREQ_ERROR_GENERAL + 30)
+/** Exceeds configured maximum limit. */
+#define APREQ_ERROR_OVERLIMIT (APREQ_ERROR_MISMATCH + 1)
+/** Below configured minimum limit. */
+#define APREQ_ERROR_UNDERLIMIT (APREQ_ERROR_MISMATCH + 2)
+/** Setting already configured. */
+#define APREQ_ERROR_NOTEMPTY (APREQ_ERROR_MISMATCH + 3)
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* APREQ_ERROR_H */
diff --git a/include/apreq_module.h b/include/apreq_module.h
new file mode 100644
index 0000000000..5f964348fa
--- /dev/null
+++ b/include/apreq_module.h
@@ -0,0 +1,457 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_MODULE_H
+#define APREQ_MODULE_H
+
+#include "apreq_cookie.h"
+#include "apreq_parser.h"
+#include "apreq_error.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ * @file apreq_module.h
+ * @brief Module API
+ * @ingroup libapreq2
+ */
+
+
+/**
+ * An apreq handle associated with a module. The structure
+ * may have variable size, because the module may append its own data
+ * structures after it.
+ */
+typedef struct apreq_handle_t {
+ /** the apreq module which implements this handle */
+ const struct apreq_module_t *module;
+ /** the pool which defines the lifetime of the parsed data */
+ apr_pool_t *pool;
+ /** the allocator, which persists at least as long as the pool */
+ apr_bucket_alloc_t *bucket_alloc;
+
+} apreq_handle_t;
+
+/**
+ * @brief Vtable describing the necessary module functions.
+ */
+
+
+typedef struct apreq_module_t {
+ /** name of this apreq module */
+ const char *name;
+ /** magic number identifying the module and version */
+ apr_uint32_t magic_number;
+
+ /** get a table with all cookies */
+ apr_status_t (*jar)(apreq_handle_t *, const apr_table_t **);
+ /** get a table with all query string parameters */
+ apr_status_t (*args)(apreq_handle_t *, const apr_table_t **);
+ /** get a table with all body parameters */
+ apr_status_t (*body)(apreq_handle_t *, const apr_table_t **);
+
+ /** get a cookie by its name */
+ apreq_cookie_t *(*jar_get)(apreq_handle_t *, const char *);
+ /** get a query string parameter by its name */
+ apreq_param_t *(*args_get)(apreq_handle_t *, const char *);
+ /** get a body parameter by its name */
+ apreq_param_t *(*body_get)(apreq_handle_t *, const char *);
+
+ /** gets the parser associated with the request body */
+ apr_status_t (*parser_get)(apreq_handle_t *, const apreq_parser_t **);
+ /** manually set a parser for the request body */
+ apr_status_t (*parser_set)(apreq_handle_t *, apreq_parser_t *);
+ /** add a hook function */
+ apr_status_t (*hook_add)(apreq_handle_t *, apreq_hook_t *);
+
+ /** determine the maximum in-memory bytes a brigade may use */
+ apr_status_t (*brigade_limit_get)(apreq_handle_t *, apr_size_t *);
+ /** set the maximum in-memory bytes a brigade may use */
+ apr_status_t (*brigade_limit_set)(apreq_handle_t *, apr_size_t);
+
+ /** determine the maximum amount of data that will be fed into a parser */
+ apr_status_t (*read_limit_get)(apreq_handle_t *, apr_uint64_t *);
+ /** set the maximum amount of data that will be fed into a parser */
+ apr_status_t (*read_limit_set)(apreq_handle_t *, apr_uint64_t);
+
+ /** determine the directory used by the parser for temporary files */
+ apr_status_t (*temp_dir_get)(apreq_handle_t *, const char **);
+ /** set the directory used by the parser for temporary files */
+ apr_status_t (*temp_dir_set)(apreq_handle_t *, const char *);
+
+} apreq_module_t;
+
+
+/**
+ * Defines the module-specific status codes which
+ * are commonly considered to be non-fatal.
+ *
+ * @param s status code returned by an apreq_module_t method.
+ *
+ * @return 1 if s is fatal, 0 otherwise.
+ */
+static APR_INLINE
+unsigned apreq_module_status_is_error(apr_status_t s) {
+ switch (s) {
+ case APR_SUCCESS:
+ case APR_INCOMPLETE:
+ case APR_EINIT:
+ case APREQ_ERROR_NODATA:
+ case APREQ_ERROR_NOPARSER:
+ case APREQ_ERROR_NOHEADER:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+
+/**
+ * Expose the parsed "cookie" header associated to this handle.
+ *
+ * @param req The request handle
+ * @param t The resulting table, which will either be NULL or a
+ * valid table object on return.
+ *
+ * @return APR_SUCCESS or a module-specific error status code.
+ */
+static APR_INLINE
+apr_status_t apreq_jar(apreq_handle_t *req, const apr_table_t **t)
+{
+ return req->module->jar(req,t);
+}
+
+/**
+ * Expose the parsed "query string" associated to this handle.
+ *
+ * @param req The request handle
+ * @param t The resulting table, which will either be NULL or a
+ * valid table object on return.
+ *
+ * @return APR_SUCCESS or a module-specific error status code.
+ */
+static APR_INLINE
+apr_status_t apreq_args(apreq_handle_t *req, const apr_table_t **t)
+{
+ return req->module->args(req,t);
+}
+
+/**
+ * Expose the parsed "request body" associated to this handle.
+ *
+ * @param req The request handle
+ * @param t The resulting table, which will either be NULL or a
+ * valid table object on return.
+ *
+ * @return APR_SUCCESS or a module-specific error status code.
+ */
+static APR_INLINE
+apr_status_t apreq_body(apreq_handle_t *req, const apr_table_t **t)
+{
+ return req->module->body(req, t);
+}
+
+
+/**
+ * Fetch the first cookie with the given name.
+ *
+ * @param req The request handle
+ * @param name Case-insensitive cookie name.
+ *
+ * @return First matching cookie, or NULL if none match.
+ */
+static APR_INLINE
+apreq_cookie_t *apreq_jar_get(apreq_handle_t *req, const char *name)
+{
+ return req->module->jar_get(req, name);
+}
+
+/**
+ * Fetch the first query string param with the given name.
+ *
+ * @param req The request handle
+ * @param name Case-insensitive param name.
+ *
+ * @return First matching param, or NULL if none match.
+ */
+static APR_INLINE
+apreq_param_t *apreq_args_get(apreq_handle_t *req, const char *name)
+{
+ return req->module->args_get(req, name);
+}
+
+/**
+ * Fetch the first body param with the given name.
+ *
+ * @param req The request handle
+ * @param name Case-insensitive cookie name.
+ *
+ * @return First matching param, or NULL if none match.
+ */
+static APR_INLINE
+apreq_param_t *apreq_body_get(apreq_handle_t *req, const char *name)
+{
+ return req->module->body_get(req, name);
+}
+
+/**
+ * Fetch the active body parser.
+ *
+ * @param req The request handle
+ * @param parser Points to the active parser on return.
+ *
+ * @return APR_SUCCESS or module-specific error.
+ *
+ */
+static APR_INLINE
+apr_status_t apreq_parser_get(apreq_handle_t *req,
+ const apreq_parser_t **parser)
+{
+ return req->module->parser_get(req, parser);
+}
+
+
+/**
+ * Set the body parser for this request.
+ *
+ * @param req The request handle
+ * @param parser New parser to use.
+ *
+ * @return APR_SUCCESS or module-specific error.
+ */
+static APR_INLINE
+apr_status_t apreq_parser_set(apreq_handle_t *req,
+ apreq_parser_t *parser)
+{
+ return req->module->parser_set(req, parser);
+}
+
+/**
+ * Add a parser hook for this request.
+ *
+ * @param req The request handle
+ * @param hook Hook to add.
+ *
+ * @return APR_SUCCESS or module-specific error.
+ */
+static APR_INLINE
+apr_status_t apreq_hook_add(apreq_handle_t *req, apreq_hook_t *hook)
+{
+ return req->module->hook_add(req, hook);
+}
+
+
+/**
+ * Set the active brigade limit.
+ *
+ * @param req The handle.
+ * @param bytes New limit to use.
+ *
+ * @return APR_SUCCESS or module-specific error.
+ *
+ */
+static APR_INLINE
+apr_status_t apreq_brigade_limit_set(apreq_handle_t *req,
+ apr_size_t bytes)
+{
+ return req->module->brigade_limit_set(req, bytes);
+}
+
+/**
+ * Get the active brigade limit.
+ *
+ * @param req The handle.
+ * @param bytes Pointer to resulting (current) limit.
+ *
+ * @return APR_SUCCESS or a module-specific error,
+ * which may leave bytes undefined.
+ */
+static APR_INLINE
+apr_status_t apreq_brigade_limit_get(apreq_handle_t *req,
+ apr_size_t *bytes)
+{
+ return req->module->brigade_limit_get(req, bytes);
+}
+
+/**
+ * Set the active read limit.
+ *
+ * @param req The handle.
+ * @param bytes New limit to use.
+ *
+ * @return APR_SUCCESS or a module-specific error.
+ *
+ */
+static APR_INLINE
+apr_status_t apreq_read_limit_set(apreq_handle_t *req,
+ apr_uint64_t bytes)
+{
+ return req->module->read_limit_set(req, bytes);
+}
+
+/**
+ * Get the active read limit.
+ *
+ * @param req The request handle.
+ * @param bytes Pointer to resulting (current) limit.
+ *
+ * @return APR_SUCCESS or a module-specific error,
+ * which may leave bytes undefined.
+ */
+static APR_INLINE
+apr_status_t apreq_read_limit_get(apreq_handle_t *req,
+ apr_uint64_t *bytes)
+{
+ return req->module->read_limit_get(req, bytes);
+}
+
+/**
+ * Set the active temp directory.
+ *
+ * @param req The handle.
+ * @param path New path to use; may be NULL.
+ *
+ * @return APR_SUCCESS or a module-specific error .
+ */
+static APR_INLINE
+apr_status_t apreq_temp_dir_set(apreq_handle_t *req, const char *path)
+{
+ return req->module->temp_dir_set(req, path);
+}
+
+/**
+ * Get the active temp directory.
+ *
+ * @param req The handle.
+ * @param path Resulting path to temp dir.
+ *
+ * @return APR_SUCCESS implies path is valid, but may also be NULL.
+ * Any other return value is module-specific, and may leave
+ * path undefined.
+ */
+static APR_INLINE
+apr_status_t apreq_temp_dir_get(apreq_handle_t *req, const char **path)
+{
+ return req->module->temp_dir_get(req, path);
+}
+
+
+
+/**
+ * Convenience macro for defining a module by mapping
+ * a function prefix to an associated apreq_module_t structure.
+ *
+ * @param pre Prefix to define new module. All attributes of
+ * the apreq_module_t struct are defined with this as their
+ * prefix. The generated struct is named by appending "_module" to
+ * the prefix.
+ * @param mmn Magic number (i.e. version number) of this module.
+ */
+#define APREQ_MODULE(pre, mmn) const apreq_module_t \
+ pre##_module = { #pre, mmn, \
+ pre##_jar, pre##_args, pre##_body, \
+ pre##_jar_get, pre##_args_get, pre##_body_get, \
+ pre##_parser_get, pre##_parser_set, pre##_hook_add, \
+ pre##_brigade_limit_get, pre##_brigade_limit_set, \
+ pre##_read_limit_get, pre##_read_limit_set, \
+ pre##_temp_dir_get, pre##_temp_dir_set, \
+ }
+
+
+/**
+ * Create an apreq handle which is suitable for a CGI program. It
+ * reads input from stdin and writes output to stdout.
+ *
+ * @param pool Pool associated to this handle.
+ *
+ * @return New handle; can only be NULL if the pool allocation failed.
+ *
+ * @remarks The handle gets cached in the pool's userdata, so subsequent
+ * calls will retrieve the original cached handle.
+ */
+APREQ_DECLARE(apreq_handle_t*) apreq_handle_cgi(apr_pool_t *pool);
+
+/**
+ * Create a custom apreq handle which knows only some static
+ * values. Useful if you want to test the parser code or if you have
+ * got data from a custom source (neither Apache 2 nor CGI).
+ *
+ * @param pool allocates the parse data,
+ * @param query_string parsed into args table
+ * @param cookie value of the request "Cookie" header
+ * @param parser parses the request body
+ * @param read_limit maximum bytes to read from the body
+ * @param in brigade containing the request body
+ *
+ * @return new handle; can only be NULL if the pool allocation failed.
+ */
+APREQ_DECLARE(apreq_handle_t*) apreq_handle_custom(apr_pool_t *pool,
+ const char *query_string,
+ const char *cookie,
+ apreq_parser_t *parser,
+ apr_uint64_t read_limit,
+ apr_bucket_brigade *in);
+
+/**
+ * Find the first query string parameter or body parameter with the
+ * specified name. The match is case-insensitive.
+ *
+ * @param req request handle.
+ * @param key desired parameter name
+ *
+ * @return The first matching parameter (with args searched first) or NULL.
+ */
+APREQ_DECLARE(apreq_param_t *)apreq_param(apreq_handle_t *req, const char *key);
+
+/**
+ * Find the first cookie with the specified name.
+ * The match is case-insensitive.
+ *
+ * @param req request handle.
+ * @param name desired cookie name
+ *
+ * @return The first matching cookie or NULL.
+ */
+#define apreq_cookie(req, name) apreq_jar_get(req, name)
+
+/**
+ * Returns a table containing key-value pairs for the full request
+ * (args + body).
+ *
+ * @param req request handle
+ * @param p allocates the returned table.
+ *
+ * @return table representing all available params; is never NULL.
+ */
+APREQ_DECLARE(apr_table_t *) apreq_params(apreq_handle_t *req, apr_pool_t *p);
+
+
+/**
+ * Returns a table containing all request cookies.
+ *
+ * @param req the apreq request handle
+ * @param p Allocates the returned table.
+ */
+APREQ_DECLARE(apr_table_t *)apreq_cookies(apreq_handle_t *req, apr_pool_t *p);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* APREQ_MODULE_H */
diff --git a/include/apreq_param.h b/include/apreq_param.h
new file mode 100644
index 0000000000..832cfc2f38
--- /dev/null
+++ b/include/apreq_param.h
@@ -0,0 +1,209 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_PARAM_H
+#define APREQ_PARAM_H
+
+#include "apreq.h"
+#include "apr_buckets.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @file apreq_param.h
+ * @brief Request parsing and parameter API
+ * @ingroup libapreq2
+ */
+
+
+/** Common data structure for params and file uploads */
+typedef struct apreq_param_t {
+ apr_table_t *info; /**< header table associated with the param */
+ apr_bucket_brigade *upload; /**< brigade used to spool upload files */
+ unsigned flags; /**< charsets, taint marks, app-specific bits */
+ const apreq_value_t v; /**< underlying name/value info */
+} apreq_param_t;
+
+
+/** @return 1 if the taint flag is set, 0 otherwise. */
+static APR_INLINE
+unsigned apreq_param_is_tainted(const apreq_param_t *p) {
+ return APREQ_FLAGS_GET(p->flags, APREQ_TAINTED);
+}
+
+/** Sets the tainted flag. */
+static APR_INLINE
+void apreq_param_tainted_on(apreq_param_t *p) {
+ APREQ_FLAGS_ON(p->flags, APREQ_TAINTED);
+}
+
+/** Turns off the taint flag. */
+static APR_INLINE
+void apreq_param_tainted_off(apreq_param_t *p) {
+ APREQ_FLAGS_OFF(p->flags, APREQ_TAINTED);
+}
+
+/** Sets the character encoding for this parameter. */
+static APR_INLINE
+apreq_charset_t apreq_param_charset_set(apreq_param_t *p, apreq_charset_t c) {
+ apreq_charset_t old = (apreq_charset_t)
+ APREQ_FLAGS_GET(p->flags, APREQ_CHARSET);
+ APREQ_FLAGS_SET(p->flags, APREQ_CHARSET, c);
+ return old;
+}
+
+/** Gets the character encoding for this parameter. */
+static APR_INLINE
+apreq_charset_t apreq_param_charset_get(apreq_param_t *p) {
+ return (apreq_charset_t)APREQ_FLAGS_GET(p->flags, APREQ_CHARSET);
+}
+
+
+/** Upgrades args and body table values to apreq_param_t structs. */
+static APR_INLINE
+apreq_param_t *apreq_value_to_param(const char *val)
+{
+ union { const char *in; char *out; } deconst;
+
+ deconst.in = val;
+ return apreq_attr_to_type(apreq_param_t, v,
+ apreq_attr_to_type(apreq_value_t, data, deconst.out));
+}
+
+
+
+/** creates a param from name/value information */
+APREQ_DECLARE(apreq_param_t *) apreq_param_make(apr_pool_t *p,
+ const char *name,
+ const apr_size_t nlen,
+ const char *val,
+ const apr_size_t vlen);
+
+/**
+ * Url-decodes a name=value pair into a param.
+ *
+ * @param param points to the decoded parameter on success
+ * @param pool Pool from which the param is allocated.
+ * @param word Start of the name=value pair.
+ * @param nlen Length of urlencoded name.
+ * @param vlen Length of urlencoded value.
+ *
+ * @return APR_SUCCESS on success.
+ * @return ::APREQ_ERROR_BADSEQ or ::APREQ_ERROR_BADCHAR on malformed input.
+ *
+ * @remarks Unless vlen == 0, this function assumes there is
+ * exactly one character ('=') which separates the pair.
+ *
+ */
+APREQ_DECLARE(apr_status_t) apreq_param_decode(apreq_param_t **param,
+ apr_pool_t *pool,
+ const char *word,
+ apr_size_t nlen,
+ apr_size_t vlen);
+
+/**
+ * Url-encodes the param into a name-value pair.
+ * @param pool Pool which allocates the returned string.
+ * @param param Param to encode.
+ * @return name-value pair representing the param.
+ */
+APREQ_DECLARE(char *) apreq_param_encode(apr_pool_t *pool,
+ const apreq_param_t *param);
+
+/**
+ * Parse a url-encoded string into a param table.
+ * @param pool pool used to allocate the param data.
+ * @param t table to which the params are added.
+ * @param qs Query string to url-decode.
+ * @return APR_SUCCESS if successful, error otherwise.
+ * @remark This function uses [&;] as the set of tokens
+ * to delineate words, and will treat a word w/o '='
+ * as a name-value pair with value-length = 0.
+ *
+ */
+APREQ_DECLARE(apr_status_t) apreq_parse_query_string(apr_pool_t *pool,
+ apr_table_t *t,
+ const char *qs);
+
+
+/**
+ * Returns an array of parameters (apreq_param_t *) matching the given key.
+ * The key is case-insensitive.
+ * @param p Allocates the returned array.
+ * @param t the parameter table returned by apreq_args(), apreq_body()
+ * or apreq_params()
+ * @param key Null-terminated search key, case insensitive.
+ * key==NULL fetches all parameters.
+ * @return an array of apreq_param_t* (pointers)
+ * @remark Also parses the request if necessary.
+ */
+APREQ_DECLARE(apr_array_header_t *) apreq_params_as_array(apr_pool_t *p,
+ const apr_table_t *t,
+ const char *key);
+
+/**
+ * Returns a ", " -joined string containing all parameters
+ * for the requested key, an empty string if none are found.
+ * The key is case-insensitive.
+ *
+ * @param p Allocates the return string.
+ * @param t the parameter table returned by apreq_args(), apreq_body()
+ * or apreq_params()
+ * @param key Null-terminated parameter name, case insensitive.
+ * key==NULL fetches all values.
+ * @param mode Join type- see apreq_join().
+ * @return the joined string or NULL on error
+ * @remark Also parses the request if necessary.
+ */
+APREQ_DECLARE(const char *) apreq_params_as_string(apr_pool_t *p,
+ const apr_table_t *t,
+ const char *key,
+ apreq_join_t mode);
+
+/**
+ * Returns a table of all params in req->body with non-NULL upload brigades.
+ * @param body parameter table returned by apreq_body() or apreq_params()
+ * @param pool Pool which allocates the table struct.
+ * @return Upload table.
+ * @remark Will parse the request if necessary.
+ */
+APREQ_DECLARE(const apr_table_t *) apreq_uploads(const apr_table_t *body,
+ apr_pool_t *pool);
+
+/**
+ * Returns the first param in req->body which has both param->v.name
+ * matching key (case insensitive) and param->upload != NULL.
+ * @param body parameter table returned by apreq_body() or apreq_params()
+ * @param name Parameter name. key == NULL returns first upload.
+ * @return Corresponding upload, NULL if none found.
+ * @remark Will parse the request as necessary.
+ */
+APREQ_DECLARE(const apreq_param_t *) apreq_upload(const apr_table_t *body,
+ const char *name);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APREQ_PARAM_H */
+
+
+
diff --git a/include/apreq_parser.h b/include/apreq_parser.h
new file mode 100644
index 0000000000..1c9c36ce23
--- /dev/null
+++ b/include/apreq_parser.h
@@ -0,0 +1,300 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_PARSERS_H
+#define APREQ_PARSERS_H
+/* These structs are defined below */
+
+#include "apreq_param.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @file apreq_parser.h
+ * @brief Request body parser API
+ * @ingroup libapreq2
+ */
+
+/**
+ * A hook is called by the parser whenever data arrives in a file
+ * upload parameter of the request body. You may associate any number
+ * of hooks with a parser instance with apreq_parser_add_hook().
+ */
+typedef struct apreq_hook_t apreq_hook_t;
+
+/**
+ * A request body parser instance.
+ */
+typedef struct apreq_parser_t apreq_parser_t;
+
+/** Parser arguments. */
+#define APREQ_PARSER_ARGS apreq_parser_t *parser, \
+ apr_table_t *t, \
+ apr_bucket_brigade *bb
+
+/** Hook arguments */
+#define APREQ_HOOK_ARGS apreq_hook_t *hook, \
+ apreq_param_t *param, \
+ apr_bucket_brigade *bb
+
+/**
+ * The callback function implementing a request body parser.
+ */
+typedef apr_status_t (*apreq_parser_function_t)(APREQ_PARSER_ARGS);
+
+/**
+ * The callback function of a hook. See apreq_hook_t.
+ */
+typedef apr_status_t (*apreq_hook_function_t)(APREQ_HOOK_ARGS);
+
+/**
+ * Declares a API parser.
+ */
+#define APREQ_DECLARE_PARSER(f) APREQ_DECLARE_NONSTD(apr_status_t) \
+ (f) (APREQ_PARSER_ARGS)
+
+/**
+ * Declares an API hook.
+ */
+#define APREQ_DECLARE_HOOK(f) APREQ_DECLARE_NONSTD(apr_status_t) \
+ (f) (APREQ_HOOK_ARGS)
+
+/**
+ * A hook is called by the parser whenever data arrives in a file
+ * upload parameter of the request body. You may associate any number
+ * of hooks with a parser instance with apreq_parser_add_hook().
+ */
+struct apreq_hook_t {
+ apreq_hook_function_t hook; /**< the hook function */
+ apreq_hook_t *next; /**< next item in the linked list */
+ apr_pool_t *pool; /**< pool which allocated this hook */
+ void *ctx; /**< a user defined pointer passed to the hook function */
+};
+
+/**
+ * A request body parser instance.
+ */
+struct apreq_parser_t {
+ /** the function which parses chunks of body data */
+ apreq_parser_function_t parser;
+ /** the Content-Type request header */
+ const char *content_type;
+ /** a pool which outlasts the bucket_alloc. */
+ apr_pool_t *pool;
+ /** bucket allocator used to create bucket brigades */
+ apr_bucket_alloc_t *bucket_alloc;
+ /** the maximum in-memory bytes a brigade may use */
+ apr_size_t brigade_limit;
+ /** the directory for generating temporary files */
+ const char *temp_dir;
+ /** linked list of hooks */
+ apreq_hook_t *hook;
+ /** internal context pointer used by the parser function */
+ void *ctx;
+};
+
+
+/**
+ * Parse the incoming brigade into a table. Parsers normally
+ * consume all the buckets of the brigade during parsing. However
+ * parsers may leave "rejected" data in the brigade, even during a
+ * successful parse, so callers may need to clean up the brigade
+ * themselves (in particular, rejected buckets should not be
+ * passed back to the parser again).
+ * @remark bb == NULL is valid: the parser should return its
+ * public status: APR_INCOMPLETE, APR_SUCCESS, or an error code.
+ */
+static APR_INLINE
+apr_status_t apreq_parser_run(struct apreq_parser_t *psr, apr_table_t *t,
+ apr_bucket_brigade *bb)
+{
+ return psr->parser(psr, t, bb);
+}
+
+/**
+ * Run the hook with the current parameter and the incoming
+ * bucket brigade. The hook may modify the brigade if necessary.
+ * Once all hooks have completed, the contents of the brigade will
+ * be added to the parameter's bb attribute.
+ * @return APR_SUCCESS on success. All other values represent errors.
+ */
+static APR_INLINE
+apr_status_t apreq_hook_run(struct apreq_hook_t *h, apreq_param_t *param,
+ apr_bucket_brigade *bb)
+{
+ return h->hook(h, param, bb);
+}
+
+
+/**
+ * RFC 822 Header parser. It will reject all data
+ * after the first CRLF CRLF sequence (an empty line).
+ * See apreq_parser_run() for more info on rejected data.
+ */
+APREQ_DECLARE_PARSER(apreq_parse_headers);
+
+/**
+ * RFC 2396 application/x-www-form-urlencoded parser.
+ */
+APREQ_DECLARE_PARSER(apreq_parse_urlencoded);
+
+/**
+ * RFC 2388 multipart/form-data (and XForms 1.0 multipart/related)
+ * parser. It will reject any buckets representing preamble and
+ * postamble text (this is normal behavior, not an error condition).
+ * See apreq_parser_run() for more info on rejected data.
+ */
+APREQ_DECLARE_PARSER(apreq_parse_multipart);
+
+/**
+ * Generic parser. No table entries will be added to
+ * the req->body table by this parser. The parser creates
+ * a dummy apreq_param_t to pass to any configured hooks. If
+ * no hooks are configured, the dummy param's bb slot will
+ * contain a copy of the request body. It can be retrieved
+ * by casting the parser's ctx pointer to (apreq_param_t **).
+ */
+APREQ_DECLARE_PARSER(apreq_parse_generic);
+
+/**
+ * apr_xml_parser hook. It will parse until EOS appears.
+ * The parsed document isn't available until parsing has
+ * completed successfully. The hook's ctx pointer may
+ * be cast as (apr_xml_doc **) to retrieve the
+ * parsed document.
+ */
+APREQ_DECLARE_HOOK(apreq_hook_apr_xml_parser);
+
+/**
+ * Construct a parser.
+ *
+ * @param pool Pool used to allocate the parser.
+ * @param ba bucket allocator used to create bucket brigades
+ * @param content_type Content-type that this parser can deal with.
+ * @param pfn The parser function.
+ * @param brigade_limit the maximum in-memory bytes a brigade may use
+ * @param temp_dir the directory used by the parser for temporary files
+ * @param hook Hooks to associate this parser with.
+ * @param ctx Parser's internal scratch pad.
+ * @return New parser.
+ */
+APREQ_DECLARE(apreq_parser_t *) apreq_parser_make(apr_pool_t *pool,
+ apr_bucket_alloc_t *ba,
+ const char *content_type,
+ apreq_parser_function_t pfn,
+ apr_size_t brigade_limit,
+ const char *temp_dir,
+ apreq_hook_t *hook,
+ void *ctx);
+
+/**
+ * Construct a hook.
+ *
+ * @param pool used to allocate the hook.
+ * @param hook The hook function.
+ * @param next List of other hooks for this hook to call on.
+ * @param ctx Hook's internal scratch pad.
+ * @return New hook.
+ */
+APREQ_DECLARE(apreq_hook_t *) apreq_hook_make(apr_pool_t *pool,
+ apreq_hook_function_t hook,
+ apreq_hook_t *next,
+ void *ctx);
+
+
+/**
+ * Add a new hook to the end of the parser's hook list.
+ *
+ * @param p Parser.
+ * @param h Hook to append.
+ */
+APREQ_DECLARE(apr_status_t) apreq_parser_add_hook(apreq_parser_t *p,
+ apreq_hook_t *h);
+
+
+/**
+ * Fetch the default parser function associated with the given MIME type.
+ * @param enctype The desired enctype (can also be a full "Content-Type"
+ * header).
+ * @return The parser function, or NULL if the enctype is unrecognized.
+ */
+APREQ_DECLARE(apreq_parser_function_t)apreq_parser(const char *enctype);
+
+
+/**
+ * Register a new parsing function with a MIME enctype.
+ * Registered parsers are added to apreq_parser()'s
+ * internal lookup table.
+ *
+ * @param enctype The MIME type.
+ * @param pfn The function to use during parsing. Setting
+ * parser == NULL will remove an existing parser.
+ *
+ * @return APR_SUCCESS or error.
+ */
+
+APREQ_DECLARE(apr_status_t) apreq_register_parser(const char *enctype,
+ apreq_parser_function_t pfn);
+
+
+/**
+ * Returns APREQ_ERROR_GENERAL. Effectively disables mfd parser
+ * if a file-upload field is present.
+ *
+ */
+APREQ_DECLARE_HOOK(apreq_hook_disable_uploads);
+
+/**
+ * Calls apr_brigade_cleanup on the incoming brigade
+ * after passing the brigade to any subsequent hooks.
+ */
+APREQ_DECLARE_HOOK(apreq_hook_discard_brigade);
+
+/**
+ * Context struct for the apreq_hook_find_param hook.
+ */
+typedef struct apreq_hook_find_param_ctx_t {
+ const char *name;
+ apreq_param_t *param;
+ apreq_hook_t *prev;
+} apreq_hook_find_param_ctx_t;
+
+
+/**
+ * Special purpose utility for locating a parameter
+ * during parsing. The hook's ctx shoud be initialized
+ * to an apreq_hook_find_param_ctx_t *, with the name
+ * attribute set to the sought parameter name, the param
+ * attribute set to NULL, and the prev attribute set to
+ * the address of the previous hook. The param attribute
+ * will be reassigned to the first param found, and once
+ * that happens this hook is immediately removed from the chain.
+ *
+ * @remarks When used, this should always be the first hook
+ * invoked, so add it manually with ctx->prev = &parser->hook
+ * instead of using apreq_parser_add_hook.
+ */
+APREQ_DECLARE_HOOK(apreq_hook_find_param);
+
+
+#ifdef __cplusplus
+}
+
+#endif
+#endif /* APREQ_PARSERS_H */
diff --git a/include/apreq_util.h b/include/apreq_util.h
new file mode 100644
index 0000000000..feb2d396ce
--- /dev/null
+++ b/include/apreq_util.h
@@ -0,0 +1,443 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_UTIL_H
+#define APREQ_UTIL_H
+
+#include "apr_file_io.h"
+#include "apr_buckets.h"
+#include "apreq.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ * This header contains useful functions for creating new
+ * parsers, hooks or modules. It includes
+ *
+ * - string <-> array converters
+ * - substring search functions
+ * - simple encoders & decoders for urlencoded strings
+ * - simple time, date, & file-size converters
+ * @file apreq_util.h
+ * @brief Utility functions for apreq.
+ * @ingroup libapreq2
+ */
+
+/**
+ * Join an array of values. The result is an empty string if there are
+ * no values.
+ *
+ * @param p Pool to allocate return value.
+ * @param sep String that is inserted between the joined values.
+ * @param arr Array of apreq_value_t entries.
+ * @param mode Join type- see apreq_join_t.
+ *
+ * @return Joined string, or NULL on error
+ */
+APREQ_DECLARE(char *) apreq_join(apr_pool_t *p,
+ const char *sep,
+ const apr_array_header_t *arr,
+ apreq_join_t mode);
+
+/**
+ * Returns offset of match string's location, or -1 if no match is found.
+ *
+ * @param hay Location of bytes to scan.
+ * @param hlen Number of bytes available for scanning.
+ * @param ndl Search string
+ * @param nlen Length of search string.
+ * @param type Match type.
+ *
+ * @return Offset of match string, or -1 if no match is found.
+ *
+ */
+APREQ_DECLARE(apr_ssize_t) apreq_index(const char* hay, apr_size_t hlen,
+ const char* ndl, apr_size_t nlen,
+ const apreq_match_t type);
+
+/**
+ * Places a quoted copy of src into dest. Embedded quotes are escaped with a
+ * backslash ('\').
+ *
+ * @param dest Location of quoted copy. Must be large enough to hold the copy
+ * and trailing null byte.
+ * @param src Original string.
+ * @param slen Length of original string.
+ * @param dest Destination string.
+ *
+ * @return length of quoted copy in dest.
+ */
+APREQ_DECLARE(apr_size_t) apreq_quote(char *dest, const char *src,
+ const apr_size_t slen);
+
+/**
+ *
+ * Same as apreq_quote() except when src begins and ends in quote marks. In
+ * that case it assumes src is quoted correctly, and just copies src to dest.
+ *
+ * @param dest Location of quoted copy. Must be large enough to hold the copy
+ * and trailing null byte.
+ * @param src Original string.
+ * @param slen Length of original string.
+ * @param dest Destination string.
+ *
+ * @return length of quoted copy in dest.
+ */
+APREQ_DECLARE(apr_size_t) apreq_quote_once(char *dest, const char *src,
+ const apr_size_t slen);
+
+/**
+ * Url-encodes a string.
+ *
+ * @param dest Location of url-encoded result string. Caller must ensure it
+ * is large enough to hold the encoded string and trailing '\\0'.
+ * @param src Original string.
+ * @param slen Length of original string.
+ *
+ * @return length of url-encoded string in dest; does not exceed 3 * slen.
+ */
+APREQ_DECLARE(apr_size_t) apreq_encode(char *dest, const char *src,
+ const apr_size_t slen);
+
+/**
+ * Convert a string from cp1252 to utf8. Caller must ensure it is large enough
+ * to hold the encoded string and trailing '\\0'.
+ *
+ * @param dest Location of utf8-encoded result string. Caller must ensure it
+ * is large enough to hold the encoded string and trailing '\\0'.
+ * @param src Original string.
+ * @param slen Length of original string.
+ *
+ * @return length of utf8-encoded string in dest; does not exceed 3 * slen.
+ */
+APREQ_DECLARE(apr_size_t) apreq_cp1252_to_utf8(char *dest,
+ const char *src, apr_size_t slen);
+
+/**
+ * Heuristically determine the charset of a string.
+ *
+ * @param src String to scan.
+ * @param slen Length of string.
+ *
+ * @return APREQ_CHARSET_ASCII if the string contains only 7-bit chars;
+ * @return APREQ_CHARSET_UTF8 if the string is a valid utf8 byte sequence;
+ * @return APREQ_CHARSET_LATIN1 if the string has no control chars;
+ * @return APREQ_CHARSET_CP1252 if the string has control chars.
+ */
+APREQ_DECLARE(apreq_charset_t) apreq_charset_divine(const char *src,
+ apr_size_t slen);
+
+/**
+ * Url-decodes a string.
+ *
+ * @param dest Location of url-encoded result string. Caller must ensure dest is
+ * large enough to hold the encoded string and trailing null character.
+ * @param dlen points to resultant length of url-decoded string in dest
+ * @param src Original string.
+ * @param slen Length of original string.
+ *
+ * @return APR_SUCCESS.
+ * @return APR_INCOMPLETE if the string
+ * ends in the middle of an escape sequence.
+ * @return ::APREQ_ERROR_BADSEQ or ::APREQ_ERROR_BADCHAR on malformed input.
+ *
+ * @remarks In the non-success case, dlen will be set to include
+ * the last succesfully decoded value. This function decodes
+ * \%uXXXX into a utf8 (wide) character, following ECMA-262
+ * (the Javascript spec) Section B.2.1.
+ */
+
+APREQ_DECLARE(apr_status_t) apreq_decode(char *dest, apr_size_t *dlen,
+ const char *src, apr_size_t slen);
+
+/**
+ * Url-decodes an iovec array.
+ *
+ * @param dest Location of url-encoded result string. Caller must ensure dest is
+ * large enough to hold the encoded string and trailing null character.
+ * @param dlen Resultant length of dest.
+ * @param v Array of iovecs that represent the source string
+ * @param nelts Number of iovecs in the array.
+ *
+ * @return APR_SUCCESS.
+ * @return APR_INCOMPLETE if the iovec
+ * ends in the middle of an escape sequence.
+ * @return ::APREQ_ERROR_BADSEQ or ::APREQ_ERROR_BADCHAR on malformed input.
+ *
+ * @remarks In the non-APR_SUCCESS case, dlen will be set to include
+ * the last succesfully decoded value. This function decodes
+ * \%uXXXX into a utf8 (wide) character, following ECMA-262
+ * (the Javascript spec) Section B.2.1.
+ */
+
+APREQ_DECLARE(apr_status_t) apreq_decodev(char *dest, apr_size_t *dlen,
+ struct iovec *v, int nelts);
+
+/**
+ * Returns an url-encoded copy of a string.
+ *
+ * @param p Pool used to allocate the return value.
+ * @param src Original string.
+ * @param slen Length of original string.
+ *
+ * @return The url-encoded string.
+ *
+ * @remarks Use this function insead of apreq_encode if its
+ * caller might otherwise overflow dest.
+ */
+static APR_INLINE
+char *apreq_escape(apr_pool_t *p, const char *src, const apr_size_t slen)
+{
+ char *rv;
+
+ if (src == NULL)
+ return NULL;
+
+ rv = (char *)apr_palloc(p, 3 * slen + 1);
+ apreq_encode(rv, src, slen);
+ return rv;
+}
+
+/**
+ * An \e in-situ url-decoder.
+ *
+ * @param str The string to decode
+ *
+ * @return Length of decoded string, or < 0 on error.
+ */
+static APR_INLINE apr_ssize_t apreq_unescape(char *str)
+{
+ apr_size_t len;
+ apr_status_t rv = apreq_decode(str, &len, str, strlen(str));
+ if (rv == APR_SUCCESS)
+ return (apr_ssize_t)len;
+ else
+ return -1;
+}
+
+/**
+ * Converts file sizes (KMG) to bytes
+ *
+ * @param s file size matching m/^\\d+[KMG]b?$/i
+ *
+ * @return 64-bit integer representation of s.
+ *
+ * @todo What happens when s is malformed? Should this return
+ * an unsigned value instead?
+ */
+
+APREQ_DECLARE(apr_int64_t) apreq_atoi64f(const char *s);
+
+/**
+ * Converts time strings (YMDhms) to seconds
+ *
+ * @param s time string matching m/^\\+?\\d+[YMDhms]$/
+ *
+ * @return 64-bit integer representation of s as seconds.
+ *
+ * @todo What happens when s is malformed? Should this return
+ * an unsigned value instead?
+ */
+
+APREQ_DECLARE(apr_int64_t) apreq_atoi64t(const char *s);
+
+/**
+ * Writes brigade to a file.
+ *
+ * @param f File that gets the brigade.
+ * @param wlen On a successful return, wlen holds the length of
+ * the brigade, which is the amount of data written to
+ * the file.
+ * @param bb Bucket brigade.
+ *
+ * @return APR_SUCCESS.
+ * @return Error status code from either an unsuccessful apr_bucket_read(),
+ * or a failed apr_file_writev().
+ *
+ * @remarks This function leaks a bucket brigade into bb->p whenever
+ * the final bucket in bb is a spool bucket.
+ */
+
+APREQ_DECLARE(apr_status_t) apreq_brigade_fwrite(apr_file_t *f,
+ apr_off_t *wlen,
+ apr_bucket_brigade *bb);
+/**
+ * Makes a temporary file.
+ *
+ * @param fp Points to the temporary apr_file_t on success.
+ * @param pool Pool to associate with the temp file. When the
+ * pool is destroyed, the temp file will be closed
+ * and deleted.
+ * @param path The base directory which will contain the temp file.
+ * If param == NULL, the directory will be selected via
+ * tempnam(). See the tempnam manpage for details.
+ *
+ * @return APR_SUCCESS.
+ * @return Error status code from unsuccessful apr_filepath_merge(),
+ * or a failed apr_file_mktemp().
+ */
+
+APREQ_DECLARE(apr_status_t) apreq_file_mktemp(apr_file_t **fp,
+ apr_pool_t *pool,
+ const char *path);
+
+/**
+ * Set aside all buckets in the brigade.
+ *
+ * @param bb Brigade.
+ * @param p Setaside buckets into this pool.
+ * @return APR_SUCCESS.
+ * @return Error status code from an unsuccessful apr_bucket_setaside().
+ */
+
+static APR_INLINE
+apr_status_t apreq_brigade_setaside(apr_bucket_brigade *bb, apr_pool_t *p)
+{
+ apr_bucket *e;
+ for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb);
+ e = APR_BUCKET_NEXT(e))
+ {
+ apr_status_t rv = apr_bucket_setaside(e, p);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ return APR_SUCCESS;
+}
+
+
+/**
+ * Copy a brigade.
+ *
+ * @param d (destination) Copied buckets are appended to this brigade.
+ * @param s (source) Brigade to copy from.
+ *
+ * @return APR_SUCCESS.
+ * @return Error status code from an unsuccessful apr_bucket_copy().
+ *
+ * @remarks s == d produces Undefined Behavior.
+ */
+
+static APR_INLINE
+apr_status_t apreq_brigade_copy(apr_bucket_brigade *d, apr_bucket_brigade *s) {
+ apr_bucket *e;
+ for (e = APR_BRIGADE_FIRST(s); e != APR_BRIGADE_SENTINEL(s);
+ e = APR_BUCKET_NEXT(e))
+ {
+ apr_bucket *c;
+ apr_status_t rv = apr_bucket_copy(e, &c);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ APR_BRIGADE_INSERT_TAIL(d, c);
+ }
+ return APR_SUCCESS;
+}
+
+/**
+ * Move the front of a brigade.
+ *
+ * @param d (destination) Append buckets to this brigade.
+ * @param s (source) Brigade to take buckets from.
+ * @param e First bucket of s after the move. All buckets
+ * before e are appended to d.
+ *
+ * @remarks This moves all buckets when e == APR_BRIGADE_SENTINEL(s).
+ */
+
+static APR_INLINE
+void apreq_brigade_move(apr_bucket_brigade *d, apr_bucket_brigade *s,
+ apr_bucket *e)
+{
+ apr_bucket *f;
+
+ if (e != APR_BRIGADE_SENTINEL(s)) {
+ f = APR_RING_FIRST(&s->list);
+ if (f == e) /* zero buckets to be moved */
+ return;
+
+ /* obtain the last bucket to be moved */
+ e = APR_RING_PREV(e, link);
+
+ APR_RING_UNSPLICE(f, e, link);
+ APR_RING_SPLICE_HEAD(&d->list, f, e, apr_bucket, link);
+ }
+ else {
+ APR_BRIGADE_CONCAT(d, s);
+ }
+}
+
+
+/**
+ * Search a header string for the value of a particular named attribute.
+ *
+ * @param hdr Header string to scan.
+ * @param name Name of attribute to search for.
+ * @param nlen Length of name.
+ * @param val Location of (first) matching value.
+ * @param vlen Length of matching value.
+ *
+ * @return APR_SUCCESS.
+ * @return ::APREQ_ERROR_NOATTR if the attribute is not found.
+ * @return ::APREQ_ERROR_BADSEQ if an unpaired quote mark was detected.
+ */
+APREQ_DECLARE(apr_status_t) apreq_header_attribute(const char *hdr,
+ const char *name,
+ const apr_size_t nlen,
+ const char **val,
+ apr_size_t *vlen);
+
+
+/**
+ * Concatenates the brigades, spooling large brigades into
+ * a tempfile (APREQ_SPOOL) bucket.
+ *
+ * @param pool Pool for creating a tempfile bucket.
+ * @param temp_dir Directory for tempfile creation.
+ * @param brigade_limit If out's length would exceed this value,
+ * the appended buckets get written to a tempfile.
+ * @param out Resulting brigade.
+ * @param in Brigade to append.
+ *
+ * @return APR_SUCCESS.
+ * @return Error status code resulting from either apr_brigade_length(),
+ * apreq_file_mktemp(), apreq_brigade_fwrite(), or apr_file_seek().
+ *
+ * @todo Flesh out these error codes, making them as explicit as possible.
+ */
+APREQ_DECLARE(apr_status_t) apreq_brigade_concat(apr_pool_t *pool,
+ const char *temp_dir,
+ apr_size_t brigade_limit,
+ apr_bucket_brigade *out,
+ apr_bucket_brigade *in);
+
+/**
+ * Determines the spool file used by the brigade. Returns NULL if the
+ * brigade is not spooled in a file (does not use an APREQ_SPOOL
+ * bucket).
+ *
+ * @param bb the bucket brigade
+ * @return the spool file, or NULL.
+ */
+APREQ_DECLARE(apr_file_t *)apreq_brigade_spoolfile(apr_bucket_brigade *bb);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* APREQ_UTIL_H */
diff --git a/include/apreq_version.h b/include/apreq_version.h
new file mode 100644
index 0000000000..8f5b8d2796
--- /dev/null
+++ b/include/apreq_version.h
@@ -0,0 +1,105 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef APREQ_VERSION_H
+#define APREQ_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "apr_version.h"
+#include "apreq.h"
+
+/**
+ * @file apreq_version.h
+ * @brief Versioning API for libapreq
+ * @ingroup libapreq2
+ *
+ * There are several different mechanisms for accessing the version. There
+ * is a string form, and a set of numbers; in addition, there are constants
+ * which can be compiled into your application, and you can query the library
+ * being used for its actual version.
+ *
+ * Note that it is possible for an application to detect that it has been
+ * compiled against a different version of libapreq by use of the compile-time
+ * constants and the use of the run-time query function.
+ *
+ * libapreq version numbering follows the guidelines specified in:
+ *
+ * http://apr.apache.org/versioning.html
+ */
+
+/* The numeric compile-time version constants. These constants are the
+ * authoritative version numbers for libapreq.
+ */
+
+/** major version
+ * Major API changes that could cause compatibility problems for older
+ * programs such as structure size changes. No binary compatibility is
+ * possible across a change in the major version.
+ */
+#define APREQ_MAJOR_VERSION 2
+
+/**
+ * Minor API changes that do not cause binary compatibility problems.
+ * Should be reset to 0 when upgrading APREQ_MAJOR_VERSION
+ */
+#define APREQ_MINOR_VERSION 8
+
+/** patch level */
+#define APREQ_PATCH_VERSION 0
+
+/**
+ * This symbol is defined for internal, "development" copies of libapreq.
+ * This symbol will be \#undef'd for releases.
+ */
+#define APREQ_IS_DEV_VERSION
+
+
+/** The formatted string of libapreq's version */
+#define APREQ_VERSION_STRING \
+ APR_STRINGIFY(APREQ_MAJOR_VERSION) "." \
+ APR_STRINGIFY(APREQ_MINOR_VERSION) "." \
+ APR_STRINGIFY(APREQ_PATCH_VERSION) \
+ APREQ_IS_DEV_STRING
+
+/**
+ * Return libapreq's version information information in a numeric form.
+ *
+ * @param pvsn Pointer to a version structure for returning the version
+ * information.
+ */
+APREQ_DECLARE(void) apreq_version(apr_version_t *pvsn);
+
+/** Return libapreq's version information as a string. */
+APREQ_DECLARE(const char *) apreq_version_string(void);
+
+
+/** Internal: string form of the "is dev" flag */
+#ifdef APREQ_IS_DEV_VERSION
+#define APREQ_IS_DEV_STRING "-dev"
+#else
+#define APREQ_IS_DEV_STRING ""
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APREQ_VERSION_H */