joes 2003/04/08 20:25:09
Added: src apreq_parsers.c apreq_parsers.h Log: Add apreq_parsers.* Revision Changes Path 1.1 httpd-apreq-2/src/apreq_parsers.c Index: apreq_parsers.c =================================================================== /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * Portions of this software are based upon public domain software * originally written at the National Center for Supercomputing Applications, * University of Illinois, Urbana-Champaign. */ #include "apreq_parsers.h" #include "apreq_params.h" #include "apreq_env.h" #ifndef MAX #define MAX(A,B) ( (A) > (B) ? (A) : (B) ) #endif #ifndef MIN #define MIN(A,B) ( (A) < (B) ? (A) : (B) ) #endif #ifndef CRLF #define CRLF "\015\012" #endif APREQ_DECLARE(apreq_parser_t *) apreq_make_parser( apr_pool_t *p, const char *enctype, APREQ_PARSER(*parser), void *out) { apreq_parser_t *p = apr_palloc(pool, APREQ_CTX_MAXSIZE + sizeof *p); p->v.name = enctype; p->v.size = 0; apreq_parser_ctx(p) = NULL; p->v.status = APR_SUCCESS; p->parser = parser; p->ext = NULL; p->out = out; return p; } APREQ_DECLARE(apr_status_t) apreq_register_parser(apreq_request_t *req, const char *enctype, APREQ_PARSER(*parser), void *out) { apreq_parser_t *p = apreq_make_parser(req->pool, enctype, parser, out); return apreq_env_add_parser(req->ctx, &p->v); } static apr_status_t split_urlword(apr_pool_t *p, apreq_table_t *t, apr_bucket_brigade *bb, const apr_size_t nlen, const apr_size_t vlen) { apreq_param_t *param = apr_palloc(p, nlen + vlen + 1 + sizeof *param); apr_size_t total, off; const apr_size_t glen = 1; apreq_value_t *v = ¶m->v; param->bb = NULL; /* XXX: could use to retain original encoded data */ param->info = NULL; param->charset = UTF_8; param->language = NULL; v->name = v->data + vlen; off = 0; total = 0; while (total < nlen) { apr_size_t dlen; char *data; apr_bucket_t *f = APR_BRIGADE_FIRST(bb); apr_status_t s = apr_bucket_read(f, &data, &dlen, APR_BLOCK_READ); apr_ssize_t rv; if ( s != APR_SUCCESS ) return s; if (dlen > nlen - total) { apr_bucket_split(f, nlen - total); dlen = nlen - total; } rv = apreq_decode((char *)v->name + off, data, dlen); if (rv < 0) { return APR_BADARG; } total += dlen; off += rv; apr_bucket_delete(f); } (char *)v->name[off] = 0; /* skip gap */ off = 0; while (off < glen) { apr_size_t dlen; char *data; apr_bucket_t *f = APR_BRIGADE_FIRST(bb); apr_status_t s = apr_bucket_read(f, &data, &dlen, APR_BLOCK_READ); if ( s != APR_SUCCESS ) return s; if (dlen > glen - off) { apr_bucket_split(f, glen - off); dlen = glen - off; } off += dlen; apr_bucket_delete(f); } off = 0; total = 0; while (total < vlen) { apr_size_t dlen; char *data; apr_bucket_t *f = APR_BRIGADE_FIRST(bb); apr_status_t s = apr_bucket_read(f, &data, &dlen, APR_BLOCK_READ); apr_ssize_t rv; if ( s != APR_SUCCESS ) return s; if (dlen > vlen - off) { apr_bucket_split(f, vlen - total); dlen = vlen - total; } rv = apreq_decode(v->data + off, data, dlen); if (rv < 0) { return APR_BADCH; } total += dlen; off += rv; apr_bucket_delete(f); } v->data[off] = 0; v->size = off; v->status = APR_SUCCESS; return apreq_table_add(t, v); } APREQ_DECLARE(apr_status_t) apreq_parse_urlencoded(apr_pool_t *p, apr_bucket_brigade *bb, apreq_parser_t *parser) { apreq_request_t *req = (apreq_request_t *)parser->out; apreq_table_t *t = req->body; apr_ssize_t nlen, glen, vlen; apr_bucket *e; /* use parser->v.status to maintain state */ #define URL_NAME 0 #define URL_VALUE 1 parse_url_brigade: /* parse the brigade for CRLF_CRLF-terminated header block, * each time starting from the front of the brigade. */ parser->v.status = URL_NAME; for (e = APR_BUCKET_FIRST(bb), nlen = vlen = 0; e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { apr_size_t off = 0, dlen; const char *name, *data; apr_status_t s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); if (APR_BUCKET_IS_EOS(e)) return nlen == 0 ? APR_SUCCESS : split_urlword(p, t, bb, nlen, vlen); if ( s != APR_SUCCESS ) return s; parse_url_bucket: switch (parser->v.status) { case URL_NAME: while (off < dlen) { switch (data[off++]) { case '=': parser->v.status = URL_VALUE; goto parse_url_bucket; default: ++nlen; } } break; case URL_VALUE: while (off < dlen) { switch (data[off++]) { case '&': case ';': s = split(urlword p, t, bb, nlen, vlen + 1); if (s != APR_SUCCESS) return s; goto parse_url_brigade; default: ++vlen; } } break; } } return APR_INCOMPLETE; } static apr_status_t split_header(apr_pool_t *p, apreq_table_t *t, apr_bucket_brigade *bb, const apr_size_t nlen, const apr_size_t glen, const apr_size_t vlen) { apreq_value_t *v = apr_palloc(p, nlen + vlen + sizeof *v); apr_size_t off; v->name = v->data + vlen; /* copy name */ off = 0; while (off < nlen) { apr_size_t dlen; char *data; apr_bucket_t *f = APR_BRIGADE_FIRST(bb); apr_status_t s = apr_bucket_read(f, &data, &dlen, APR_BLOCK_READ); if ( s != APR_SUCCESS ) return s; if (dlen > nlen - off) { apr_bucket_split(f, nlen - off); dlen = nlen - off; } memcpy((char *)v->name + off, data, dlen); off += dlen; apr_bucket_delete(f); } /* skip gap */ off = 0; while (off < glen) { apr_size_t dlen; char *data; apr_bucket_t *f = APR_BRIGADE_FIRST(bb); apr_status_t s = apr_bucket_read(f, &data, &dlen, APR_BLOCK_READ); if ( s != APR_SUCCESS ) return s; if (dlen > glen - off) { apr_bucket_split(f, glen - off); dlen = glen - off; } off += dlen; apr_bucket_delete(f); } /* copy value */ off = 0; while (off < vlen) { apr_size_t dlen; char *data; apr_bucket_t *f = APR_BRIGADE_FIRST(bb); apr_status_t s = apr_bucket_read(f, &data, &dlen, APR_BLOCK_READ); if ( s != APR_SUCCESS ) return s; if (dlen > vlen - off) { apr_bucket_split(f, vlen - off); dlen = vlen - off; } memcpy(v->data + off, data, dlen); off += dlen; apr_bucket_delete(f); } v->status = APR_SUCCESS; (char *)v->name[nlen] = 0; /* remove trailing (CR)LF from value */ v->size = vlen - 1; if ( v->size > 0 && v->data[v->size-1] == '\r') v->size--; v->data[v->size] = 0; return apreq_table_add(t, v); } APREQ_DECLARE(apr_status_t) apreq_parse_headers(apr_pool_t *p, apr_bucket_brigade *bb, apreq_parser_t *parser) { apreq_table_t *t = (apreq_table_t *) parser->out; apr_ssize_t nlen, glen, vlen; apr_bucket *e; /* use parser->v.status to maintain state */ #define HDR_NAME 0 #define HDR_GAP 1 #define HDR_VALUE 2 #define HDR_NEWLINE 3 #define HDR_CONTINUE 4 parse_hdr_brigade: /* parse the brigade for CRLF_CRLF-terminated header block, * each time starting from the front of the brigade. */ parser->v.status = HDR_NAME; for (e = APR_BUCKET_FIRST(bb), nlen = glen = vlen = 0, e != APR_BUCKET_IS_EOS(e) && e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { apr_size_t off = 0, dlen; const char *name, *data; apr_status_t s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); if ( s != APR_SUCCESS ) return s; parse_hdr_bucket: /* gap nlen = 13 * vvv glen = 3 * Sample-Header: grape vlen = 5 * ^^^^^^^^^^^^^ ^^^^^ * name value */ switch (parser->v.status) { case HDR_NAME: while (off < dlen) { switch (data[off++]) { case '\n': /* No more headers: split, shift & bail out */ if (off < dlen) apr_bucket_split(e, off); e = APR_BUCKET_NEXT(e); do apr_bucket_delete( APR_BUCKET_FIRST(bb) ); while (e != APR_BUCKET_FIRST(bb)); return APR_SUCCESS; case ':': ++glen; ++off; parser->v.status = HDR_GAP; goto parse_hdr_bucket; default: ++nlen; ++off; } } break; case HDR_GAP: while (off < dlen) { switch (data[off++]) { case ' ': case '\t': ++glen; break; case '\n': parser->v.status = HDR_NEWLINE; goto parse_hdr_bucket; default: parser->v.status = HDR_VALUE; ++vlen; goto parse_hdr_bucket; } } break; case HDR_VALUE: while (off < dlen) { if (data[off++] != '\n') ++vlen; else { --vlen; parser->v.status = HDR_NEWLINE; goto parse_hdr_bucket; } } break; case HDR_NEWLINE: if (off == dlen) break; else { switch (data[off]) { case ' ': case '\t': parser->v.status = HDR_CONTINUE; ++off; vlen += 2; break; default: /* can parse brigade now */ if (off > 0) { apr_split_bucket(e, off - 1); e = APR_BUCKET_NEXT(e); } s = split_header(p, t, bb, nlen, glen, vlen); if (s != APR_SUCCESS) return s; goto parse_hdr_brigade; } /* cases ' ', '\t' fall through to HDR_CONTINUE */ } case HDR_CONTINUE: while (off < dlen) { switch (data[off++]) { case ' ': case '\t': ++vlen; break; case '\n': parser->v.status = HDR_NEWLINE; goto parse_hdr_bucket; default: parser->v.status = HDR_VALUE; ++vlen; goto parse_hdr_bucket; } } break; } } return APR_INCOMPLETE; } struct mfd_ctx { apreq_table_t *t; apr_bucket_brigade *bb; char bdry[1]; }; static apr_status_t split_on_bdry(apr_pool_t *pool, apr_bucket_brigade *out, apr_bucket_brigade *in, char *bdry, apreq_parser_t *parser) { apr_bucket_t *e = APR_BRIGADE_FIRST(in); apr_size_t blen = strlen(bdry); while ( e != APR_BRIGADE_SENTINEL(in) ) { apr_ssize_t *idx; apr_size_t len; char *buf; apr_status_t s; if (APR_BUCKET_IS_EOS(e)) return APR_EOS; s = apr_bucket_read(e, &buf, &len); if (s != APR_SUCCESS) return s; if (len == 0) { e = APR_BUCKET_NEXT(e); continue; } if (strncmp(bdry + off, buf, MIN(len, blen - off)) == 0) { if ( len >= blen - off ) { /* complete match */ apr_bucket_split(e, blen - off); e = APR_BUCKET_NEXT(e); do apr_bucket_delete( APR_BRIGADE_FIRST(in) ); while (APR_BRIGADE_FIRST(in) != e); return APR_SUCCESS; } /* partial match */ off += len; e = APR_BUCKET_NEXT(e); continue; } else if (off > 0) { /* prior (partial) strncmp failed, restart */ apr_bucket_t *f; do { f = APR_BRIGADE_FIRST(in); APR_BUCKET_REMOVE(f); APR_BRIGADE_INSERT_TAIL(out, f); } while (e != f); off = 0; } idx = apreq_index(buf, len, bdry, blen); if (idx > 0) apr_bucket_split(e, idx); APR_BUCKET_REMOVE(e); APR_BRIGADE_INSERT_TAIL(out, e); e = APR_BRIGADE_FIRST(e); } return APR_INCOMPLETE; } APREQ_DECLARE(apr_status_t) apreq_parse_multipart(apr_pool_t *pool, apr_bucket_brigade *bb, apreq_parser_t *parser) { apr_bucket *e; apreq_request_t *req = (apreq_request_t *)parser->out; struct mfd_ctx *ctx = (struct mfd_ctx *) apreq_parser_ctx(parser); #define MAX_BLEN 100 #define MFD_HEADER 0 #define MFD_PARAM 1 #define MFD_UPLOAD 2 #define MFD_ERROR -1 if (parser->v.size == 0) { apr_ssize_t total = 0; char blen = 0; ctx->bdry[blen++] = '\r'; ctx->bdry[blen++] = '\n'; /* parse first line for mfd boundary */ while (1) { apr_size_t dlen; char *data, *newline; apr_bucket_t *f = APR_BRIGADE_FIRST(bb); apr_status_t s; if (f == APR_BRIGADE_SENTINEL(bb)) return APR_EAGAIN; if (APR_BUCKET_IS_EOS(f)) return APR_EOS; s = apr_bucket_read(f, &data, &dlen, APR_BLOCK_READ); if (s != APR_SUCCESS) return s; newline = memchr(data, '\n', dlen); if (newline != NULL) { if (newline + 1 < data + dlen) apr_bucket_split(f, newline + 1 - data); else dlen = newline - data; if (blen + dlen >= MAX_BLEN) return APR_BADARG; memcpy(ctx->bdry + blen, data, dlen); apr_bucket_delete(f); blen += dlen; break; } if (blen + dlen >= MAX_BLEN) return APR_BADARG; memcpy(ctx->bdry + blen, data, dlen); apr_bucket_delete(f); blen += dlen; } blen -= 2; /* ignore trailing CRLF */ ctx->bdry[blen] = 0; parser->v.size = blen + sizeof *ctx; parser->v.status = MFD_HEADER; } switch (parser->v.status) { case MFD_HEADER: { apreq_parser_t par = {0}; apr_status_t s; char *cd; if (ctx->t == NULL) ctx->t = apreq_make_table(pool, APREQ_NELTS); par.out = (void *)ctx->t; s = apreq_parse_headers(pool, bb, &par); if (s != APR_SUCCESS) return s; parser->v.status = MFD_VALUE; ctx->bb = apr_brigade_create(pool, apr_bucket_alloc_create(pool)); /* parse Content-Disposition header to determine value type */ cd = apreq_table_get(ctx->t, "Content-Disposition"); /* fall thru */ } case MFD_PARAM: case MFD_UPLOAD: { apr_status_t s = split_on_bdry(pool, ctx->bb, bb, ctx->bdry, parser); if (s != APR_SUCCESS) return s; if (parser->v.status == MFD_PARAM) { apr_size_t len; apreq_param_t *param; apreq_value_t *v; apr_status_t s; s = apr_brigade_length(ctx->bb, 1, &len); if (s != APR_SUCCESS) { parser->v.status = MFD_ERROR; return s; } param = apr_palloc(pool, len + sizeof *param); v = ¶m->v; param->charset = UTF_8; param->language = NULL; param->info = ctx->t; param->bb = ctx->bb; v->size = len; v->status = apr_brigade_flatten(ctx->bb, v->data, &v->size); apreq_table_add(req->body, v); } parser->v.status = MFD_VALUE; ctx->t = NULL; ctx->bb = NULL; } break; case MFD_ERROR: return APR_EGENERAL; } return APR_INCOMPLETE; } 1.1 httpd-apreq-2/src/apreq_parsers.h Index: apreq_parsers.h =================================================================== #ifndef APREQ_PARSERS_H #define APREQ_PARSERS_H #ifdef __cplusplus extern "C" { #endif /* parsers: single copy paradigm */ /*********************************************************** * API: apreq_parsers.c */ typedef struct apreq_parser_t apreq_parser_t; #define APREQ_PARSER(f) apr_status_t (f)(apr_pool_t *p, apr_bucket_brigade *bb, apreq_parser_t *parser) #define APREQ_CTX_MAXSIZE 128 struct apreq_parser_t { APREQ_PARSER (*parser); void *out; apreq_parser_t *ext; apreq_value_t v; /* maintains (internal) parser state */ }; #define apreq_value_to_parser(ptr) apreq_attr_to_type(apreq_parser_t, \ v, ptr) #define apreq_ctx_to_parser(ptr) apreq_value_to_parser(apreq_strtoval(ptr)) #define apreq_parser_hook(p) ((p)->next) #define apreq_parser_enctype(p) ((p)->v.name) #define apreq_parser_data(p) ((p)->v.data) #define apreq_parse(pool, bb, p) (p)->parser(pool, bb, p) APREQ_DECLARE(apr_status_t) apreq_parse_headers(apr_pool_t *p, apr_bucket_brigade *bb, apreq_parser_t *parser); APREQ_DECLARE(apreq_parser_t *) apreq_make_parser(const char *enctype, APREQ_PARSER(*parser), apreq_parser_t *ext); APREQ_DECLARE(apr_status_t) apreq_add_parser(apreq_table_t *t, apreq_parser_t *parser); #define apreq_add_parser(t,p) apreq_table_add(t, &(p)->v) APREQ_DECLARE(apr_status_t) apreq_copy_parser(apr_pool_t *p, const apreq_value_t *v); APREQ_DECLARE(apr_status_t) apreq_merge_parsers(apr_pool_t *p, const apr_array_header_t *a); #ifdef __cplusplus } #endif #endif /*APREQ_PARSERS_H*/
