diff options
author | Philip M. Gollucci <pgollucci@apache.org> | 2011-11-10 19:08:04 +0100 |
---|---|---|
committer | Philip M. Gollucci <pgollucci@apache.org> | 2011-11-10 19:08:04 +0100 |
commit | e9806ab9d920ffa7eea1572c94189bb557ff8c52 (patch) | |
tree | e57a032c63b07d6b261dd4f69bce2bab3db3bacd | |
parent | import apache 2.x module portion of apreq (diff) | |
download | apache2-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.h | 308 | ||||
-rw-r--r-- | include/apreq_cookie.h | 237 | ||||
-rw-r--r-- | include/apreq_error.h | 97 | ||||
-rw-r--r-- | include/apreq_module.h | 457 | ||||
-rw-r--r-- | include/apreq_param.h | 209 | ||||
-rw-r--r-- | include/apreq_parser.h | 300 | ||||
-rw-r--r-- | include/apreq_util.h | 443 | ||||
-rw-r--r-- | include/apreq_version.h | 105 |
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 */ |