Coverage Report

Created: 2025-07-18 06:33

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