/* ** 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 successfully 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 successfully 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 */