import apreq include files
authorPhilip M. Gollucci <pgollucci@apache.org>
Thu, 10 Nov 2011 18:08:04 +0000 (18:08 +0000)
committerPhilip M. Gollucci <pgollucci@apache.org>
Thu, 10 Nov 2011 18:08:04 +0000 (18:08 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1200458 13f79535-47bb-0310-9956-ffa450edef68

include/apreq.h [new file with mode: 0644]
include/apreq_cookie.h [new file with mode: 0644]
include/apreq_error.h [new file with mode: 0644]
include/apreq_module.h [new file with mode: 0644]
include/apreq_param.h [new file with mode: 0644]
include/apreq_parser.h [new file with mode: 0644]
include/apreq_util.h [new file with mode: 0644]
include/apreq_version.h [new file with mode: 0644]

diff --git a/include/apreq.h b/include/apreq.h
new file mode 100644 (file)
index 0000000..2d1ba5e
--- /dev/null
@@ -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 (file)
index 0000000..ee887a1
--- /dev/null
@@ -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 (file)
index 0000000..b2de1fd
--- /dev/null
@@ -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 (file)
index 0000000..5f96434
--- /dev/null
@@ -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 (file)
index 0000000..832cfc2
--- /dev/null
@@ -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 (file)
index 0000000..1c9c36c
--- /dev/null
@@ -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 (file)
index 0000000..feb2d39
--- /dev/null
@@ -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 (file)
index 0000000..8f5b8d2
--- /dev/null
@@ -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 */