/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.09k | #define CRLF "\015\012" |
26 | | #endif |
27 | | |
28 | 4.57k | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
29 | | |
30 | 871 | #define PARSER_STATUS_CHECK(PREFIX) do { \ |
31 | 871 | if (ctx->status == PREFIX##_ERROR) \ |
32 | 871 | return APREQ_ERROR_GENERAL; \ |
33 | 871 | else if (ctx->status == PREFIX##_COMPLETE) \ |
34 | 871 | return APR_SUCCESS; \ |
35 | 871 | else if (bb == NULL) \ |
36 | 871 | return APR_INCOMPLETE; \ |
37 | 871 | } while (0); |
38 | | |
39 | | /* maximum recursion level in the mfd parser */ |
40 | 87 | #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 | 774 | { |
74 | 774 | apr_bucket *e; |
75 | 774 | apr_size_t slen = strlen(start_string); |
76 | | |
77 | 789 | for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); |
78 | 774 | e = APR_BUCKET_NEXT(e)) |
79 | 780 | { |
80 | 780 | const char *buf; |
81 | 780 | apr_status_t s, bytes_to_check; |
82 | 780 | apr_size_t blen; |
83 | | |
84 | 780 | if (slen == 0) |
85 | 9 | return APR_SUCCESS; |
86 | | |
87 | 771 | if (APR_BUCKET_IS_EOS(e)) |
88 | 0 | return APR_EOF; |
89 | | |
90 | 771 | s = apr_bucket_read(e, &buf, &blen, APR_BLOCK_READ); |
91 | | |
92 | 771 | if (s != APR_SUCCESS) |
93 | 0 | return s; |
94 | | |
95 | 771 | if (blen == 0) |
96 | 0 | continue; |
97 | | |
98 | 771 | bytes_to_check = MIN(slen,blen); |
99 | | |
100 | 771 | if (strncmp(buf,start_string,bytes_to_check) != 0) |
101 | 756 | return APREQ_ERROR_GENERAL; |
102 | | |
103 | 15 | slen -= bytes_to_check; |
104 | 15 | start_string += bytes_to_check; |
105 | 15 | } |
106 | | |
107 | | /* slen > 0, so brigade isn't large enough yet */ |
108 | 9 | return APR_INCOMPLETE; |
109 | 774 | } |
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.66k | { |
117 | 2.66k | apr_bucket *e = APR_BRIGADE_FIRST(in); |
118 | 2.66k | apr_size_t blen = strlen(bdry), off = 0; |
119 | | |
120 | 4.38k | while ( e != APR_BRIGADE_SENTINEL(in) ) { |
121 | 3.79k | apr_ssize_t idx; |
122 | 3.79k | apr_size_t len; |
123 | 3.79k | const char *buf; |
124 | 3.79k | apr_status_t s; |
125 | | |
126 | 3.79k | if (APR_BUCKET_IS_EOS(e)) |
127 | 0 | return APR_EOF; |
128 | | |
129 | 3.79k | s = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); |
130 | 3.79k | if (s != APR_SUCCESS) |
131 | 0 | return s; |
132 | | |
133 | 3.79k | 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.80k | look_for_boundary_up_front: |
141 | 3.80k | if (strncmp(bdry + off, buf, MIN(len, blen - off)) == 0) { |
142 | 2.22k | if ( len >= blen - off ) { |
143 | | /* complete match */ |
144 | 2.07k | if (len > blen - off) |
145 | 2.04k | apr_bucket_split(e, blen - off); |
146 | 2.07k | e = APR_BUCKET_NEXT(e); |
147 | | |
148 | 2.08k | do { |
149 | 2.08k | apr_bucket *f = APR_BRIGADE_FIRST(in); |
150 | 2.08k | apr_bucket_delete(f); |
151 | 2.08k | } while (APR_BRIGADE_FIRST(in) != e); |
152 | | |
153 | 2.07k | return APR_SUCCESS; |
154 | 2.07k | } |
155 | | /* partial match */ |
156 | 148 | off += len; |
157 | 148 | e = APR_BUCKET_NEXT(e); |
158 | 148 | continue; |
159 | 2.22k | } |
160 | 1.58k | 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 | 12 | apr_bucket_brigade * volatile in_v = in; |
171 | | |
172 | 12 | do { |
173 | 12 | apr_bucket *f = APR_BRIGADE_FIRST(in_v); |
174 | 12 | APR_BUCKET_REMOVE(f); |
175 | 12 | APR_BRIGADE_INSERT_TAIL(out, f); |
176 | 12 | } while (e != APR_BRIGADE_FIRST(in_v)); |
177 | 12 | off = 0; |
178 | 12 | goto look_for_boundary_up_front; |
179 | 12 | } |
180 | | |
181 | 1.56k | if (pattern != NULL && len >= blen) { |
182 | 453 | const char *match = apr_strmatch(pattern, buf, len); |
183 | 453 | if (match != NULL) |
184 | 170 | idx = match - buf; |
185 | 283 | else { |
186 | 283 | idx = apreq_index(buf + len-blen, blen, bdry, blen, |
187 | 283 | APREQ_MATCH_PARTIAL); |
188 | 283 | if (idx >= 0) |
189 | 56 | idx += len-blen; |
190 | 283 | } |
191 | 453 | } |
192 | 1.11k | else |
193 | 1.11k | 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.56k | if (idx >= 0) |
201 | 1.11k | apr_bucket_split(e, idx); |
202 | | |
203 | 1.56k | APR_BUCKET_REMOVE(e); |
204 | 1.56k | APR_BRIGADE_INSERT_TAIL(out, e); |
205 | 1.56k | e = APR_BRIGADE_FIRST(in); |
206 | 1.56k | } |
207 | | |
208 | 592 | return APR_INCOMPLETE; |
209 | 2.66k | } |
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 | 982 | { |
221 | 982 | apr_status_t s; |
222 | 982 | apr_size_t blen; |
223 | 982 | struct mfd_ctx *ctx; |
224 | 982 | const char *attr; |
225 | 982 | char *buf; |
226 | | |
227 | 982 | attr = (content_type) ? strchr(content_type, ';') : NULL; |
228 | 982 | if (!attr) |
229 | 12 | return NULL; /* missing semicolon */ |
230 | | |
231 | 970 | ctx = apr_palloc(pool, sizeof *ctx); |
232 | | |
233 | 970 | attr++; |
234 | 970 | blen = strlen(attr) + 1; |
235 | 970 | buf = apr_palloc(pool, 4 + blen); |
236 | 970 | buf += 4; |
237 | 970 | memcpy(buf, attr, blen); |
238 | | |
239 | 970 | s = apreq_header_attribute(buf, "boundary", 8, |
240 | 970 | (const char **)&ctx->bdry, &blen); |
241 | 970 | if (s != APR_SUCCESS || !blen) |
242 | 96 | return NULL; /* missing or empty boundary */ |
243 | | |
244 | 874 | ctx->bdry[blen] = 0; |
245 | | |
246 | 874 | *--ctx->bdry = '-'; |
247 | 874 | *--ctx->bdry = '-'; |
248 | 874 | *--ctx->bdry = '\n'; |
249 | 874 | *--ctx->bdry = '\r'; |
250 | | |
251 | 874 | ctx->status = MFD_INIT; |
252 | 874 | ctx->pattern = apr_strmatch_precompile(pool, ctx->bdry, 1); |
253 | 874 | ctx->hdr_parser = apreq_parser_make(pool, ba, "", |
254 | 874 | apreq_parse_headers, |
255 | 874 | brigade_limit, |
256 | 874 | temp_dir, NULL, NULL); |
257 | 874 | ctx->info = NULL; |
258 | 874 | ctx->bb = apr_brigade_create(pool, ba); |
259 | 874 | ctx->in = apr_brigade_create(pool, ba); |
260 | 874 | ctx->eos = apr_bucket_eos_create(ba); |
261 | 874 | ctx->next_parser = NULL; |
262 | 874 | ctx->param_name = NULL; |
263 | 874 | ctx->upload = NULL; |
264 | 874 | ctx->level = level; |
265 | | |
266 | 874 | return ctx; |
267 | 970 | } |
268 | | |
269 | | APREQ_DECLARE_PARSER(apreq_parse_multipart) |
270 | 976 | { |
271 | 976 | apr_pool_t *pool = parser->pool; |
272 | 976 | apr_bucket_alloc_t *ba = parser->bucket_alloc; |
273 | 976 | struct mfd_ctx *ctx = parser->ctx; |
274 | 976 | apr_status_t s; |
275 | | |
276 | 976 | if (ctx == NULL) { |
277 | 895 | ctx = create_multipart_context(parser->content_type, |
278 | 895 | pool, ba, |
279 | 895 | parser->brigade_limit, |
280 | 895 | parser->temp_dir, 1); |
281 | 895 | if (ctx == NULL) |
282 | 105 | return APREQ_ERROR_GENERAL; |
283 | | |
284 | | |
285 | 790 | parser->ctx = ctx; |
286 | 790 | } |
287 | | |
288 | 871 | PARSER_STATUS_CHECK(MFD); |
289 | 871 | APR_BRIGADE_CONCAT(ctx->in, bb); |
290 | | |
291 | 1.89k | mfd_parse_brigade: |
292 | | |
293 | 1.89k | switch (ctx->status) { |
294 | | |
295 | 939 | case MFD_INIT: |
296 | 939 | { |
297 | 939 | s = split_on_bdry(ctx->bb, ctx->in, NULL, ctx->bdry + 2); |
298 | 939 | if (s != APR_SUCCESS) { |
299 | 94 | apreq_brigade_setaside(ctx->in, pool); |
300 | 94 | apreq_brigade_setaside(ctx->bb, pool); |
301 | 94 | return s; |
302 | 94 | } |
303 | 845 | ctx->status = MFD_NEXTLINE; |
304 | | /* Be polite and return any preamble text to the caller. */ |
305 | 845 | APR_BRIGADE_CONCAT(bb, ctx->bb); |
306 | 845 | } |
307 | | |
308 | | /* fall through */ |
309 | | |
310 | 1.08k | case MFD_NEXTLINE: |
311 | 1.08k | { |
312 | 1.08k | s = split_on_bdry(ctx->bb, ctx->in, NULL, CRLF); |
313 | 1.08k | if (s == APR_EOF) { |
314 | 0 | ctx->status = MFD_COMPLETE; |
315 | 0 | return APR_SUCCESS; |
316 | 0 | } |
317 | 1.08k | if (s != APR_SUCCESS) { |
318 | 90 | apreq_brigade_setaside(ctx->in, pool); |
319 | 90 | apreq_brigade_setaside(ctx->bb, pool); |
320 | 90 | return s; |
321 | 90 | } |
322 | 993 | if (!APR_BRIGADE_EMPTY(ctx->bb)) { |
323 | 399 | char *line; |
324 | 399 | apr_size_t len; |
325 | 399 | apr_brigade_pflatten(ctx->bb, &line, &len, pool); |
326 | | |
327 | 399 | if (len >= 2 && strncmp(line, "--", 2) == 0) { |
328 | 75 | APR_BRIGADE_CONCAT(bb, ctx->in); |
329 | 75 | ctx->status = MFD_COMPLETE; |
330 | 75 | return APR_SUCCESS; |
331 | 75 | } |
332 | 324 | apr_brigade_cleanup(ctx->bb); |
333 | 324 | } |
334 | | |
335 | 918 | ctx->status = MFD_HEADER; |
336 | 918 | ctx->info = NULL; |
337 | 918 | } |
338 | | /* fall through */ |
339 | | |
340 | 918 | case MFD_HEADER: |
341 | 918 | { |
342 | 918 | if (ctx->info == NULL) { |
343 | 918 | ctx->info = apr_table_make(pool, APREQ_DEFAULT_NELTS); |
344 | | /* flush out header parser internal structs for reuse */ |
345 | 918 | ctx->hdr_parser->ctx = NULL; |
346 | 918 | } |
347 | 918 | s = apreq_parser_run(ctx->hdr_parser, ctx->info, ctx->in); |
348 | 918 | switch (s) { |
349 | 774 | case APR_SUCCESS: |
350 | 774 | ctx->status = MFD_POST_HEADER; |
351 | 774 | break; |
352 | 83 | case APR_INCOMPLETE: |
353 | 83 | apreq_brigade_setaside(ctx->in, pool); |
354 | 83 | return APR_INCOMPLETE; |
355 | 61 | default: |
356 | 61 | ctx->status = MFD_ERROR; |
357 | 61 | return s; |
358 | 918 | } |
359 | 918 | } |
360 | | /* fall through */ |
361 | | |
362 | 774 | case MFD_POST_HEADER: |
363 | 774 | { |
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 | 774 | const char *cd, *ct, *name, *filename; |
379 | 774 | apr_size_t nlen, flen; |
380 | 774 | apr_bucket *e; |
381 | | |
382 | 774 | switch (brigade_start_string(ctx->in, ctx->bdry + 2)) { |
383 | | |
384 | 9 | case APR_INCOMPLETE: |
385 | 9 | apreq_brigade_setaside(ctx->in, pool); |
386 | 9 | return APR_INCOMPLETE; |
387 | | |
388 | 9 | case APR_SUCCESS: |
389 | | /* part has no body- return CRLF to front */ |
390 | 9 | e = apr_bucket_immortal_create(CRLF, 2, |
391 | 9 | ctx->bb->bucket_alloc); |
392 | 9 | APR_BRIGADE_INSERT_HEAD(ctx->in, e); |
393 | 9 | break; |
394 | | |
395 | 756 | default: |
396 | 756 | ; /* has body, ok */ |
397 | 774 | } |
398 | | |
399 | 765 | 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 | 765 | ct = apr_table_get(ctx->info, "Content-Type"); |
407 | | |
408 | 765 | if (ct != NULL && strncmp(ct, "multipart/", 10) == 0) { |
409 | 87 | struct mfd_ctx *next_ctx; |
410 | | |
411 | 87 | if (ctx->level >= MAX_LEVEL) { |
412 | 0 | ctx->status = MFD_ERROR; |
413 | 0 | goto mfd_parse_brigade; |
414 | 0 | } |
415 | | |
416 | 87 | next_ctx = create_multipart_context(ct, pool, ba, |
417 | 87 | parser->brigade_limit, |
418 | 87 | parser->temp_dir, |
419 | 87 | ctx->level + 1); |
420 | 87 | if (next_ctx == NULL) { |
421 | 3 | ctx->status = MFD_ERROR; |
422 | 3 | goto mfd_parse_brigade; |
423 | 3 | } |
424 | | |
425 | 84 | if (cd != NULL) { |
426 | 6 | s = apreq_header_attribute(cd, "name", 4, |
427 | 6 | &name, &nlen); |
428 | 6 | if (s == APR_SUCCESS && nlen) { |
429 | 2 | next_ctx->param_name = apr_pstrmemdup(pool, name, |
430 | 2 | nlen); |
431 | 2 | } |
432 | 4 | else if (s != APREQ_ERROR_NOATTR) { |
433 | 3 | ctx->status = MFD_ERROR; |
434 | 3 | goto mfd_parse_brigade; |
435 | 3 | } |
436 | 6 | } |
437 | 81 | if (!next_ctx->param_name) { |
438 | 79 | const char *cid = apr_table_get(ctx->info, |
439 | 79 | "Content-ID"); |
440 | 79 | if (cid) { |
441 | 2 | next_ctx->param_name = apr_pstrdup(pool, cid); |
442 | 2 | } |
443 | 77 | else { |
444 | 77 | next_ctx->param_name = ""; |
445 | 77 | } |
446 | 79 | } |
447 | | |
448 | 81 | ctx->next_parser = apreq_parser_make(pool, ba, ct, |
449 | 81 | apreq_parse_multipart, |
450 | 81 | parser->brigade_limit, |
451 | 81 | parser->temp_dir, |
452 | 81 | parser->hook, |
453 | 81 | next_ctx); |
454 | 81 | ctx->status = MFD_MIXED; |
455 | 81 | goto mfd_parse_brigade; |
456 | | |
457 | 84 | } |
458 | | |
459 | | /* Look for a normal form-data part. */ |
460 | | |
461 | 678 | if (cd != NULL && strncmp(cd, "form-data", 9) == 0) { |
462 | 70 | s = apreq_header_attribute(cd, "name", 4, &name, &nlen); |
463 | 70 | if (s != APR_SUCCESS || !nlen) { |
464 | 10 | ctx->status = MFD_ERROR; |
465 | 10 | goto mfd_parse_brigade; |
466 | 10 | } |
467 | | |
468 | 60 | s = apreq_header_attribute(cd, "filename", |
469 | 60 | 8, &filename, &flen); |
470 | 60 | 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 | apreq_param_tainted_on(param); |
476 | 4 | param->info = ctx->info; |
477 | 4 | param->upload |
478 | 4 | = apr_brigade_create(pool, ctx->bb->bucket_alloc); |
479 | 4 | ctx->upload = param; |
480 | 4 | ctx->status = MFD_UPLOAD; |
481 | 4 | goto mfd_parse_brigade; |
482 | 4 | } |
483 | 56 | else if (s != APREQ_ERROR_NOATTR) { |
484 | 5 | ctx->status = MFD_ERROR; |
485 | 5 | goto mfd_parse_brigade; |
486 | 5 | } |
487 | 51 | else { |
488 | 51 | ctx->param_name = apr_pstrmemdup(pool, name, nlen); |
489 | 51 | ctx->status = MFD_PARAM; |
490 | | /* fall thru */ |
491 | 51 | } |
492 | 60 | } |
493 | | |
494 | | /* else check for a file part in a multipart section */ |
495 | 608 | else if (cd != NULL && strncmp(cd, "file", 4) == 0) { |
496 | 17 | apreq_param_t *param; |
497 | | |
498 | 17 | s = apreq_header_attribute(cd, "filename", |
499 | 17 | 8, &filename, &flen); |
500 | 17 | if (s != APR_SUCCESS || !flen || !ctx->param_name) { |
501 | 17 | ctx->status = MFD_ERROR; |
502 | 17 | goto mfd_parse_brigade; |
503 | 17 | } |
504 | 0 | name = ctx->param_name; |
505 | 0 | nlen = strlen(name); |
506 | 0 | param = apreq_param_make(pool, name, nlen, |
507 | 0 | filename, flen); |
508 | 0 | apreq_param_tainted_on(param); |
509 | 0 | param->info = ctx->info; |
510 | 0 | param->upload = apr_brigade_create(pool, |
511 | 0 | ctx->bb->bucket_alloc); |
512 | 0 | ctx->upload = param; |
513 | 0 | ctx->status = MFD_UPLOAD; |
514 | 0 | goto mfd_parse_brigade; |
515 | 17 | } |
516 | | |
517 | | /* otherwise look for Content-ID in multipart/mixed case */ |
518 | 591 | else { |
519 | 591 | const char *cid = apr_table_get(ctx->info, "Content-ID"); |
520 | 591 | apreq_param_t *param; |
521 | | |
522 | 591 | if (cid != NULL) { |
523 | 15 | name = cid; |
524 | 15 | nlen = strlen(name); |
525 | 15 | } |
526 | 576 | else { |
527 | 576 | name = ""; |
528 | 576 | nlen = 0; |
529 | 576 | } |
530 | | |
531 | 591 | filename = ""; |
532 | 591 | flen = 0; |
533 | 591 | param = apreq_param_make(pool, name, nlen, |
534 | 591 | filename, flen); |
535 | 591 | apreq_param_tainted_on(param); |
536 | 591 | param->info = ctx->info; |
537 | 591 | param->upload = apr_brigade_create(pool, |
538 | 591 | ctx->bb->bucket_alloc); |
539 | 591 | ctx->upload = param; |
540 | 591 | ctx->status = MFD_UPLOAD; |
541 | 591 | goto mfd_parse_brigade; |
542 | 591 | } |
543 | 678 | } |
544 | | /* fall through */ |
545 | | |
546 | 51 | case MFD_PARAM: |
547 | 51 | { |
548 | 51 | apreq_param_t *param; |
549 | 51 | apreq_value_t *v; |
550 | 51 | apr_size_t len; |
551 | 51 | apr_off_t off; |
552 | | |
553 | 51 | s = split_on_bdry(ctx->bb, ctx->in, ctx->pattern, ctx->bdry); |
554 | | |
555 | 51 | switch (s) { |
556 | | |
557 | 5 | case APR_INCOMPLETE: |
558 | 5 | apreq_brigade_setaside(ctx->in, pool); |
559 | 5 | apreq_brigade_setaside(ctx->bb, pool); |
560 | 5 | return s; |
561 | | |
562 | 46 | case APR_SUCCESS: |
563 | 46 | s = apr_brigade_length(ctx->bb, 1, &off); |
564 | 46 | if (s != APR_SUCCESS) { |
565 | 0 | ctx->status = MFD_ERROR; |
566 | 0 | return s; |
567 | 0 | } |
568 | 46 | len = off; |
569 | 46 | param = apreq_param_make(pool, ctx->param_name, |
570 | 46 | strlen(ctx->param_name), |
571 | 46 | NULL, len); |
572 | 46 | apreq_param_tainted_on(param); |
573 | 46 | param->info = ctx->info; |
574 | | |
575 | 46 | *(const apreq_value_t **)&v = ¶m->v; |
576 | 46 | apr_brigade_flatten(ctx->bb, v->data, &len); |
577 | 46 | v->data[len] = 0; |
578 | | |
579 | 46 | if (parser->hook != NULL) { |
580 | 46 | s = apreq_hook_run(parser->hook, param, NULL); |
581 | 46 | if (s != APR_SUCCESS) { |
582 | 0 | ctx->status = MFD_ERROR; |
583 | 0 | return s; |
584 | 0 | } |
585 | 46 | } |
586 | | |
587 | 46 | apreq_param_charset_set(param, |
588 | 46 | apreq_charset_divine(v->data, len)); |
589 | 46 | apreq_value_table_add(v, t); |
590 | 46 | ctx->status = MFD_NEXTLINE; |
591 | 46 | ctx->param_name = NULL; |
592 | 46 | apr_brigade_cleanup(ctx->bb); |
593 | 46 | goto mfd_parse_brigade; |
594 | | |
595 | 0 | default: |
596 | 0 | ctx->status = MFD_ERROR; |
597 | 0 | return s; |
598 | 51 | } |
599 | | |
600 | | |
601 | 51 | } |
602 | 0 | break; /* not reached */ |
603 | | |
604 | 595 | case MFD_UPLOAD: |
605 | 595 | { |
606 | 595 | apreq_param_t *param = ctx->upload; |
607 | | |
608 | 595 | s = split_on_bdry(ctx->bb, ctx->in, ctx->pattern, ctx->bdry); |
609 | 595 | switch (s) { |
610 | | |
611 | 403 | case APR_INCOMPLETE: |
612 | 403 | if (parser->hook != NULL) { |
613 | 403 | s = apreq_hook_run(parser->hook, param, ctx->bb); |
614 | 403 | if (s != APR_SUCCESS) { |
615 | 0 | ctx->status = MFD_ERROR; |
616 | 0 | return s; |
617 | 0 | } |
618 | 403 | } |
619 | 403 | apreq_brigade_setaside(ctx->bb, pool); |
620 | 403 | apreq_brigade_setaside(ctx->in, pool); |
621 | 403 | s = apreq_brigade_concat(pool, parser->temp_dir, |
622 | 403 | parser->brigade_limit, |
623 | 403 | param->upload, ctx->bb); |
624 | 403 | return (s == APR_SUCCESS) ? APR_INCOMPLETE : s; |
625 | | |
626 | 192 | case APR_SUCCESS: |
627 | 192 | if (parser->hook != NULL) { |
628 | 192 | APR_BRIGADE_INSERT_TAIL(ctx->bb, ctx->eos); |
629 | 192 | s = apreq_hook_run(parser->hook, param, ctx->bb); |
630 | 192 | APR_BUCKET_REMOVE(ctx->eos); |
631 | 192 | if (s != APR_SUCCESS) { |
632 | 0 | ctx->status = MFD_ERROR; |
633 | 0 | return s; |
634 | 0 | } |
635 | 192 | } |
636 | 192 | apreq_value_table_add(¶m->v, t); |
637 | 192 | apreq_brigade_setaside(ctx->bb, pool); |
638 | 192 | s = apreq_brigade_concat(pool, parser->temp_dir, |
639 | 192 | parser->brigade_limit, |
640 | 192 | param->upload, ctx->bb); |
641 | | |
642 | 192 | if (s != APR_SUCCESS) |
643 | 0 | return s; |
644 | | |
645 | 192 | ctx->status = MFD_NEXTLINE; |
646 | 192 | goto mfd_parse_brigade; |
647 | | |
648 | 0 | default: |
649 | 0 | ctx->status = MFD_ERROR; |
650 | 0 | return s; |
651 | 595 | } |
652 | | |
653 | 595 | } |
654 | 0 | break; /* not reached */ |
655 | | |
656 | | |
657 | 81 | case MFD_MIXED: |
658 | 81 | { |
659 | 81 | s = apreq_parser_run(ctx->next_parser, t, ctx->in); |
660 | 81 | switch (s) { |
661 | 68 | case APR_SUCCESS: |
662 | 68 | ctx->status = MFD_INIT; |
663 | 68 | ctx->param_name = NULL; |
664 | 68 | goto mfd_parse_brigade; |
665 | 9 | case APR_INCOMPLETE: |
666 | 9 | APR_BRIGADE_CONCAT(bb, ctx->in); |
667 | 9 | return APR_INCOMPLETE; |
668 | 4 | default: |
669 | 4 | ctx->status = MFD_ERROR; |
670 | 4 | return s; |
671 | 81 | } |
672 | | |
673 | 81 | } |
674 | 0 | break; /* not reached */ |
675 | | |
676 | 38 | default: |
677 | 38 | return APREQ_ERROR_GENERAL; |
678 | 1.89k | } |
679 | | |
680 | 0 | return APR_INCOMPLETE; |
681 | 1.89k | } |