/src/httpd/server/apreq_parser_multipart.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | ** contributor license agreements. See the NOTICE file distributed with |
4 | | ** this work for additional information regarding copyright ownership. |
5 | | ** The ASF licenses this file to You under the Apache License, Version 2.0 |
6 | | ** (the "License"); you may not use this file except in compliance with |
7 | | ** the License. You may obtain a copy of the License at |
8 | | ** |
9 | | ** http://www.apache.org/licenses/LICENSE-2.0 |
10 | | ** |
11 | | ** Unless required by applicable law or agreed to in writing, software |
12 | | ** distributed under the License is distributed on an "AS IS" BASIS, |
13 | | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | ** See the License for the specific language governing permissions and |
15 | | ** limitations under the License. |
16 | | */ |
17 | | |
18 | | #include "apreq_parser.h" |
19 | | #include "apreq_error.h" |
20 | | #include "apreq_util.h" |
21 | | #include "apr_strings.h" |
22 | | #include "apr_strmatch.h" |
23 | | |
24 | | #ifndef CRLF |
25 | 1.13k | #define CRLF "\015\012" |
26 | | #endif |
27 | | |
28 | 4.79k | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
29 | | |
30 | 898 | #define PARSER_STATUS_CHECK(PREFIX) do { \ |
31 | 898 | if (ctx->status == PREFIX##_ERROR) \ |
32 | 898 | return APREQ_ERROR_GENERAL; \ |
33 | 898 | else if (ctx->status == PREFIX##_COMPLETE) \ |
34 | 898 | return APR_SUCCESS; \ |
35 | 898 | else if (bb == NULL) \ |
36 | 898 | return APR_INCOMPLETE; \ |
37 | 898 | } while (0); |
38 | | |
39 | | /* maximum recursion level in the mfd parser */ |
40 | 95 | #define MAX_LEVEL 8 |
41 | | |
42 | | struct mfd_ctx { |
43 | | apr_table_t *info; |
44 | | apr_bucket_brigade *in; |
45 | | apr_bucket_brigade *bb; |
46 | | apreq_parser_t *hdr_parser; |
47 | | apreq_parser_t *next_parser; |
48 | | const apr_strmatch_pattern *pattern; |
49 | | char *bdry; |
50 | | enum { |
51 | | MFD_INIT, |
52 | | MFD_NEXTLINE, |
53 | | MFD_HEADER, |
54 | | MFD_POST_HEADER, |
55 | | MFD_PARAM, |
56 | | MFD_UPLOAD, |
57 | | MFD_MIXED, |
58 | | MFD_COMPLETE, |
59 | | MFD_ERROR |
60 | | } status; |
61 | | apr_bucket *eos; |
62 | | const char *param_name; |
63 | | apreq_param_t *upload; |
64 | | unsigned level; |
65 | | }; |
66 | | |
67 | | |
68 | | /********************* multipart/form-data *********************/ |
69 | | |
70 | | APR_INLINE |
71 | | static apr_status_t brigade_start_string(apr_bucket_brigade *bb, |
72 | | const char *start_string) |
73 | 808 | { |
74 | 808 | apr_bucket *e; |
75 | 808 | apr_size_t slen = strlen(start_string); |
76 | | |
77 | 831 | for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); |
78 | 808 | e = APR_BUCKET_NEXT(e)) |
79 | 821 | { |
80 | 821 | const char *buf; |
81 | 821 | apr_status_t s, bytes_to_check; |
82 | 821 | apr_size_t blen; |
83 | | |
84 | 821 | if (slen == 0) |
85 | 12 | return APR_SUCCESS; |
86 | | |
87 | 809 | if (APR_BUCKET_IS_EOS(e)) |
88 | 0 | return APR_EOF; |
89 | | |
90 | 809 | s = apr_bucket_read(e, &buf, &blen, APR_BLOCK_READ); |
91 | | |
92 | 809 | if (s != APR_SUCCESS) |
93 | 0 | return s; |
94 | | |
95 | 809 | if (blen == 0) |
96 | 0 | continue; |
97 | | |
98 | 809 | bytes_to_check = MIN(slen,blen); |
99 | | |
100 | 809 | if (strncmp(buf,start_string,bytes_to_check) != 0) |
101 | 786 | return APREQ_ERROR_GENERAL; |
102 | | |
103 | 23 | slen -= bytes_to_check; |
104 | 23 | start_string += bytes_to_check; |
105 | 23 | } |
106 | | |
107 | | /* slen > 0, so brigade isn't large enough yet */ |
108 | 10 | return APR_INCOMPLETE; |
109 | 808 | } |
110 | | |
111 | | |
112 | | static apr_status_t split_on_bdry(apr_bucket_brigade *out, |
113 | | apr_bucket_brigade *in, |
114 | | const apr_strmatch_pattern *pattern, |
115 | | const char *bdry) |
116 | 2.77k | { |
117 | 2.77k | apr_bucket *e = APR_BRIGADE_FIRST(in); |
118 | 2.77k | apr_size_t blen = strlen(bdry), off = 0; |
119 | | |
120 | 4.58k | while ( e != APR_BRIGADE_SENTINEL(in) ) { |
121 | 3.96k | apr_ssize_t idx; |
122 | 3.96k | apr_size_t len; |
123 | 3.96k | const char *buf; |
124 | 3.96k | apr_status_t s; |
125 | | |
126 | 3.96k | if (APR_BUCKET_IS_EOS(e)) |
127 | 0 | return APR_EOF; |
128 | | |
129 | 3.96k | s = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); |
130 | 3.96k | if (s != APR_SUCCESS) |
131 | 0 | return s; |
132 | | |
133 | 3.96k | if (len == 0) { |
134 | 1 | apr_bucket *f = e; |
135 | 1 | e = APR_BUCKET_NEXT(e); |
136 | 1 | apr_bucket_delete(f); |
137 | 1 | continue; |
138 | 1 | } |
139 | | |
140 | 3.98k | look_for_boundary_up_front: |
141 | 3.98k | if (strncmp(bdry + off, buf, MIN(len, blen - off)) == 0) { |
142 | 2.32k | if ( len >= blen - off ) { |
143 | | /* complete match */ |
144 | 2.16k | if (len > blen - off) |
145 | 2.11k | apr_bucket_split(e, blen - off); |
146 | 2.16k | e = APR_BUCKET_NEXT(e); |
147 | | |
148 | 2.17k | do { |
149 | 2.17k | apr_bucket *f = APR_BRIGADE_FIRST(in); |
150 | 2.17k | apr_bucket_delete(f); |
151 | 2.17k | } while (APR_BRIGADE_FIRST(in) != e); |
152 | | |
153 | 2.16k | return APR_SUCCESS; |
154 | 2.16k | } |
155 | | /* partial match */ |
156 | 158 | off += len; |
157 | 158 | e = APR_BUCKET_NEXT(e); |
158 | 158 | continue; |
159 | 2.32k | } |
160 | 1.66k | else if (off > 0) { |
161 | | /* prior (partial) strncmp failed, |
162 | | * so we can move previous buckets across |
163 | | * and retest buf against the full bdry. |
164 | | */ |
165 | | |
166 | | /* give hints to GCC by making the brigade volatile, otherwise the |
167 | | * loop below will end up being endless. See: |
168 | | * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=193740 |
169 | | */ |
170 | 18 | apr_bucket_brigade * volatile in_v = in; |
171 | | |
172 | 18 | do { |
173 | 18 | apr_bucket *f = APR_BRIGADE_FIRST(in_v); |
174 | 18 | APR_BUCKET_REMOVE(f); |
175 | 18 | APR_BRIGADE_INSERT_TAIL(out, f); |
176 | 18 | } while (e != APR_BRIGADE_FIRST(in_v)); |
177 | 18 | off = 0; |
178 | 18 | goto look_for_boundary_up_front; |
179 | 18 | } |
180 | | |
181 | 1.64k | if (pattern != NULL && len >= blen) { |
182 | 488 | const char *match = apr_strmatch(pattern, buf, len); |
183 | 488 | if (match != NULL) |
184 | 184 | idx = match - buf; |
185 | 304 | else { |
186 | 304 | idx = apreq_index(buf + len-blen, blen, bdry, blen, |
187 | 304 | APREQ_MATCH_PARTIAL); |
188 | 304 | if (idx >= 0) |
189 | 61 | idx += len-blen; |
190 | 304 | } |
191 | 488 | } |
192 | 1.15k | else |
193 | 1.15k | idx = apreq_index(buf, len, bdry, blen, APREQ_MATCH_PARTIAL); |
194 | | |
195 | | /* Theoretically idx should never be 0 here, because we |
196 | | * already tested the front of the brigade for a potential match. |
197 | | * However, it doesn't hurt to allow for the possibility, |
198 | | * since this will just start the whole loop over again. |
199 | | */ |
200 | 1.64k | if (idx >= 0) |
201 | 1.17k | apr_bucket_split(e, idx); |
202 | | |
203 | 1.64k | APR_BUCKET_REMOVE(e); |
204 | 1.64k | APR_BRIGADE_INSERT_TAIL(out, e); |
205 | 1.64k | e = APR_BRIGADE_FIRST(in); |
206 | 1.64k | } |
207 | | |
208 | 615 | return APR_INCOMPLETE; |
209 | 2.77k | } |
210 | | |
211 | | |
212 | | static |
213 | | struct mfd_ctx * create_multipart_context(const char *content_type, |
214 | | apr_pool_t *pool, |
215 | | apr_bucket_alloc_t *ba, |
216 | | apr_size_t brigade_limit, |
217 | | const char *temp_dir, |
218 | | unsigned level) |
219 | | |
220 | 1.03k | { |
221 | 1.03k | apr_status_t s; |
222 | 1.03k | apr_size_t blen; |
223 | 1.03k | struct mfd_ctx *ctx; |
224 | 1.03k | const char *attr; |
225 | 1.03k | char *buf; |
226 | | |
227 | 1.03k | attr = (content_type) ? strchr(content_type, ';') : NULL; |
228 | 1.03k | if (!attr) |
229 | 15 | return NULL; /* missing semicolon */ |
230 | | |
231 | 1.02k | ctx = apr_palloc(pool, sizeof *ctx); |
232 | | |
233 | 1.02k | attr++; |
234 | 1.02k | blen = strlen(attr) + 1; |
235 | 1.02k | buf = apr_palloc(pool, 4 + blen); |
236 | 1.02k | buf += 4; |
237 | 1.02k | memcpy(buf, attr, blen); |
238 | | |
239 | 1.02k | s = apreq_header_attribute(buf, "boundary", 8, |
240 | 1.02k | (const char **)&ctx->bdry, &blen); |
241 | 1.02k | if (s != APR_SUCCESS || !blen) |
242 | 119 | return NULL; /* missing or empty boundary */ |
243 | | |
244 | 901 | ctx->bdry[blen] = 0; |
245 | | |
246 | 901 | *--ctx->bdry = '-'; |
247 | 901 | *--ctx->bdry = '-'; |
248 | 901 | *--ctx->bdry = '\n'; |
249 | 901 | *--ctx->bdry = '\r'; |
250 | | |
251 | 901 | ctx->status = MFD_INIT; |
252 | 901 | ctx->pattern = apr_strmatch_precompile(pool, ctx->bdry, 1); |
253 | 901 | ctx->hdr_parser = apreq_parser_make(pool, ba, "", |
254 | 901 | apreq_parse_headers, |
255 | 901 | brigade_limit, |
256 | 901 | temp_dir, NULL, NULL); |
257 | 901 | ctx->info = NULL; |
258 | 901 | ctx->bb = apr_brigade_create(pool, ba); |
259 | 901 | ctx->in = apr_brigade_create(pool, ba); |
260 | 901 | ctx->eos = apr_bucket_eos_create(ba); |
261 | 901 | ctx->next_parser = NULL; |
262 | 901 | ctx->param_name = NULL; |
263 | 901 | ctx->upload = NULL; |
264 | 901 | ctx->level = level; |
265 | | |
266 | 901 | return ctx; |
267 | 1.02k | } |
268 | | |
269 | | APREQ_DECLARE_PARSER(apreq_parse_multipart) |
270 | 1.02k | { |
271 | 1.02k | apr_pool_t *pool = parser->pool; |
272 | 1.02k | apr_bucket_alloc_t *ba = parser->bucket_alloc; |
273 | 1.02k | struct mfd_ctx *ctx = parser->ctx; |
274 | 1.02k | apr_status_t s; |
275 | | |
276 | 1.02k | if (ctx == NULL) { |
277 | 940 | ctx = create_multipart_context(parser->content_type, |
278 | 940 | pool, ba, |
279 | 940 | parser->brigade_limit, |
280 | 940 | parser->temp_dir, 1); |
281 | 940 | if (ctx == NULL) |
282 | 126 | return APREQ_ERROR_GENERAL; |
283 | | |
284 | | |
285 | 814 | parser->ctx = ctx; |
286 | 814 | } |
287 | | |
288 | 898 | PARSER_STATUS_CHECK(MFD); |
289 | 898 | APR_BRIGADE_CONCAT(ctx->in, bb); |
290 | | |
291 | 1.97k | mfd_parse_brigade: |
292 | | |
293 | 1.97k | switch (ctx->status) { |
294 | | |
295 | 971 | case MFD_INIT: |
296 | 971 | { |
297 | 971 | s = split_on_bdry(ctx->bb, ctx->in, NULL, ctx->bdry + 2); |
298 | 971 | if (s != APR_SUCCESS) { |
299 | 110 | apreq_brigade_setaside(ctx->in, pool); |
300 | 110 | apreq_brigade_setaside(ctx->bb, pool); |
301 | 110 | return s; |
302 | 110 | } |
303 | 861 | ctx->status = MFD_NEXTLINE; |
304 | | /* Be polite and return any preamble text to the caller. */ |
305 | 861 | APR_BRIGADE_CONCAT(bb, ctx->bb); |
306 | 861 | } |
307 | | |
308 | | /* fall through */ |
309 | | |
310 | 1.12k | case MFD_NEXTLINE: |
311 | 1.12k | { |
312 | 1.12k | s = split_on_bdry(ctx->bb, ctx->in, NULL, CRLF); |
313 | 1.12k | if (s == APR_EOF) { |
314 | 0 | ctx->status = MFD_COMPLETE; |
315 | 0 | return APR_SUCCESS; |
316 | 0 | } |
317 | 1.12k | if (s != APR_SUCCESS) { |
318 | 86 | apreq_brigade_setaside(ctx->in, pool); |
319 | 86 | apreq_brigade_setaside(ctx->bb, pool); |
320 | 86 | return s; |
321 | 86 | } |
322 | 1.03k | if (!APR_BRIGADE_EMPTY(ctx->bb)) { |
323 | 426 | char *line; |
324 | 426 | apr_size_t len; |
325 | 426 | apr_brigade_pflatten(ctx->bb, &line, &len, pool); |
326 | | |
327 | 426 | if (len >= 2 && strncmp(line, "--", 2) == 0) { |
328 | 77 | APR_BRIGADE_CONCAT(bb, ctx->in); |
329 | 77 | ctx->status = MFD_COMPLETE; |
330 | 77 | return APR_SUCCESS; |
331 | 77 | } |
332 | 349 | apr_brigade_cleanup(ctx->bb); |
333 | 349 | } |
334 | | |
335 | 961 | ctx->status = MFD_HEADER; |
336 | 961 | ctx->info = NULL; |
337 | 961 | } |
338 | | /* fall through */ |
339 | | |
340 | 961 | case MFD_HEADER: |
341 | 961 | { |
342 | 961 | if (ctx->info == NULL) { |
343 | 961 | ctx->info = apr_table_make(pool, APREQ_DEFAULT_NELTS); |
344 | | /* flush out header parser internal structs for reuse */ |
345 | 961 | ctx->hdr_parser->ctx = NULL; |
346 | 961 | } |
347 | 961 | s = apreq_parser_run(ctx->hdr_parser, ctx->info, ctx->in); |
348 | 961 | switch (s) { |
349 | 808 | case APR_SUCCESS: |
350 | 808 | ctx->status = MFD_POST_HEADER; |
351 | 808 | break; |
352 | 80 | case APR_INCOMPLETE: |
353 | 80 | apreq_brigade_setaside(ctx->in, pool); |
354 | 80 | return APR_INCOMPLETE; |
355 | 73 | default: |
356 | 73 | ctx->status = MFD_ERROR; |
357 | 73 | return s; |
358 | 961 | } |
359 | 961 | } |
360 | | /* fall through */ |
361 | | |
362 | 808 | case MFD_POST_HEADER: |
363 | 808 | { |
364 | | /* Must handle special case of missing CRLF (mainly |
365 | | * coming from empty file uploads). See RFC2065 S5.1.1: |
366 | | * |
367 | | * body-part = MIME-part-header [CRLF *OCTET] |
368 | | * |
369 | | * So the CRLF we already matched in MFD_HEADER may have been |
370 | | * part of the boundary string! Both Konqueror (v??) and |
371 | | * Mozilla-0.97 are known to emit such blocks. |
372 | | * |
373 | | * Here we first check for this condition with |
374 | | * brigade_start_string, and prefix the brigade with |
375 | | * an additional CRLF bucket if necessary. |
376 | | */ |
377 | | |
378 | 808 | const char *cd, *ct, *name, *filename; |
379 | 808 | apr_size_t nlen, flen; |
380 | 808 | apr_bucket *e; |
381 | | |
382 | 808 | switch (brigade_start_string(ctx->in, ctx->bdry + 2)) { |
383 | | |
384 | 10 | case APR_INCOMPLETE: |
385 | 10 | apreq_brigade_setaside(ctx->in, pool); |
386 | 10 | return APR_INCOMPLETE; |
387 | | |
388 | 12 | case APR_SUCCESS: |
389 | | /* part has no body- return CRLF to front */ |
390 | 12 | e = apr_bucket_immortal_create(CRLF, 2, |
391 | 12 | ctx->bb->bucket_alloc); |
392 | 12 | APR_BRIGADE_INSERT_HEAD(ctx->in, e); |
393 | 12 | break; |
394 | | |
395 | 786 | default: |
396 | 786 | ; /* has body, ok */ |
397 | 808 | } |
398 | | |
399 | 798 | cd = apr_table_get(ctx->info, "Content-Disposition"); |
400 | | |
401 | | /* First check to see if must descend into a new multipart |
402 | | * block. If we do, create a new parser and pass control |
403 | | * to it. |
404 | | */ |
405 | | |
406 | 798 | ct = apr_table_get(ctx->info, "Content-Type"); |
407 | | |
408 | 798 | if (ct != NULL && strncmp(ct, "multipart/", 10) == 0) { |
409 | 95 | struct mfd_ctx *next_ctx; |
410 | | |
411 | 95 | if (ctx->level >= MAX_LEVEL) { |
412 | 0 | ctx->status = MFD_ERROR; |
413 | 0 | goto mfd_parse_brigade; |
414 | 0 | } |
415 | | |
416 | 95 | next_ctx = create_multipart_context(ct, pool, ba, |
417 | 95 | parser->brigade_limit, |
418 | 95 | parser->temp_dir, |
419 | 95 | ctx->level + 1); |
420 | 95 | if (next_ctx == NULL) { |
421 | 8 | ctx->status = MFD_ERROR; |
422 | 8 | goto mfd_parse_brigade; |
423 | 8 | } |
424 | | |
425 | 87 | if (cd != NULL) { |
426 | 7 | s = apreq_header_attribute(cd, "name", 4, |
427 | 7 | &name, &nlen); |
428 | 7 | if (s == APR_SUCCESS && nlen) { |
429 | 2 | next_ctx->param_name = apr_pstrmemdup(pool, name, |
430 | 2 | nlen); |
431 | 2 | } |
432 | 5 | else if (s != APREQ_ERROR_NOATTR) { |
433 | 3 | ctx->status = MFD_ERROR; |
434 | 3 | goto mfd_parse_brigade; |
435 | 3 | } |
436 | 7 | } |
437 | 84 | if (!next_ctx->param_name) { |
438 | 82 | const char *cid = apr_table_get(ctx->info, |
439 | 82 | "Content-ID"); |
440 | 82 | if (cid) { |
441 | 2 | next_ctx->param_name = apr_pstrdup(pool, cid); |
442 | 2 | } |
443 | 80 | else { |
444 | 80 | next_ctx->param_name = ""; |
445 | 80 | } |
446 | 82 | } |
447 | | |
448 | 84 | ctx->next_parser = apreq_parser_make(pool, ba, ct, |
449 | 84 | apreq_parse_multipart, |
450 | 84 | parser->brigade_limit, |
451 | 84 | parser->temp_dir, |
452 | 84 | parser->hook, |
453 | 84 | next_ctx); |
454 | 84 | ctx->status = MFD_MIXED; |
455 | 84 | goto mfd_parse_brigade; |
456 | | |
457 | 87 | } |
458 | | |
459 | | /* Look for a normal form-data part. */ |
460 | | |
461 | 703 | if (cd != NULL && strncmp(cd, "form-data", 9) == 0) { |
462 | 71 | s = apreq_header_attribute(cd, "name", 4, &name, &nlen); |
463 | 71 | if (s != APR_SUCCESS || !nlen) { |
464 | 7 | ctx->status = MFD_ERROR; |
465 | 7 | goto mfd_parse_brigade; |
466 | 7 | } |
467 | | |
468 | 64 | s = apreq_header_attribute(cd, "filename", |
469 | 64 | 8, &filename, &flen); |
470 | 64 | if (s == APR_SUCCESS && flen) { |
471 | 4 | apreq_param_t *param; |
472 | | |
473 | 4 | param = apreq_param_make(pool, name, nlen, |
474 | 4 | filename, flen); |
475 | 4 | if (param == NULL) |
476 | 0 | return APR_ENOMEM; |
477 | 4 | apreq_param_tainted_on(param); |
478 | 4 | param->info = ctx->info; |
479 | 4 | param->upload |
480 | 4 | = apr_brigade_create(pool, ctx->bb->bucket_alloc); |
481 | 4 | ctx->upload = param; |
482 | 4 | ctx->status = MFD_UPLOAD; |
483 | 4 | goto mfd_parse_brigade; |
484 | 4 | } |
485 | 60 | else if (s != APREQ_ERROR_NOATTR) { |
486 | 3 | ctx->status = MFD_ERROR; |
487 | 3 | goto mfd_parse_brigade; |
488 | 3 | } |
489 | 57 | else { |
490 | 57 | ctx->param_name = apr_pstrmemdup(pool, name, nlen); |
491 | 57 | ctx->status = MFD_PARAM; |
492 | | /* fall thru */ |
493 | 57 | } |
494 | 64 | } |
495 | | |
496 | | /* else check for a file part in a multipart section */ |
497 | 632 | else if (cd != NULL && strncmp(cd, "file", 4) == 0) { |
498 | 11 | apreq_param_t *param; |
499 | | |
500 | 11 | s = apreq_header_attribute(cd, "filename", |
501 | 11 | 8, &filename, &flen); |
502 | 11 | if (s != APR_SUCCESS || !flen || !ctx->param_name) { |
503 | 11 | ctx->status = MFD_ERROR; |
504 | 11 | goto mfd_parse_brigade; |
505 | 11 | } |
506 | 0 | name = ctx->param_name; |
507 | 0 | nlen = strlen(name); |
508 | 0 | param = apreq_param_make(pool, name, nlen, |
509 | 0 | filename, flen); |
510 | 0 | if (param == NULL) |
511 | 0 | return APR_ENOMEM; |
512 | 0 | apreq_param_tainted_on(param); |
513 | 0 | param->info = ctx->info; |
514 | 0 | param->upload = apr_brigade_create(pool, |
515 | 0 | ctx->bb->bucket_alloc); |
516 | 0 | ctx->upload = param; |
517 | 0 | ctx->status = MFD_UPLOAD; |
518 | 0 | goto mfd_parse_brigade; |
519 | 0 | } |
520 | | |
521 | | /* otherwise look for Content-ID in multipart/mixed case */ |
522 | 621 | else { |
523 | 621 | const char *cid = apr_table_get(ctx->info, "Content-ID"); |
524 | 621 | apreq_param_t *param; |
525 | | |
526 | 621 | if (cid != NULL) { |
527 | 15 | name = cid; |
528 | 15 | nlen = strlen(name); |
529 | 15 | } |
530 | 606 | else { |
531 | 606 | name = ""; |
532 | 606 | nlen = 0; |
533 | 606 | } |
534 | | |
535 | 621 | filename = ""; |
536 | 621 | flen = 0; |
537 | 621 | param = apreq_param_make(pool, name, nlen, |
538 | 621 | filename, flen); |
539 | 621 | if (param == NULL) |
540 | 0 | return APR_ENOMEM; |
541 | 621 | apreq_param_tainted_on(param); |
542 | 621 | param->info = ctx->info; |
543 | 621 | param->upload = apr_brigade_create(pool, |
544 | 621 | ctx->bb->bucket_alloc); |
545 | 621 | ctx->upload = param; |
546 | 621 | ctx->status = MFD_UPLOAD; |
547 | 621 | goto mfd_parse_brigade; |
548 | 621 | } |
549 | 703 | } |
550 | | /* fall through */ |
551 | | |
552 | 57 | case MFD_PARAM: |
553 | 57 | { |
554 | 57 | apreq_param_t *param; |
555 | 57 | apreq_value_t *v; |
556 | 57 | apr_size_t len; |
557 | 57 | apr_off_t off; |
558 | | |
559 | 57 | s = split_on_bdry(ctx->bb, ctx->in, ctx->pattern, ctx->bdry); |
560 | | |
561 | 57 | switch (s) { |
562 | | |
563 | 4 | case APR_INCOMPLETE: |
564 | 4 | apreq_brigade_setaside(ctx->in, pool); |
565 | 4 | apreq_brigade_setaside(ctx->bb, pool); |
566 | 4 | return s; |
567 | | |
568 | 53 | case APR_SUCCESS: |
569 | 53 | s = apr_brigade_length(ctx->bb, 1, &off); |
570 | 53 | if (s != APR_SUCCESS) { |
571 | 0 | ctx->status = MFD_ERROR; |
572 | 0 | return s; |
573 | 0 | } |
574 | 53 | len = off; |
575 | 53 | param = apreq_param_make(pool, ctx->param_name, |
576 | 53 | strlen(ctx->param_name), |
577 | 53 | NULL, len); |
578 | 53 | if (param == NULL) |
579 | 0 | return APR_ENOMEM; |
580 | 53 | apreq_param_tainted_on(param); |
581 | 53 | param->info = ctx->info; |
582 | | |
583 | 53 | *(const apreq_value_t **)&v = ¶m->v; |
584 | 53 | apr_brigade_flatten(ctx->bb, v->data, &len); |
585 | 53 | v->data[len] = 0; |
586 | | |
587 | 53 | if (parser->hook != NULL) { |
588 | 53 | s = apreq_hook_run(parser->hook, param, NULL); |
589 | 53 | if (s != APR_SUCCESS) { |
590 | 0 | ctx->status = MFD_ERROR; |
591 | 0 | return s; |
592 | 0 | } |
593 | 53 | } |
594 | | |
595 | 53 | apreq_param_charset_set(param, |
596 | 53 | apreq_charset_divine(v->data, len)); |
597 | 53 | apreq_value_table_add(v, t); |
598 | 53 | ctx->status = MFD_NEXTLINE; |
599 | 53 | ctx->param_name = NULL; |
600 | 53 | apr_brigade_cleanup(ctx->bb); |
601 | 53 | goto mfd_parse_brigade; |
602 | | |
603 | 0 | default: |
604 | 0 | ctx->status = MFD_ERROR; |
605 | 0 | return s; |
606 | 57 | } |
607 | | |
608 | | |
609 | 57 | } |
610 | 0 | break; /* not reached */ |
611 | | |
612 | 625 | case MFD_UPLOAD: |
613 | 625 | { |
614 | 625 | apreq_param_t *param = ctx->upload; |
615 | | |
616 | 625 | s = split_on_bdry(ctx->bb, ctx->in, ctx->pattern, ctx->bdry); |
617 | 625 | switch (s) { |
618 | | |
619 | 415 | case APR_INCOMPLETE: |
620 | 415 | if (parser->hook != NULL) { |
621 | 415 | s = apreq_hook_run(parser->hook, param, ctx->bb); |
622 | 415 | if (s != APR_SUCCESS) { |
623 | 0 | ctx->status = MFD_ERROR; |
624 | 0 | return s; |
625 | 0 | } |
626 | 415 | } |
627 | 415 | apreq_brigade_setaside(ctx->bb, pool); |
628 | 415 | apreq_brigade_setaside(ctx->in, pool); |
629 | 415 | s = apreq_brigade_concat(pool, parser->temp_dir, |
630 | 415 | parser->brigade_limit, |
631 | 415 | param->upload, ctx->bb); |
632 | 415 | return (s == APR_SUCCESS) ? APR_INCOMPLETE : s; |
633 | | |
634 | 210 | case APR_SUCCESS: |
635 | 210 | if (parser->hook != NULL) { |
636 | 210 | APR_BRIGADE_INSERT_TAIL(ctx->bb, ctx->eos); |
637 | 210 | s = apreq_hook_run(parser->hook, param, ctx->bb); |
638 | 210 | APR_BUCKET_REMOVE(ctx->eos); |
639 | 210 | if (s != APR_SUCCESS) { |
640 | 0 | ctx->status = MFD_ERROR; |
641 | 0 | return s; |
642 | 0 | } |
643 | 210 | } |
644 | 210 | apreq_value_table_add(¶m->v, t); |
645 | 210 | apreq_brigade_setaside(ctx->bb, pool); |
646 | 210 | s = apreq_brigade_concat(pool, parser->temp_dir, |
647 | 210 | parser->brigade_limit, |
648 | 210 | param->upload, ctx->bb); |
649 | | |
650 | 210 | if (s != APR_SUCCESS) |
651 | 0 | return s; |
652 | | |
653 | 210 | ctx->status = MFD_NEXTLINE; |
654 | 210 | goto mfd_parse_brigade; |
655 | | |
656 | 0 | default: |
657 | 0 | ctx->status = MFD_ERROR; |
658 | 0 | return s; |
659 | 625 | } |
660 | | |
661 | 625 | } |
662 | 0 | break; /* not reached */ |
663 | | |
664 | | |
665 | 84 | case MFD_MIXED: |
666 | 84 | { |
667 | 84 | s = apreq_parser_run(ctx->next_parser, t, ctx->in); |
668 | 84 | switch (s) { |
669 | 73 | case APR_SUCCESS: |
670 | 73 | ctx->status = MFD_INIT; |
671 | 73 | ctx->param_name = NULL; |
672 | 73 | goto mfd_parse_brigade; |
673 | 9 | case APR_INCOMPLETE: |
674 | 9 | APR_BRIGADE_CONCAT(bb, ctx->in); |
675 | 9 | return APR_INCOMPLETE; |
676 | 2 | default: |
677 | 2 | ctx->status = MFD_ERROR; |
678 | 2 | return s; |
679 | 84 | } |
680 | | |
681 | 84 | } |
682 | 0 | break; /* not reached */ |
683 | | |
684 | 32 | default: |
685 | 32 | return APREQ_ERROR_GENERAL; |
686 | 1.97k | } |
687 | | |
688 | 0 | return APR_INCOMPLETE; |
689 | 1.97k | } |