joes 2004/09/11 23:16:43
Modified: . CHANGES
src apreq_parsers.c apreq_version.h
t parsers.c
Log:
Add support for multipart/mixed file uploads.
Revision Changes Path
1.67 +2 -0 httpd-apreq-2/CHANGES
Index: CHANGES
===================================================================
RCS file: /home/cvs/httpd-apreq-2/CHANGES,v
retrieving revision 1.66
retrieving revision 1.67
diff -u -r1.66 -r1.67
--- CHANGES 10 Sep 2004 23:54:08 -0000 1.66
+++ CHANGES 12 Sep 2004 06:16:43 -0000 1.67
@@ -6,6 +6,7 @@
- C API [joes]
+ Support "multipart/mixed" file uploads.
Support XForms' "multipart/related" enctype.
- C API [joes]
@@ -13,6 +14,7 @@
hook around APR's expat-based apr_xml_parser. Add a generic
parser apreq_parse_generic() to parse arbitrary enctypes using
the hook API.
+
@section v2_04_dev Changes with libapreq2-2.04-dev (released August 30, 2004)
1.64 +156 -71 httpd-apreq-2/src/apreq_parsers.c
Index: apreq_parsers.c
===================================================================
RCS file: /home/cvs/httpd-apreq-2/src/apreq_parsers.c,v
retrieving revision 1.63
retrieving revision 1.64
diff -u -r1.63 -r1.64
--- apreq_parsers.c 10 Sep 2004 23:54:08 -0000 1.63
+++ apreq_parsers.c 12 Sep 2004 06:16:43 -0000 1.64
@@ -50,7 +50,7 @@
void *ctx)
{
apreq_parser_t *p = apr_palloc(pool, sizeof *p);
- p->enctype = apr_pstrdup(pool,enctype);
+ p->enctype = enctype;
p->parser = parser;
p->hook = hook;
p->ctx = ctx;
@@ -618,11 +618,11 @@
}
struct mfd_ctx {
- void *hook_data;
apr_table_t *info;
apr_bucket_brigade *in;
apr_bucket_brigade *bb;
apreq_parser_t *hdr_parser;
+ apreq_parser_t *mix_parser;
const apr_strmatch_pattern *pattern;
char *bdry;
enum {
@@ -632,11 +632,13 @@
MFD_POST_HEADER,
MFD_PARAM,
MFD_UPLOAD,
+ MFD_MIXED,
MFD_COMPLETE,
MFD_ERROR
} status;
- apr_bucket *eos;
- apreq_param_t *upload;
+ apr_bucket *eos;
+ const char *param_name;
+ apreq_param_t *upload;
};
@@ -800,6 +802,56 @@
return s;
}
+static struct mfd_ctx *create_multipart_context(void *env,
+ char *enctype)
+{
+ apr_status_t s;
+ char *ct;
+ apr_size_t blen;
+ apr_pool_t *pool = apreq_env_pool(env);
+ apr_bucket_alloc_t *bucket_alloc = apr_bucket_alloc_create(pool);
+ struct mfd_ctx *ctx = apr_palloc(pool, sizeof *ctx);
+
+ ctx->status = MFD_INIT;
+
+ ct = strchr(enctype, ';');
+ if (ct == NULL) {
+ ctx->status = MFD_ERROR;
+ apreq_log(APREQ_ERROR APR_EGENERAL, env,
+ "mfd parser cannot find required "
+ "semicolon in supplied Content-Type header");
+ return NULL;
+ }
+ *ct++ = 0;
+
+ s = apreq_header_attribute(ct, "boundary", 8,
+ (const char **)&ctx->bdry, &blen);
+ if (s != APR_SUCCESS) {
+ ctx->status = MFD_ERROR;
+ apreq_log(APREQ_ERROR APR_EGENERAL, env,
+ "mfd parser cannot find boundary "
+ "attribute in supplied Content-Type header");
+ return NULL;
+ }
+ ctx->bdry[blen] = 0;
+
+ *--ctx->bdry = '-';
+ *--ctx->bdry = '-';
+ *--ctx->bdry = '\n';
+ *--ctx->bdry = '\r';
+
+ ctx->pattern = apr_strmatch_precompile(pool,ctx->bdry,1);
+ ctx->hdr_parser = apreq_make_parser(pool, "", apreq_parse_headers,
+ NULL,NULL);
+ ctx->info = NULL;
+ ctx->bb = apr_brigade_create(pool, bucket_alloc);
+ ctx->in = apr_brigade_create(pool, bucket_alloc);
+ ctx->eos = apr_bucket_eos_create(bucket_alloc);
+ ctx->mix_parser = NULL;
+ ctx->param_name = NULL;
+ ctx->upload = NULL;
+ return ctx;
+}
APREQ_DECLARE_PARSER(apreq_parse_multipart)
{
@@ -808,48 +860,14 @@
apr_status_t s;
if (ctx == NULL) {
- char *ct;
- apr_size_t blen;
- apr_bucket_alloc_t *bucket_alloc = apr_bucket_alloc_create(pool);
-
- ctx = apr_pcalloc(pool, sizeof *ctx);
- parser->ctx = ctx;
- ctx->status = MFD_INIT;
-
- ct = strchr(parser->enctype, ';');
- if (ct == NULL) {
- ctx->status = MFD_ERROR;
- apreq_log(APREQ_ERROR APR_EGENERAL, env,
- "mfd parser cannot find required "
- "semicolon in Content-Type header");
+ ctx = create_multipart_context(env, apr_pstrdup(pool,
+ parser->enctype));
+ if (ctx == NULL)
return APR_EGENERAL;
- }
- *ct++ = 0;
-
- s = apreq_header_attribute(ct, "boundary", 8,
- (const char **)&ctx->bdry, &blen);
- if (s != APR_SUCCESS) {
- ctx->status = MFD_ERROR;
- apreq_log(APREQ_ERROR APR_EGENERAL, env,
- "mfd parser cannot find boundary "
- "attribute in Content-Type header");
- return s;
- }
- ctx->bdry[blen] = 0;
-
- *--ctx->bdry = '-';
- *--ctx->bdry = '-';
- *--ctx->bdry = '\n';
- *--ctx->bdry = '\r';
-
- ctx->pattern = apr_strmatch_precompile(pool,ctx->bdry,1);
- ctx->hdr_parser = apreq_make_parser(pool, "", apreq_parse_headers,
- NULL,NULL);
- ctx->info = NULL;
- ctx->bb = apr_brigade_create(pool, bucket_alloc);
- ctx->in = apr_brigade_create(pool, bucket_alloc);
- ctx->eos = apr_bucket_eos_create(bucket_alloc);
+ ctx->mix_parser = apreq_make_parser(pool, "", apreq_parse_multipart,
+ NULL, parser->hook);
+ parser->ctx = ctx;
}
else if (ctx->status == MFD_COMPLETE) {
apreq_log(APREQ_DEBUG APR_SUCCESS, env,
@@ -965,16 +983,79 @@
cd = apr_table_get(ctx->info, "Content-Disposition");
if (cd != NULL) {
- s = apreq_header_attribute(cd, "name", 4, &name, &nlen);
- if (s != APR_SUCCESS) {
- ctx->status = MFD_ERROR;
- return APR_EGENERAL;
- }
+ if (ctx->mix_parser != NULL) {
+ /* multipart/form-data */
+
+ s = apreq_header_attribute(cd, "name", 4, &name, &nlen);
+ if (s != APR_SUCCESS) {
+ ctx->status = MFD_ERROR;
+ return APR_EGENERAL;
+ }
- s = apreq_header_attribute(cd, "filename", 8, &filename,
&flen);
+ s = apreq_header_attribute(cd, "filename",
+ 8, &filename, &flen);
+ if (s != APR_SUCCESS) {
+ const char *ct = apr_table_get(ctx->info,
+ "Content-Type");
+ if (ct != NULL
+ && strncmp(ct, "multipart/mixed", 15) == 0)
+ {
+ struct mfd_ctx *mix_ctx;
+ mix_ctx = create_multipart_context(env,
+ apr_pstrdup(pool, ct));
+ if (mix_ctx == NULL) {
+ ctx->status = MFD_ERROR;
+ return APR_EGENERAL;
+ }
+
+ mix_ctx->param_name = apr_pstrmemdup(pool,
+ name, nlen);
+ ctx->mix_parser->ctx = mix_ctx;
+ ctx->status = MFD_MIXED;
+ goto mfd_parse_brigade;
+ }
+ ctx->param_name = apr_pstrmemdup(pool, name, nlen);
+ ctx->status = MFD_PARAM;
+ }
+ else {
+ apreq_param_t *param;
+ param = apreq_make_param(pool, name, nlen,
+ filename, flen);
+ param->info = ctx->info;
+ param->bb = apr_brigade_create(pool,
+
ctx->bb->bucket_alloc);
+ ctx->upload = param;
+ ctx->status = MFD_UPLOAD;
+ goto mfd_parse_brigade;
+
+ }
+ }
+ else {
+ /* multipart/mixed */
+ s = apreq_header_attribute(cd, "filename",
+ 8, &filename, &flen);
+ if (s != APR_SUCCESS) {
+ ctx->status = MFD_PARAM;
+ }
+ else {
+ apreq_param_t *param;
+ name = ctx->param_name;
+ nlen = strlen(name);
+ param = apreq_make_param(pool, name, nlen,
+ filename, flen);
+ param->info = ctx->info;
+ param->bb = apr_brigade_create(pool,
+
ctx->bb->bucket_alloc);
+ ctx->upload = param;
+ ctx->status = MFD_UPLOAD;
+ goto mfd_parse_brigade;
+ }
+ }
}
else {
+ /* multipart/related */
+ apreq_param_t *param;
cd = apr_table_get(ctx->info, "Content-ID");
if (cd == NULL) {
ctx->status = MFD_ERROR;
@@ -984,26 +1065,16 @@
nlen = strlen(name);
filename = "";
flen = 0;
- s = APR_SUCCESS;
- }
-
- if (s != APR_SUCCESS) {
- name = apr_pstrmemdup(pool, name, nlen);
- e = apr_bucket_immortal_create(name, nlen,
- ctx->bb->bucket_alloc);
- APR_BRIGADE_INSERT_HEAD(ctx->bb,e);
- ctx->status = MFD_PARAM;
- }
- else {
- apreq_param_t *param = apreq_make_param(pool, name, nlen,
- filename, flen);
+ param = apreq_make_param(pool, name, nlen,
+ filename, flen);
param->info = ctx->info;
param->bb = apr_brigade_create(pool,
-
apr_bucket_alloc_create(pool));
+ ctx->bb->bucket_alloc);
ctx->upload = param;
ctx->status = MFD_UPLOAD;
goto mfd_parse_brigade;
}
+
}
/* fall through */
@@ -1011,10 +1082,8 @@
{
apreq_param_t *param;
apreq_value_t *v;
- const char *name;
apr_size_t len;
apr_off_t off;
- apr_bucket *e;
s = split_on_bdry(ctx->bb, ctx->in, ctx->pattern, ctx->bdry);
@@ -1026,10 +1095,6 @@
return s;
case APR_SUCCESS:
- e = APR_BRIGADE_FIRST(ctx->bb);
- apr_bucket_read(e, &name, &len, APR_BLOCK_READ);
- apr_bucket_delete(e);
-
s = apr_brigade_length(ctx->bb, 1, &off);
if (s != APR_SUCCESS) {
ctx->status = MFD_ERROR;
@@ -1041,12 +1106,13 @@
param->info = ctx->info;
v = ¶m->v;
- v->name = name;
+ v->name = ctx->param_name;
apr_brigade_flatten(ctx->bb, v->data, &len);
v->size = len;
v->data[v->size] = 0;
apr_table_addn(t, v->name, v->data);
ctx->status = MFD_NEXTLINE;
+ ctx->param_name = NULL;
apr_brigade_cleanup(ctx->bb);
goto mfd_parse_brigade;
@@ -1106,6 +1172,25 @@
}
break; /* not reached */
+
+
+ case MFD_MIXED:
+ {
+ s = APREQ_RUN_PARSER(ctx->mix_parser, env, t, ctx->in);
+ switch (s) {
+ case APR_SUCCESS:
+ ctx->status = MFD_INIT;
+ goto mfd_parse_brigade;
+ case APR_INCOMPLETE:
+ apr_brigade_cleanup(ctx->in);
+ return APR_INCOMPLETE;
+ default:
+ ctx->status = MFD_ERROR;
+ return s;
+ }
+
+ }
+ break; /* not reached */
default:
return APR_EGENERAL;
1.25 +1 -1 httpd-apreq-2/src/apreq_version.h
Index: apreq_version.h
===================================================================
RCS file: /home/cvs/httpd-apreq-2/src/apreq_version.h,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -r1.24 -r1.25
--- apreq_version.h 8 Aug 2004 18:42:06 -0000 1.24
+++ apreq_version.h 12 Sep 2004 06:16:43 -0000 1.25
@@ -61,7 +61,7 @@
#define APREQ_MINOR_VERSION 0
/** patch level */
-#define APREQ_PATCH_VERSION 20
+#define APREQ_PATCH_VERSION 21
/**
* This symbol is defined for internal, "development" copies of libapreq.
1.23 +83 -0 httpd-apreq-2/t/parsers.c
Index: parsers.c
===================================================================
RCS file: /home/cvs/httpd-apreq-2/t/parsers.c,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- parsers.c 10 Sep 2004 23:54:08 -0000 1.22
+++ parsers.c 12 Sep 2004 06:16:43 -0000 1.23
@@ -74,6 +74,29 @@
"...Binary data here..." CRLF
"--f93dcbA3--" CRLF;
+static char mix_data[] =
+"--AaB03x" CRLF
+"Content-Disposition: form-data; name=\"submit-name\"" CRLF CRLF
+"Larry" CRLF
+"--AaB03x" CRLF
+"Content-Disposition: form-data; name=\"files\"" CRLF
+"Content-Type: multipart/mixed; boundary=BbC04y" CRLF CRLF
+"--BbC04y" CRLF
+"Content-Disposition: file; filename=\"file1.txt\"" CRLF
+"Content-Type: text/plain" CRLF CRLF
+"... contents of file1.txt ..." CRLF
+"--BbC04y" CRLF
+"Content-Disposition: file; filename=\"file2.gif\"" CRLF
+"Content-Type: image/gif" CRLF
+"Content-Transfer-Encoding: binary" CRLF CRLF
+"...contents of file2.gif..." CRLF
+"--BbC04y--" CRLF
+"--AaB03x" CRLF
+"content-disposition: form-data; name=\"field1\"" CRLF
+"content-type: text/plain;charset=windows-1250" CRLF
+"content-transfer-encoding: quoted-printable" CRLF CRLF
+"Joe owes =80100." CRLF
+"--AaB03x--" CRLF;
extern apr_bucket_brigade *bb;
@@ -324,6 +347,65 @@
CuAssertStrNEquals(tc, data, val, vlen);
}
+typedef struct {
+ const char *key;
+ const char *val;
+} array_elt;
+
+
+static void parse_mixed(CuTest *tc)
+{
+ const char *val;
+ apr_size_t vlen;
+ apr_status_t rv;
+ apreq_param_t *param;
+ const apr_array_header_t *arr;
+ array_elt *elt;
+ apreq_request_t *req = apreq_request(APREQ_MFD_ENCTYPE
+ "; charset=\"iso-8859-1\"; boundary=\"AaB03x\"" ,"");
+ apr_bucket_brigade *bb = apr_brigade_create(p,
+ apr_bucket_alloc_create(p));
+ apr_bucket *e = apr_bucket_immortal_create(mix_data,
+ strlen(mix_data),
+ bb->bucket_alloc);
+
+ CuAssertPtrNotNull(tc, req);
+ APR_BRIGADE_INSERT_HEAD(bb, e);
+ APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc));
+
+ rv = apreq_parse_request(req,bb);
+ CuAssertIntEquals(tc, APR_SUCCESS, rv);
+
+ param = apreq_param(req, "submit-name");
+ CuAssertPtrNotNull(tc, param);
+ CuAssertStrEquals(tc, "Larry", param->v.data);
+
+ val = apr_table_get(req->body,"field1");
+ CuAssertStrEquals(tc, "Joe owes =80100.", val);
+
+ param = apreq_param(req, "files");
+ CuAssertPtrNotNull(tc, param);
+ CuAssertStrEquals(tc, "file1.txt", param->v.data);
+
+ CuAssertPtrNotNull(tc, param->bb);
+ apr_brigade_pflatten(param->bb, (char **)&val, &vlen, p);
+ CuAssertIntEquals(tc, strlen("... contents of file1.txt ..."), vlen);
+ CuAssertStrNEquals(tc, "... contents of file1.txt ...", val, vlen);
+
+ arr = apr_table_elts(req->body);
+ CuAssertIntEquals(tc, 4, arr->nelts);
+
+ elt = (array_elt *)&arr->elts[2 * arr->elt_size];
+ CuAssertStrEquals(tc, "files", elt->key);
+ CuAssertStrEquals(tc, "file2.gif", elt->val);
+
+ param = apreq_value_to_param(apreq_strtoval(elt->val));
+ CuAssertPtrNotNull(tc, param->bb);
+ apr_brigade_pflatten(param->bb, (char **)&val, &vlen, p);
+ CuAssertIntEquals(tc, strlen("...contents of file2.gif..."), vlen);
+ CuAssertStrNEquals(tc, "...contents of file2.gif...", val, vlen);
+
+}
CuSuite *testparser(void)
@@ -334,6 +416,7 @@
SUITE_ADD_TEST(suite, parse_disable_uploads);
SUITE_ADD_TEST(suite, parse_generic);
SUITE_ADD_TEST(suite, parse_related);
+ SUITE_ADD_TEST(suite, parse_mixed);
return suite;
}