#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
+#if APR_HAVE_PROCESS_H
+#include <process.h> /* for getpid() on Win32 */
+#endif
#if APR_HAVE_NETDB_H
#include <netdb.h> /* for gethostbyname() */
#endif
*/
#ifdef CASE_BLIND_FILESYSTEM
#define IS_SLASH(s) ((s == '/') || (s == '\\'))
+#define SLASHES "/\\"
#else
#define IS_SLASH(s) (s == '/')
+#define SLASHES "/"
#endif
APLOG_USE_MODULE(core);
if (no > 9) { /* Ordinary character. */
if (c == '\\' && *src)
- c = *src++;
+ src++;
len++;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
return res;
}
-/* Check a string for any ${ENV} environment variable
- * construct and replace each them by the value of
- * that environment variable, if it exists. If the
- * environment value does not exist, leave the ${ENV}
- * construct alone; it means something else.
- */
-AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
-{
-# define SMALL_EXPANSION 5
- struct sll {
- struct sll *next;
- const char *string;
- apr_size_t len;
- } *result, *current, sresult[SMALL_EXPANSION];
- char *res_buf, *cp;
- const char *s, *e, *ep;
- unsigned spc;
- apr_size_t outlen;
-
- s = ap_strchr_c(word, '$');
- if (!s) {
- return word;
- }
-
- /* well, actually something to do */
- ep = word + strlen(word);
- spc = 0;
- result = current = &(sresult[spc++]);
- current->next = NULL;
- current->string = word;
- current->len = s - word;
- outlen = current->len;
-
- do {
- /* prepare next entry */
- if (current->len) {
- current->next = (spc < SMALL_EXPANSION)
- ? &(sresult[spc++])
- : (struct sll *)apr_palloc(p,
- sizeof(*current->next));
- current = current->next;
- current->next = NULL;
- current->len = 0;
- }
-
- if (*s == '$') {
- if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) {
- word = getenv(apr_pstrndup(p, s+2, e-s-2));
- if (word) {
- current->string = word;
- current->len = strlen(word);
- outlen += current->len;
- }
- else {
- current->string = s;
- current->len = e - s + 1;
- outlen += current->len;
- }
- s = e + 1;
- }
- else {
- current->string = s++;
- current->len = 1;
- ++outlen;
- }
- }
- else {
- word = s;
- s = ap_strchr_c(s, '$');
- current->string = word;
- current->len = s ? s - word : ep - word;
- outlen += current->len;
- }
- } while (s && *s);
-
- /* assemble result */
- res_buf = cp = apr_palloc(p, outlen + 1);
- do {
- if (result->len) {
- memcpy(cp, result->string, result->len);
- cp += result->len;
- }
- result = result->next;
- } while (result);
- res_buf[outlen] = '\0';
-
- return res_buf;
-}
-
AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
{
#ifdef DEBUG
return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
}
+/* we can't use apr_file_* directly because of linking issues on Windows */
static apr_status_t cfg_close(void *param)
{
- apr_file_t *cfp = (apr_file_t *) param;
- return (apr_file_close(cfp));
+ return apr_file_close(param);
}
-static int cfg_getch(void *param)
+static apr_status_t cfg_getch(char *ch, void *param)
{
- char ch;
- apr_file_t *cfp = (apr_file_t *) param;
- if (apr_file_getc(&ch, cfp) == APR_SUCCESS)
- return ch;
- return (int)EOF;
+ return apr_file_getc(ch, param);
}
-static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
+static apr_status_t cfg_getstr(void *buf, size_t bufsiz, void *param)
{
- apr_file_t *cfp = (apr_file_t *) param;
- apr_status_t rv;
- rv = apr_file_gets(buf, bufsiz, cfp);
- if (rv == APR_SUCCESS) {
- return buf;
- }
- return NULL;
+ return apr_file_gets(buf, bufsiz, param);
}
/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
new_cfg = apr_palloc(p, sizeof(*new_cfg));
new_cfg->param = file;
new_cfg->name = apr_pstrdup(p, name);
- new_cfg->getch = (int (*)(void *)) cfg_getch;
- new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
- new_cfg->close = (int (*)(void *)) cfg_close;
+ new_cfg->getch = cfg_getch;
+ new_cfg->getstr = cfg_getstr;
+ new_cfg->close = cfg_close;
new_cfg->line_number = 0;
*ret_cfg = new_cfg;
return APR_SUCCESS;
/* Allocate a ap_configfile_t handle with user defined functions and params */
-AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p,
- const char *descr,
- void *param,
- int(*getch)(void *param),
- void *(*getstr) (void *buf, size_t bufsiz, void *param),
- int(*close_func)(void *param))
+AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(
+ apr_pool_t *p, const char *descr, void *param,
+ apr_status_t (*getc_func) (char *ch, void *param),
+ apr_status_t (*gets_func) (void *buf, size_t bufsize, void *param),
+ apr_status_t (*close_func) (void *param))
{
ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
-#ifdef DEBUG
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "Opening config handler %s", descr);
-#endif
new_cfg->param = param;
new_cfg->name = descr;
- new_cfg->getch = getch;
- new_cfg->getstr = getstr;
+ new_cfg->getch = getc_func;
+ new_cfg->getstr = gets_func;
new_cfg->close = close_func;
new_cfg->line_number = 0;
return new_cfg;
}
/* Read one character from a configfile_t */
-AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp)
+AP_DECLARE(apr_status_t) ap_cfg_getc(char *ch, ap_configfile_t *cfp)
{
- register int ch = cfp->getch(cfp->param);
- if (ch == LF)
+ apr_status_t rc = cfp->getch(ch, cfp->param);
+ if (rc == APR_SUCCESS && *ch == LF)
++cfp->line_number;
- return ch;
+ return rc;
+}
+
+AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp,
+ apr_status_t rc)
+{
+ char buf[MAX_STRING_LEN];
+ if (rc == APR_SUCCESS)
+ return NULL;
+ return apr_psprintf(p, "Error reading %s at line %d: %s",
+ cfp->name, cfp->line_number,
+ rc == APR_ENOSPC ? "Line too long"
+ : apr_strerror(rc, buf, sizeof(buf)));
}
/* Read one line from open ap_configfile_t, strip LF, increase line number */
/* If custom handler does not define a getstr() function, read char by char */
-AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
+AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
{
+ apr_status_t rc;
+ char *src, *dst;
/* If a "get string" function is defined, use it */
if (cfp->getstr != NULL) {
- char *src, *dst;
char *cp;
char *cbuf = buf;
size_t cbufsize = bufsize;
while (1) {
++cfp->line_number;
- if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
- return 1;
+ rc = cfp->getstr(cbuf, cbufsize, cfp->param);
+ if (rc == APR_EOF) {
+ if (cbuf != buf) {
+ *cbuf = '\0';
+ break;
+ }
+ else {
+ return APR_EOF;
+ }
+ }
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
/*
* check for line continuation,
* i.e. match [^\\]\\[\r]\n only
*/
cp = cbuf;
- while (cp < cbuf+cbufsize && *cp != '\0')
- cp++;
+ cp += strlen(cp);
if (cp > cbuf && cp[-1] == LF) {
cp--;
if (cp > cbuf && cp[-1] == CR)
cp--;
if (cp > cbuf && cp[-1] == '\\') {
cp--;
- if (!(cp > cbuf && cp[-1] == '\\')) {
- /*
- * line continuation requested -
- * then remove backslash and continue
- */
- cbufsize -= (cp-cbuf);
- cbuf = cp;
- continue;
- }
- else {
- /*
- * no real continuation because escaped -
- * then just remove escape character
- */
- for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
- cp[0] = cp[1];
- }
+ /*
+ * line continuation requested -
+ * then remove backslash and continue
+ */
+ cbufsize -= (cp-cbuf);
+ cbuf = cp;
+ continue;
}
}
+ else if (cp - buf >= bufsize - 1) {
+ return APR_ENOSPC;
+ }
break;
}
-
- /*
- * Leading and trailing white space is eliminated completely
- */
- src = buf;
- while (apr_isspace(*src))
- ++src;
- /* blast trailing whitespace */
- dst = &src[strlen(src)];
- while (--dst >= src && apr_isspace(*dst))
- *dst = '\0';
- /* Zap leading whitespace by shifting */
- if (src != buf)
- for (dst = buf; (*dst++ = *src++) != '\0'; )
- ;
-
-#ifdef DEBUG_CFG_LINES
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf);
-#endif
- return 0;
} else {
/* No "get string" function defined; read character by character */
- register int c;
- register size_t i = 0;
-
- buf[0] = '\0';
- /* skip leading whitespace */
- do {
- c = cfp->getch(cfp->param);
- } while (c == '\t' || c == ' ');
-
- if (c == EOF)
- return 1;
+ size_t i = 0;
- if(bufsize < 2) {
+ if (bufsize < 2) {
/* too small, assume caller is crazy */
- return 1;
+ return APR_EINVAL;
}
+ buf[0] = '\0';
while (1) {
- if ((c == '\t') || (c == ' ')) {
- buf[i++] = ' ';
- while ((c == '\t') || (c == ' '))
- c = cfp->getch(cfp->param);
- }
- if (c == CR) {
- /* silently ignore CR (_assume_ that a LF follows) */
- c = cfp->getch(cfp->param);
+ char c;
+ rc = cfp->getch(&c, cfp->param);
+ if (rc == APR_EOF) {
+ if (i > 0)
+ break;
+ else
+ return APR_EOF;
}
+ if (rc != APR_SUCCESS)
+ return rc;
if (c == LF) {
- /* increase line number and return on LF */
++cfp->line_number;
- }
- if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
- /*
- * check for line continuation
- */
+ /* check for line continuation */
if (i > 0 && buf[i-1] == '\\') {
i--;
- if (!(i > 0 && buf[i-1] == '\\')) {
- /* line is continued */
- c = cfp->getch(cfp->param);
- continue;
- }
- /* else nothing needs be done because
- * then the backslash is escaped and
- * we just strip to a single one
- */
+ continue;
+ }
+ else {
+ break;
}
- /* blast trailing whitespace */
- while (i > 0 && apr_isspace(buf[i - 1]))
- --i;
- buf[i] = '\0';
-#ifdef DEBUG_CFG_LINES
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "Read config: %s", buf);
-#endif
- return 0;
+ }
+ else if (i >= bufsize - 2) {
+ return APR_ENOSPC;
}
buf[i] = c;
++i;
- c = cfp->getch(cfp->param);
}
+ buf[i] = '\0';
}
+
+ /*
+ * Leading and trailing white space is eliminated completely
+ */
+ src = buf;
+ while (apr_isspace(*src))
+ ++src;
+ /* blast trailing whitespace */
+ dst = &src[strlen(src)];
+ while (--dst >= src && apr_isspace(*dst))
+ *dst = '\0';
+ /* Zap leading whitespace by shifting */
+ if (src != buf)
+ memmove(buf, src, dst - src + 2);
+
+#ifdef DEBUG_CFG_LINES
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Read config: '%s'", buf);
+#endif
+ return APR_SUCCESS;
}
/* Size an HTTP header field list item, as separated by a comma.
AP_DECLARE(int) ap_unescape_url(char *url)
{
/* Traditional */
-#ifdef CASE_BLIND_FILESYSTEM
- return unescape_url(url, "/\\", NULL);
-#else
- return unescape_url(url, "/", NULL);
-#endif
+ return unescape_url(url, SLASHES, NULL);
}
-AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
+AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes)
{
/* AllowEncodedSlashes (corrected) */
- return unescape_url(url, NULL, "/");
+ if (decode_slashes) {
+ /* no chars reserved */
+ return unescape_url(url, NULL, NULL);
+ } else {
+ /* reserve (do not decode) encoded slashes */
+ return unescape_url(url, NULL, SLASHES);
+ }
}
#ifdef NEW_APIS
/* IFDEF these out until they've been thought through.
char *estr;
const char *cls;
int has_body;
-
+
has_body = (!r->header_only
&& (r->kept_body
|| apr_table_get(r->headers_in, "Transfer-Encoding")
);
return has_body;
}
+
+AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
+{
+ void **ptr = (void **)data_;
+ *ptr = NULL;
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
+
+ for ( ; *src; src++, dest++)
+ {
+ if (!apr_isprint(*src))
+ *dest = 'x';
+ else if (!apr_isalnum(*src))
+ *dest = '_';
+ else
+ *dest = (char)*src;
+ }
+ *dest = '\0';
+ return APR_SUCCESS;
+
+}
+
+AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
+ const char **dest)
+{
+ char *new = apr_palloc(p, strlen(src)+1);
+ if (!new)
+ return APR_ENOMEM;
+ *dest = new;
+ return ap_str2_alnum(src, new);
+}
+
+/**
+ * Read the body and parse any form found, which must be of the
+ * type application/x-www-form-urlencoded.
+ *
+ * Name/value pairs are returned in an array, with the names as
+ * strings with a maximum length of HUGE_STRING_LEN, and the
+ * values as bucket brigades. This allows values to be arbitrarily
+ * large.
+ *
+ * All url-encoding is removed from both the names and the values
+ * on the fly. The names are interpreted as strings, while the
+ * values are interpreted as blocks of binary data, that may
+ * contain the 0 character.
+ *
+ * In order to ensure that resource limits are not exceeded, a
+ * maximum size must be provided. If the sum of the lengths of
+ * the names and the values exceed this size, this function
+ * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
+ *
+ * An optional number of parameters can be provided, if the number
+ * of parameters provided exceeds this amount, this function will
+ * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
+ * no limit is imposed, and the number of parameters is in turn
+ * constrained by the size parameter above.
+ *
+ * This function honours any kept_body configuration, and the
+ * original raw request body will be saved to the kept_body brigade
+ * if so configured, just as ap_discard_request_body does.
+ *
+ * NOTE: File upload is not yet supported, but can be without change
+ * to the function call.
+ */
+
+/* form parsing stuff */
+typedef enum {
+ FORM_NORMAL,
+ FORM_AMP,
+ FORM_NAME,
+ FORM_VALUE,
+ FORM_PERCENTA,
+ FORM_PERCENTB,
+ FORM_ABORT
+} ap_form_type_t;
+
+AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
+ apr_array_header_t **ptr,
+ apr_size_t num, apr_size_t usize)
+{
+ apr_bucket_brigade *bb = NULL;
+ int seen_eos = 0;
+ char buffer[HUGE_STRING_LEN + 1];
+ const char *ct;
+ apr_size_t offset = 0;
+ apr_ssize_t size;
+ ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
+ ap_form_pair_t *pair = NULL;
+ apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
+
+ char hi = 0;
+ char low = 0;
+
+ *ptr = pairs;
+
+ /* sanity check - we only support forms for now */
+ ct = apr_table_get(r->headers_in, "Content-Type");
+ if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
+ return ap_discard_request_body(r);
+ }
+
+ if (usize > APR_SIZE_MAX >> 1)
+ size = APR_SIZE_MAX >> 1;
+ else
+ size = usize;
+
+ if (!f) {
+ f = r->input_filters;
+ }
+
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ do {
+ apr_bucket *bucket = NULL, *last = NULL;
+
+ int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
+ APR_BLOCK_READ, HUGE_STRING_LEN);
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(bb);
+ return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
+ }
+
+ for (bucket = APR_BRIGADE_FIRST(bb);
+ bucket != APR_BRIGADE_SENTINEL(bb);
+ last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
+ const char *data;
+ apr_size_t len, slide;
+
+ if (last) {
+ apr_bucket_delete(last);
+ }
+ if (APR_BUCKET_IS_EOS(bucket)) {
+ seen_eos = 1;
+ break;
+ }
+ if (bucket->length == 0) {
+ continue;
+ }
+
+ rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(bb);
+ return HTTP_BAD_REQUEST;
+ }
+
+ slide = len;
+ while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
+ char c = *data++;
+ if ('+' == c) {
+ c = ' ';
+ }
+ else if ('&' == c) {
+ state = FORM_AMP;
+ }
+ if ('%' == c) {
+ percent = FORM_PERCENTA;
+ continue;
+ }
+ if (FORM_PERCENTA == percent) {
+ if (c >= 'a') {
+ hi = c - 'a' + 10;
+ }
+ else if (c >= 'A') {
+ hi = c - 'A' + 10;
+ }
+ else if (c >= '0') {
+ hi = c - '0';
+ }
+ hi = hi << 4;
+ percent = FORM_PERCENTB;
+ continue;
+ }
+ if (FORM_PERCENTB == percent) {
+ if (c >= 'a') {
+ low = c - 'a' + 10;
+ }
+ else if (c >= 'A') {
+ low = c - 'A' + 10;
+ }
+ else if (c >= '0') {
+ low = c - '0';
+ }
+ c = low | hi;
+ percent = FORM_NORMAL;
+ }
+ switch (state) {
+ case FORM_AMP:
+ if (pair) {
+ const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+ apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pair->value, b);
+ }
+ state = FORM_NAME;
+ pair = NULL;
+ offset = 0;
+ num--;
+ break;
+ case FORM_NAME:
+ if (offset < HUGE_STRING_LEN) {
+ if ('=' == c) {
+ buffer[offset] = 0;
+ offset = 0;
+ pair = (ap_form_pair_t *) apr_array_push(pairs);
+ pair->name = apr_pstrdup(r->pool, buffer);
+ pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ state = FORM_VALUE;
+ }
+ else {
+ buffer[offset++] = c;
+ size--;
+ }
+ }
+ else {
+ state = FORM_ABORT;
+ }
+ break;
+ case FORM_VALUE:
+ if (offset >= HUGE_STRING_LEN) {
+ const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+ apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pair->value, b);
+ offset = 0;
+ }
+ buffer[offset++] = c;
+ size--;
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+
+ apr_brigade_cleanup(bb);
+ } while (!seen_eos);
+
+ if (FORM_ABORT == state || size < 0 || num == 0) {
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+ else if (FORM_VALUE == state && pair && offset > 0) {
+ const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+ apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pair->value, b);
+ }
+
+ return OK;
+
+}