Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/rfc1867.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
12
   |          Jani Taskinen <jani@php.net>                                |
13
   +----------------------------------------------------------------------+
14
 */
15
16
/*
17
 *  This product includes software developed by the Apache Group
18
 *  for use in the Apache HTTP server project (http://www.apache.org/).
19
 *
20
 */
21
22
#include <stdio.h>
23
#include "php.h"
24
#include "php_open_temporary_file.h"
25
#include "zend_globals.h"
26
#include "php_globals.h"
27
#include "php_variables.h"
28
#include "rfc1867.h"
29
#include "zend_smart_string.h"
30
#include "zend_exceptions.h"
31
32
#ifndef DEBUG_FILE_UPLOAD
33
# define DEBUG_FILE_UPLOAD 0
34
#endif
35
36
static int dummy_encoding_translation(void)
37
0
{
38
0
  return 0;
39
0
}
40
41
static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop);
42
static char *php_ap_getword_conf(const zend_encoding *encoding, char *str);
43
44
static php_rfc1867_encoding_translation_t php_rfc1867_encoding_translation = dummy_encoding_translation;
45
static php_rfc1867_get_detect_order_t php_rfc1867_get_detect_order = NULL;
46
static php_rfc1867_set_input_encoding_t php_rfc1867_set_input_encoding = NULL;
47
static php_rfc1867_getword_t php_rfc1867_getword = php_ap_getword;
48
static php_rfc1867_getword_conf_t php_rfc1867_getword_conf = php_ap_getword_conf;
49
static php_rfc1867_basename_t php_rfc1867_basename = NULL;
50
51
PHPAPI zend_result (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra) = NULL;
52
53
static bool safe_php_register_variable(char *var, char *strval, size_t val_len, zval *track_vars_array, bool override_protection);
54
55
/* The longest property name we use in an uploaded file array */
56
0
#define MAX_SIZE_OF_INDEX sizeof("[full_path]")
57
58
/* The longest anonymous name */
59
0
#define MAX_SIZE_ANONNAME 33
60
61
static void normalize_protected_variable(char *varname) /* {{{ */
62
0
{
63
0
  char *s = varname, *index = NULL, *indexend = NULL, *p;
64
65
  /* skip leading space */
66
0
  while (*s == ' ') {
67
0
    s++;
68
0
  }
69
70
  /* and remove it */
71
0
  if (s != varname) {
72
0
    memmove(varname, s, strlen(s)+1);
73
0
  }
74
75
0
  for (p = varname; *p && *p != '['; p++) {
76
0
    switch(*p) {
77
0
      case ' ':
78
0
      case '.':
79
0
        *p = '_';
80
0
        break;
81
0
    }
82
0
  }
83
84
  /* find index */
85
0
  index = strchr(varname, '[');
86
0
  if (index) {
87
0
    index++;
88
0
    s = index;
89
0
  } else {
90
0
    return;
91
0
  }
92
93
  /* done? */
94
0
  while (index) {
95
0
    while (*index == ' ' || *index == '\r' || *index == '\n' || *index=='\t') {
96
0
      index++;
97
0
    }
98
0
    indexend = strchr(index, ']');
99
0
    indexend = indexend ? indexend + 1 : index + strlen(index);
100
101
0
    if (s != index) {
102
0
      memmove(s, index, strlen(index)+1);
103
0
      s += indexend-index;
104
0
    } else {
105
0
      s = indexend;
106
0
    }
107
108
0
    if (*s == '[') {
109
0
      s++;
110
0
      index = s;
111
0
    } else {
112
0
      index = NULL;
113
0
    }
114
0
  }
115
0
  *s = '\0';
116
0
}
117
/* }}} */
118
119
static bool add_protected_variable(char *varname) /* {{{ */
120
0
{
121
0
  normalize_protected_variable(varname);
122
0
  zend_hash_str_add_empty_element(&PG(rfc1867_protected_variables), varname, strlen(varname));
123
0
  return !ZEND_HAS_OOM();
124
0
}
125
/* }}} */
126
127
static bool is_protected_variable(char *varname) /* {{{ */
128
0
{
129
0
  normalize_protected_variable(varname);
130
0
  return zend_hash_str_exists(&PG(rfc1867_protected_variables), varname, strlen(varname));
131
0
}
132
/* }}} */
133
134
static bool safe_php_register_variable(char *var, char *strval, size_t val_len, zval *track_vars_array, bool override_protection) /* {{{ */
135
0
{
136
0
  if (override_protection || !is_protected_variable(var)) {
137
0
    php_register_variable_safe(var, strval, val_len, track_vars_array);
138
0
  }
139
0
  return !ZEND_HAS_OOM();
140
0
}
141
/* }}} */
142
143
static bool safe_php_register_variable_ex(char *var, zval *val, zval *track_vars_array, bool override_protection) /* {{{ */
144
0
{
145
0
  if (override_protection || !is_protected_variable(var)) {
146
0
    php_register_variable_ex(var, val, track_vars_array);
147
0
  }
148
0
  return !ZEND_HAS_OOM();
149
0
}
150
/* }}} */
151
152
static bool register_http_post_files_variable(char *strvar, char *val, zval *http_post_files, bool override_protection) /* {{{ */
153
0
{
154
0
  return safe_php_register_variable(strvar, val, strlen(val), http_post_files, override_protection);
155
0
}
156
/* }}} */
157
158
static bool register_http_post_files_variable_ex(char *var, zval *val, zval *http_post_files, bool override_protection) /* {{{ */
159
0
{
160
0
  return safe_php_register_variable_ex(var, val, http_post_files, override_protection);
161
0
}
162
/* }}} */
163
164
0
static void free_filename(zval *el) {
165
0
  zend_string *filename = Z_STR_P(el);
166
0
  zend_string_release_ex(filename, 0);
167
0
}
168
169
PHPAPI void destroy_uploaded_files_hash(void) /* {{{ */
170
0
{
171
0
  zval *el;
172
173
0
  ZEND_HASH_MAP_FOREACH_VAL(SG(rfc1867_uploaded_files), el) {
174
0
    zend_string *filename = Z_STR_P(el);
175
0
    VCWD_UNLINK(ZSTR_VAL(filename));
176
0
  } ZEND_HASH_FOREACH_END();
177
0
  zend_hash_destroy(SG(rfc1867_uploaded_files));
178
0
  FREE_HASHTABLE(SG(rfc1867_uploaded_files));
179
0
  SG(rfc1867_uploaded_files) = NULL;
180
0
}
181
/* }}} */
182
183
/* {{{ Following code is based on apache_multipart_buffer.c from libapreq-0.33 package. */
184
185
0
#define FILLUNIT (1024 * 5)
186
187
typedef struct {
188
189
  /* read buffer */
190
  char *buffer;
191
  char *buf_begin;
192
  int  bufsize;
193
  int  bytes_in_buffer;
194
195
  /* boundary info */
196
  char *boundary;
197
  char *boundary_next;
198
  int  boundary_next_len;
199
200
  const zend_encoding *input_encoding;
201
  const zend_encoding **detect_order;
202
  size_t detect_order_size;
203
} multipart_buffer;
204
205
typedef struct {
206
  char *key;
207
  char *value;
208
} mime_header_entry;
209
210
/*
211
 * Fill up the buffer with client data.
212
 * Returns number of bytes added to buffer.
213
 */
214
static int fill_buffer(multipart_buffer *self)
215
0
{
216
0
  int bytes_to_read, total_read = 0, actual_read = 0;
217
218
  /* shift the existing data if necessary */
219
0
  if (self->bytes_in_buffer > 0 && self->buf_begin != self->buffer) {
220
0
    memmove(self->buffer, self->buf_begin, self->bytes_in_buffer);
221
0
  }
222
223
0
  self->buf_begin = self->buffer;
224
225
0
  zend_long post_max_size = REQUEST_PARSE_BODY_OPTION_GET(post_max_size, SG(post_max_size));
226
0
  if (post_max_size > 0 && SG(read_post_bytes) > post_max_size) {
227
0
    return 0;
228
0
  }
229
230
  /* calculate the free space in the buffer */
231
0
  bytes_to_read = self->bufsize - self->bytes_in_buffer;
232
233
  /* read the required number of bytes */
234
0
  while (bytes_to_read > 0) {
235
236
0
    char *buf = self->buffer + self->bytes_in_buffer;
237
238
0
    actual_read = (int)sapi_module.read_post(buf, bytes_to_read);
239
240
    /* update the buffer length */
241
0
    if (actual_read > 0) {
242
0
      self->bytes_in_buffer += actual_read;
243
0
      SG(read_post_bytes) += actual_read;
244
0
      total_read += actual_read;
245
0
      bytes_to_read -= actual_read;
246
0
    } else {
247
0
      break;
248
0
    }
249
0
  }
250
251
0
  return total_read;
252
0
}
253
254
/* eof if we are out of bytes, or if we hit the final boundary */
255
static int multipart_buffer_eof(multipart_buffer *self)
256
0
{
257
0
  return self->bytes_in_buffer == 0 && fill_buffer(self) < 1;
258
0
}
259
260
/* create new multipart_buffer structure */
261
static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len)
262
0
{
263
0
  multipart_buffer *self = (multipart_buffer *) ecalloc(1, sizeof(multipart_buffer));
264
265
0
  int minsize = boundary_len + 6;
266
0
  if (minsize < FILLUNIT) minsize = FILLUNIT;
267
268
0
  self->buffer = (char *) ecalloc(1, minsize + 1);
269
0
  self->bufsize = minsize;
270
271
0
  spprintf(&self->boundary, 0, "--%s", boundary);
272
273
0
  self->boundary_next_len = (int)spprintf(&self->boundary_next, 0, "\n--%s", boundary);
274
275
0
  self->buf_begin = self->buffer;
276
0
  self->bytes_in_buffer = 0;
277
278
0
  if (php_rfc1867_encoding_translation()) {
279
0
    php_rfc1867_get_detect_order(&self->detect_order, &self->detect_order_size);
280
0
  } else {
281
0
    self->detect_order = NULL;
282
0
    self->detect_order_size = 0;
283
0
  }
284
285
0
  self->input_encoding = NULL;
286
287
0
  return self;
288
0
}
289
290
/*
291
 * Gets the next CRLF terminated line from the input buffer.
292
 * If it doesn't find a CRLF, and the buffer isn't completely full, returns
293
 * NULL; otherwise, returns the beginning of the null-terminated line,
294
 * minus the CRLF.
295
 *
296
 * Note that we really just look for LF terminated lines. This works
297
 * around a bug in internet explorer for the macintosh which sends mime
298
 * boundaries that are only LF terminated when you use an image submit
299
 * button in a multipart/form-data form.
300
 */
301
static char *next_line(multipart_buffer *self)
302
0
{
303
  /* look for LF in the data */
304
0
  char* line = self->buf_begin;
305
0
  char* ptr = memchr(self->buf_begin, '\n', self->bytes_in_buffer);
306
307
0
  if (ptr) { /* LF found */
308
309
    /* terminate the string, remove CRLF */
310
0
    if ((ptr - line) > 0 && *(ptr-1) == '\r') {
311
0
      *(ptr-1) = 0;
312
0
    } else {
313
0
      *ptr = 0;
314
0
    }
315
316
    /* bump the pointer */
317
0
    self->buf_begin = ptr + 1;
318
0
    self->bytes_in_buffer -= (self->buf_begin - line);
319
320
0
  } else { /* no LF found */
321
322
    /* buffer isn't completely full, fail */
323
0
    if (self->bytes_in_buffer < self->bufsize) {
324
0
      return NULL;
325
0
    }
326
    /* return entire buffer as a partial line */
327
0
    line[self->bufsize] = 0;
328
0
    self->bytes_in_buffer = 0;
329
    /* Let fill_buffer() handle the reset of self->buf_begin */
330
0
  }
331
332
0
  return line;
333
0
}
334
335
/* Returns the next CRLF terminated line from the client */
336
static char *get_line(multipart_buffer *self)
337
0
{
338
0
  char* ptr = next_line(self);
339
340
0
  if (!ptr) {
341
0
    fill_buffer(self);
342
0
    ptr = next_line(self);
343
0
  }
344
345
0
  return ptr;
346
0
}
347
348
/* Free header entry */
349
static void php_free_hdr_entry(mime_header_entry *h)
350
0
{
351
0
  if (h->key) {
352
0
    efree(h->key);
353
0
  }
354
0
  if (h->value) {
355
0
    efree(h->value);
356
0
  }
357
0
}
358
359
/* finds a boundary */
360
static int find_boundary(multipart_buffer *self, char *boundary)
361
0
{
362
0
  char *line;
363
364
  /* loop through lines */
365
0
  while( (line = get_line(self)) )
366
0
  {
367
    /* finished if we found the boundary */
368
0
    if (!strcmp(line, boundary)) {
369
0
      return 1;
370
0
    }
371
0
  }
372
373
  /* didn't find the boundary */
374
0
  return 0;
375
0
}
376
377
static bool php_mime_should_save_hdr(const char *name)
378
0
{
379
0
  return !strcasecmp(name, "Content-Disposition")
380
0
    || !strcasecmp(name, "Content-Type");
381
0
}
382
383
static char *php_mime_get_hdr_value(zend_llist header, char *key);
384
385
/* parse headers */
386
static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header)
387
0
{
388
0
  char *line;
389
0
  mime_header_entry entry = {0};
390
0
  smart_string buf_value = {0};
391
0
  char *key = NULL;
392
393
  /* didn't find boundary, abort */
394
0
  if (!find_boundary(self, self->boundary)) {
395
0
    return 0;
396
0
  }
397
398
  /* get lines of text, or CRLF_CRLF */
399
400
0
  while ((line = get_line(self)) && line[0] != '\0') {
401
    /* add header to table */
402
0
    char *value = NULL;
403
404
0
    if (php_rfc1867_encoding_translation()) {
405
0
      self->input_encoding = zend_multibyte_encoding_detector((const unsigned char *) line, strlen(line), self->detect_order, self->detect_order_size);
406
0
    }
407
408
    /* space in the beginning means same header */
409
0
    if (!isspace((unsigned char)line[0])) {
410
0
      value = strchr(line, ':');
411
0
    }
412
413
0
    if (value) {
414
0
      if (buf_value.c && key) {
415
        /* new entry, add the old one to the list */
416
0
        smart_string_0(&buf_value);
417
0
        entry.key = key;
418
0
        entry.value = buf_value.c;
419
0
        zend_llist_add_element(header, &entry);
420
0
        buf_value.c = NULL;
421
0
        key = NULL;
422
0
      }
423
424
0
      *value = '\0';
425
426
      /* Keep only headers we use, and ignore duplicates */
427
0
      if (!php_mime_should_save_hdr(line)
428
0
          || php_mime_get_hdr_value(*header, line)) {
429
0
        key = NULL;
430
0
      } else {
431
0
        key = estrdup(line);
432
0
        do { value++; } while (isspace((unsigned char)*value));
433
0
        smart_string_appends(&buf_value, value);
434
0
      }
435
0
    } else if (buf_value.c) { /* If no ':' on the line, add to previous line */
436
0
      smart_string_appends(&buf_value, line);
437
0
    } else {
438
0
      continue;
439
0
    }
440
441
0
    if (ZEND_HAS_OOM()) {
442
0
      goto oom;
443
0
    }
444
0
  }
445
446
0
  if (buf_value.c && key) {
447
    /* add the last one to the list */
448
0
    smart_string_0(&buf_value);
449
0
    entry.key = key;
450
0
    entry.value = buf_value.c;
451
0
    zend_llist_add_element(header, &entry);
452
0
  }
453
454
0
  return 1;
455
456
0
oom:
457
0
  if (key) {
458
0
    efree(key);
459
0
  }
460
0
  smart_string_free(&buf_value);
461
0
  return 0;
462
0
}
463
464
static char *php_mime_get_hdr_value(zend_llist header, char *key)
465
0
{
466
0
  mime_header_entry *entry;
467
468
0
  if (key == NULL) {
469
0
    return NULL;
470
0
  }
471
472
0
  entry = zend_llist_get_first(&header);
473
0
  while (entry) {
474
0
    if (!strcasecmp(entry->key, key)) {
475
0
      return entry->value;
476
0
    }
477
0
    entry = zend_llist_get_next(&header);
478
0
  }
479
480
0
  return NULL;
481
0
}
482
483
static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop)
484
0
{
485
0
  char *pos = *line, quote;
486
0
  char *res;
487
488
0
  while (*pos && *pos != stop) {
489
0
    if ((quote = *pos) == '"' || quote == '\'') {
490
0
      ++pos;
491
0
      while (*pos && *pos != quote) {
492
0
        if (*pos == '\\' && pos[1] && pos[1] == quote) {
493
0
          pos += 2;
494
0
        } else {
495
0
          ++pos;
496
0
        }
497
0
      }
498
0
      if (*pos) {
499
0
        ++pos;
500
0
      }
501
0
    } else ++pos;
502
0
  }
503
0
  if (*pos == '\0') {
504
0
    res = estrdup(*line);
505
0
    *line += strlen(*line);
506
0
    return res;
507
0
  }
508
509
0
  res = estrndup(*line, pos - *line);
510
511
0
  while (*pos == stop) {
512
0
    ++pos;
513
0
  }
514
515
0
  *line = pos;
516
0
  return res;
517
0
}
518
519
static char *substring_conf(char *start, size_t len, char quote)
520
0
{
521
0
  char *result = emalloc(len + 1);
522
0
  char *resp = result;
523
0
  size_t i;
524
525
0
  for (i = 0; i < len && start[i] != quote; ++i) {
526
0
    if (start[i] == '\\' && (start[i + 1] == '\\' || (quote && start[i + 1] == quote))) {
527
0
      *resp++ = start[++i];
528
0
    } else {
529
0
      *resp++ = start[i];
530
0
    }
531
0
  }
532
533
0
  *resp = '\0';
534
0
  return result;
535
0
}
536
537
static char *php_ap_getword_conf(const zend_encoding *encoding, char *str)
538
0
{
539
0
  while (*str && isspace((unsigned char)*str)) {
540
0
    ++str;
541
0
  }
542
543
0
  if (!*str) {
544
0
    return estrdup("");
545
0
  }
546
547
0
  if (*str == '"' || *str == '\'') {
548
0
    char quote = *str;
549
550
0
    str++;
551
0
    return substring_conf(str, strlen(str), quote);
552
0
  } else {
553
0
    char *strend = str;
554
555
0
    while (*strend && !isspace((unsigned char)*strend)) {
556
0
      ++strend;
557
0
    }
558
0
    return substring_conf(str, (size_t)(strend - str), 0);
559
0
  }
560
0
}
561
562
static char *php_ap_basename(const zend_encoding *encoding, char *path)
563
0
{
564
0
  char *s = strrchr(path, '\\');
565
0
  char *s2 = strrchr(path, '/');
566
567
0
  if (s && s2) {
568
0
    if (s > s2) {
569
0
      ++s;
570
0
    } else {
571
0
      s = ++s2;
572
0
    }
573
0
    return s;
574
0
  } else if (s) {
575
0
    return ++s;
576
0
  } else if (s2) {
577
0
    return ++s2;
578
0
  }
579
0
  return path;
580
0
}
581
582
/*
583
 * Search for a string in a fixed-length byte string.
584
 * If partial is true, partial matches are allowed at the end of the buffer.
585
 * Returns NULL if not found, or a pointer to the start of the first match.
586
 */
587
static void *php_ap_memstr(char *haystack, int haystacklen, char *needle, int needlen, int partial)
588
0
{
589
0
  int len = haystacklen;
590
0
  char *ptr = haystack;
591
592
  /* iterate through first character matches */
593
0
  while( (ptr = memchr(ptr, needle[0], len)) ) {
594
595
    /* calculate length after match */
596
0
    len = haystacklen - (ptr - (char *)haystack);
597
598
    /* done if matches up to capacity of buffer */
599
0
    if (memcmp(needle, ptr, needlen < len ? needlen : len) == 0 && (partial || len >= needlen)) {
600
0
      break;
601
0
    }
602
603
    /* next character */
604
0
    ptr++; len--;
605
0
  }
606
607
0
  return ptr;
608
0
}
609
610
/* read until a boundary condition */
611
static size_t multipart_buffer_read(multipart_buffer *self, char *buf, size_t bytes, int *end)
612
0
{
613
0
  size_t len, max;
614
0
  char *bound;
615
616
  /* fill buffer if needed */
617
0
  if (bytes > (size_t)self->bytes_in_buffer) {
618
0
    fill_buffer(self);
619
0
  }
620
621
  /* look for a potential boundary match, only read data up to that point */
622
0
  if ((bound = php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 1))) {
623
0
    max = bound - self->buf_begin;
624
0
    if (end && php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 0)) {
625
0
      *end = 1;
626
0
    }
627
0
  } else {
628
0
    max = self->bytes_in_buffer;
629
0
  }
630
631
  /* maximum number of bytes we are reading */
632
0
  len = max < bytes-1 ? max : bytes-1;
633
634
  /* if we read any data... */
635
0
  if (len > 0) {
636
637
    /* copy the data */
638
0
    memcpy(buf, self->buf_begin, len);
639
0
    buf[len] = 0;
640
641
0
    if (bound && len > 0 && buf[len-1] == '\r') {
642
0
      buf[--len] = 0;
643
0
    }
644
645
    /* update the buffer */
646
0
    self->bytes_in_buffer -= (int)len;
647
0
    self->buf_begin += len;
648
0
  }
649
650
0
  return len;
651
0
}
652
653
/*
654
  XXX: this is horrible memory-usage-wise, but we only expect
655
  to do this on small pieces of form data.
656
*/
657
static char *multipart_buffer_read_body(multipart_buffer *self, size_t *len)
658
0
{
659
0
  char buf[FILLUNIT], *out=NULL;
660
0
  size_t total_bytes=0, read_bytes=0;
661
662
0
  while((read_bytes = multipart_buffer_read(self, buf, sizeof(buf), NULL))) {
663
0
    char *new_out = safe_ferealloc(out, 1, total_bytes, read_bytes + 1, /* fallible */ true);
664
0
    if (!new_out) {
665
0
      break;
666
0
    }
667
0
    out = new_out;
668
0
    memcpy(out + total_bytes, buf, read_bytes);
669
0
    total_bytes += read_bytes;
670
0
  }
671
672
0
  if (out) {
673
0
    out[total_bytes] = '\0';
674
0
  }
675
0
  *len = total_bytes;
676
677
0
  return out;
678
0
}
679
/* }}} */
680
681
/*
682
 * The combined READER/HANDLER
683
 *
684
 */
685
686
SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)
687
0
{
688
0
  char *boundary, *s = NULL, *boundary_end = NULL, *start_arr = NULL, *array_index = NULL;
689
0
  char *lbuf = NULL, *abuf = NULL;
690
0
  zend_string *temp_filename = NULL;
691
0
  int boundary_len = 0, cancel_upload = 0, is_arr_upload = 0;
692
0
  size_t array_len = 0;
693
0
  int64_t total_bytes = 0, max_file_size = 0;
694
0
  int skip_upload = 0, anonymous_index = 0;
695
0
  HashTable *uploaded_files = NULL;
696
0
  multipart_buffer *mbuff;
697
0
  zval *array_ptr = (zval *) arg;
698
0
  bool throw_exceptions = SG(request_parse_body_context).throw_exceptions;
699
0
  int fd = -1;
700
0
  zend_llist header;
701
0
  void *event_extra_data = NULL;
702
0
  unsigned int llen = 0;
703
0
  zend_long upload_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_file_uploads, zend_ini_long_literal("max_file_uploads"));
704
0
  zend_long body_parts_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_multipart_body_parts, zend_ini_long_literal("max_multipart_body_parts"));
705
0
  zend_long post_max_size = REQUEST_PARSE_BODY_OPTION_GET(post_max_size, SG(post_max_size));
706
0
  zend_long max_input_vars = REQUEST_PARSE_BODY_OPTION_GET(max_input_vars, PG(max_input_vars));
707
0
  zend_long upload_max_filesize = REQUEST_PARSE_BODY_OPTION_GET(upload_max_filesize, PG(upload_max_filesize));
708
0
  const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding();
709
0
  php_rfc1867_getword_t getword;
710
0
  php_rfc1867_getword_conf_t getword_conf;
711
0
  php_rfc1867_basename_t _basename;
712
0
  zend_long count = 0;
713
714
0
#define EMIT_WARNING_OR_ERROR(...) do { \
715
0
    if (throw_exceptions) { \
716
0
      zend_throw_exception_ex(zend_ce_request_parse_body_exception, 0, __VA_ARGS__); \
717
0
    } else { \
718
0
      php_error_docref(NULL, E_WARNING, __VA_ARGS__); \
719
0
    } \
720
0
  } while (0)
721
722
0
  if (php_rfc1867_encoding_translation() && internal_encoding) {
723
0
    getword = php_rfc1867_getword;
724
0
    getword_conf = php_rfc1867_getword_conf;
725
0
    _basename = php_rfc1867_basename;
726
0
  } else {
727
0
    getword = php_ap_getword;
728
0
    getword_conf = php_ap_getword_conf;
729
0
    _basename = php_ap_basename;
730
0
  }
731
732
0
  if (post_max_size > 0 && SG(request_info).content_length > post_max_size) {
733
0
    EMIT_WARNING_OR_ERROR("POST Content-Length of " ZEND_LONG_FMT " bytes exceeds the limit of " ZEND_LONG_FMT " bytes", SG(request_info).content_length, post_max_size);
734
0
    return;
735
0
  }
736
737
0
  if (body_parts_cnt < 0) {
738
0
    body_parts_cnt = max_input_vars + upload_cnt;
739
0
  }
740
0
  int body_parts_limit = body_parts_cnt;
741
742
  /* Get the boundary */
743
0
  boundary = strstr(content_type_dup, "boundary");
744
0
  if (!boundary) {
745
0
    int content_type_len = (int)strlen(content_type_dup);
746
0
    char *content_type_lcase = estrndup(content_type_dup, content_type_len);
747
748
0
    zend_str_tolower(content_type_lcase, content_type_len);
749
0
    boundary = strstr(content_type_lcase, "boundary");
750
0
    if (boundary) {
751
0
      boundary = content_type_dup + (boundary - content_type_lcase);
752
0
    }
753
0
    efree(content_type_lcase);
754
0
  }
755
756
0
  if (!boundary || !(boundary = strchr(boundary, '='))) {
757
0
    EMIT_WARNING_OR_ERROR("Missing boundary in multipart/form-data POST data");
758
0
    return;
759
0
  }
760
761
0
  boundary++;
762
0
  boundary_len = (int)strlen(boundary);
763
764
0
  if (boundary[0] == '"') {
765
0
    boundary++;
766
0
    boundary_end = strchr(boundary, '"');
767
0
    if (!boundary_end) {
768
0
      EMIT_WARNING_OR_ERROR("Invalid boundary in multipart/form-data POST data");
769
0
      return;
770
0
    }
771
0
  } else {
772
    /* search for the end of the boundary */
773
0
    boundary_end = strpbrk(boundary, ",;");
774
0
  }
775
0
  if (boundary_end) {
776
0
    boundary_end[0] = '\0';
777
0
    boundary_len = boundary_end-boundary;
778
0
  }
779
780
  /* Boundaries larger than FILLUNIT-strlen("\r\n--") characters lead to
781
   * erroneous parsing */
782
0
  if (boundary_len > FILLUNIT-strlen("\r\n--")) {
783
0
    sapi_module.sapi_error(E_WARNING, "Boundary too large in multipart/form-data POST data");
784
0
    return;
785
0
  }
786
787
  /* Initialize the buffer */
788
0
  mbuff = multipart_buffer_new(boundary, boundary_len);
789
790
  /* Initialize $_FILES[] */
791
0
  zend_hash_init(&PG(rfc1867_protected_variables), 8, NULL, NULL, 0);
792
793
0
  ALLOC_HASHTABLE(uploaded_files);
794
0
  zend_hash_init(uploaded_files, 8, NULL, free_filename, 0);
795
0
  SG(rfc1867_uploaded_files) = uploaded_files;
796
797
0
  if (Z_TYPE(PG(http_globals)[TRACK_VARS_FILES]) != IS_ARRAY) {
798
    /* php_auto_globals_create_files() might have already done that */
799
0
    array_init(&PG(http_globals)[TRACK_VARS_FILES]);
800
0
  }
801
802
0
  zend_llist_init(&header, sizeof(mime_header_entry), (llist_dtor_func_t) php_free_hdr_entry, 0);
803
804
0
  if (php_rfc1867_callback != NULL) {
805
0
    multipart_event_start event_start;
806
807
0
    event_start.content_length = SG(request_info).content_length;
808
0
    if (php_rfc1867_callback(MULTIPART_EVENT_START, &event_start, &event_extra_data) == FAILURE) {
809
0
      goto fileupload_done;
810
0
    }
811
0
    if (ZEND_HAS_OOM()) {
812
0
      goto fileupload_done;
813
0
    }
814
0
  }
815
816
0
  while (!multipart_buffer_eof(mbuff))
817
0
  {
818
0
    char buff[FILLUNIT];
819
0
    char *cd = NULL, *param = NULL, *filename = NULL, *tmp = NULL;
820
0
    size_t blen = 0, wlen = 0;
821
0
    zend_off_t offset;
822
823
0
    zend_llist_clean(&header);
824
825
0
    if (ZEND_HAS_OOM()) {
826
      /* A previous body part has reached the soft memory limit: Stop here */
827
0
      goto fileupload_done;
828
0
    }
829
830
0
    if (!multipart_buffer_headers(mbuff, &header)) {
831
0
      goto fileupload_done;
832
0
    }
833
834
0
    if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) {
835
0
      char *pair = NULL;
836
0
      int end = 0;
837
838
0
      if (--body_parts_cnt < 0) {
839
0
        EMIT_WARNING_OR_ERROR("Multipart body parts limit exceeded %d. To increase the limit change max_multipart_body_parts in php.ini.", body_parts_limit);
840
0
        goto fileupload_done;
841
0
      }
842
843
0
      while (isspace((unsigned char)*cd)) {
844
0
        ++cd;
845
0
      }
846
847
0
      while (*cd && (pair = getword(mbuff->input_encoding, &cd, ';')))
848
0
      {
849
0
        char *key = NULL, *word = pair;
850
851
0
        if (ZEND_HAS_OOM()) {
852
0
          efree(word);
853
0
          break;
854
0
        }
855
856
0
        while (isspace((unsigned char)*cd)) {
857
0
          ++cd;
858
0
        }
859
860
0
        if (strchr(pair, '=')) {
861
0
          key = getword(mbuff->input_encoding, &pair, '=');
862
863
0
          if (ZEND_HAS_OOM()) {
864
0
            efree(word);
865
0
            efree(key);
866
0
            break;
867
0
          }
868
869
0
          if (!strcasecmp(key, "name")) {
870
0
            if (param) {
871
0
              efree(param);
872
0
            }
873
0
            param = getword_conf(mbuff->input_encoding, pair);
874
0
            if (mbuff->input_encoding && internal_encoding) {
875
0
              unsigned char *new_param;
876
0
              size_t new_param_len;
877
0
              if ((size_t)-1 != zend_multibyte_encoding_converter(&new_param, &new_param_len, (unsigned char *)param, strlen(param), internal_encoding, mbuff->input_encoding)) {
878
0
                efree(param);
879
0
                param = (char *)new_param;
880
0
              }
881
0
            }
882
0
          } else if (!strcasecmp(key, "filename")) {
883
0
            if (filename) {
884
0
              efree(filename);
885
0
            }
886
0
            filename = getword_conf(mbuff->input_encoding, pair);
887
0
            if (mbuff->input_encoding && internal_encoding) {
888
0
              unsigned char *new_filename;
889
0
              size_t new_filename_len;
890
0
              if ((size_t)-1 != zend_multibyte_encoding_converter(&new_filename, &new_filename_len, (unsigned char *)filename, strlen(filename), internal_encoding, mbuff->input_encoding)) {
891
0
                efree(filename);
892
0
                filename = (char *)new_filename;
893
0
              }
894
0
            }
895
0
          }
896
897
0
          if (ZEND_HAS_OOM()) {
898
0
            efree(word);
899
0
            efree(key);
900
0
            break;
901
0
          }
902
0
        }
903
0
        if (key) {
904
0
          efree(key);
905
0
        }
906
0
        efree(word);
907
0
      }
908
909
0
      if (ZEND_HAS_OOM()) {
910
0
        if (filename) {
911
0
          efree(filename);
912
0
        }
913
0
        if (param) {
914
0
          efree(param);
915
0
        }
916
0
        goto fileupload_done;
917
0
      }
918
919
      /* Normal form variable, safe to read all data into memory */
920
0
      if (!filename && param) {
921
0
        size_t value_len;
922
0
        char *value = multipart_buffer_read_body(mbuff, &value_len);
923
0
        size_t new_val_len; /* Dummy variable */
924
925
0
        if (ZEND_HAS_OOM()) {
926
0
          if (value) {
927
0
            efree(value);
928
0
          }
929
0
          efree(param);
930
0
          goto fileupload_done;
931
0
        }
932
933
0
        if (!value) {
934
0
          value = estrdup("");
935
0
          value_len = 0;
936
0
        }
937
938
0
        if (mbuff->input_encoding && internal_encoding) {
939
0
          unsigned char *new_value;
940
0
          size_t new_value_len;
941
0
          if ((size_t)-1 != zend_multibyte_encoding_converter(&new_value, &new_value_len, (unsigned char *)value, value_len, internal_encoding, mbuff->input_encoding)) {
942
0
            efree(value);
943
0
            value = (char *)new_value;
944
0
            value_len = new_value_len;
945
0
          }
946
0
        }
947
948
0
        if (++count <= max_input_vars && sapi_module.input_filter(PARSE_POST, param, &value, value_len, &new_val_len)) {
949
0
          if (ZEND_HAS_OOM()) {
950
0
            efree(param);
951
0
            efree(value);
952
0
            goto fileupload_done;
953
0
          }
954
955
0
          if (php_rfc1867_callback != NULL) {
956
0
            multipart_event_formdata event_formdata;
957
0
            size_t newlength = new_val_len;
958
959
0
            event_formdata.post_bytes_processed = SG(read_post_bytes);
960
0
            event_formdata.name = param;
961
0
            event_formdata.value = &value;
962
0
            event_formdata.length = new_val_len;
963
0
            event_formdata.newlength = &newlength;
964
0
            if (php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data) == FAILURE) {
965
0
              efree(param);
966
0
              efree(value);
967
0
              continue;
968
0
            }
969
0
            if (ZEND_HAS_OOM()) {
970
0
              efree(param);
971
0
              efree(value);
972
0
              goto fileupload_done;
973
0
            }
974
0
            new_val_len = newlength;
975
0
          }
976
0
          if (!safe_php_register_variable(param, value, new_val_len, array_ptr, 0)) {
977
0
            efree(param);
978
0
            efree(value);
979
0
            goto fileupload_done;
980
0
          }
981
0
        } else {
982
0
          if (count == max_input_vars + 1) {
983
0
            EMIT_WARNING_OR_ERROR("Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", max_input_vars);
984
0
          }
985
986
0
          if (php_rfc1867_callback != NULL) {
987
0
            multipart_event_formdata event_formdata;
988
989
0
            event_formdata.post_bytes_processed = SG(read_post_bytes);
990
0
            event_formdata.name = param;
991
0
            event_formdata.value = &value;
992
0
            event_formdata.length = value_len;
993
0
            event_formdata.newlength = NULL;
994
0
            php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data);
995
0
            if (ZEND_HAS_OOM()) {
996
0
              efree(param);
997
0
              efree(value);
998
0
              goto fileupload_done;
999
0
            }
1000
0
          }
1001
0
        }
1002
1003
0
        if (!strcasecmp(param, "MAX_FILE_SIZE")) {
1004
0
          max_file_size = strtoll(value, NULL, 10);
1005
0
        }
1006
1007
0
        efree(param);
1008
0
        efree(value);
1009
0
        continue;
1010
0
      }
1011
1012
      /* If file_uploads=off, skip the file part */
1013
0
      if (!PG(file_uploads)) {
1014
0
        skip_upload = 1;
1015
0
      } else if (upload_cnt <= 0) {
1016
0
        skip_upload = 1;
1017
0
        if (upload_cnt == 0) {
1018
0
          --upload_cnt;
1019
0
          EMIT_WARNING_OR_ERROR("Maximum number of allowable file uploads has been exceeded");
1020
0
        }
1021
0
      }
1022
1023
      /* Return with an error if the posted data is garbled */
1024
0
      if (!param && !filename) {
1025
0
        EMIT_WARNING_OR_ERROR("File Upload Mime headers garbled");
1026
0
        goto fileupload_done;
1027
0
      }
1028
1029
0
      if (!param) {
1030
0
        param = emalloc(MAX_SIZE_ANONNAME);
1031
0
        snprintf(param, MAX_SIZE_ANONNAME, "%u", anonymous_index++);
1032
0
      }
1033
1034
      /* New Rule: never repair potential malicious user input */
1035
0
      if (!skip_upload) {
1036
0
        long c = 0;
1037
0
        tmp = param;
1038
1039
0
        while (*tmp) {
1040
0
          if (*tmp == '[') {
1041
0
            c++;
1042
0
          } else if (*tmp == ']') {
1043
0
            c--;
1044
0
            if (tmp[1] && tmp[1] != '[') {
1045
0
              skip_upload = 1;
1046
0
              break;
1047
0
            }
1048
0
          }
1049
0
          if (c < 0) {
1050
0
            skip_upload = 1;
1051
0
            break;
1052
0
          }
1053
0
          tmp++;
1054
0
        }
1055
        /* Brackets should always be closed */
1056
0
        if(c != 0) {
1057
0
          skip_upload = 1;
1058
0
        }
1059
0
      }
1060
1061
0
      total_bytes = cancel_upload = 0;
1062
0
      temp_filename = NULL;
1063
0
      fd = -1;
1064
1065
0
      if (!skip_upload && php_rfc1867_callback != NULL) {
1066
0
        multipart_event_file_start event_file_start;
1067
1068
0
        event_file_start.post_bytes_processed = SG(read_post_bytes);
1069
0
        event_file_start.name = param;
1070
0
        event_file_start.filename = &filename;
1071
0
        if (php_rfc1867_callback(MULTIPART_EVENT_FILE_START, &event_file_start, &event_extra_data) == FAILURE) {
1072
0
          temp_filename = NULL;
1073
0
          efree(param);
1074
0
          efree(filename);
1075
0
          continue;
1076
0
        }
1077
0
        if (ZEND_HAS_OOM()) {
1078
0
          efree(param);
1079
0
          efree(filename);
1080
0
          goto fileupload_done;
1081
0
        }
1082
0
      }
1083
1084
0
      if (skip_upload) {
1085
0
        efree(param);
1086
0
        efree(filename);
1087
0
        continue;
1088
0
      }
1089
1090
0
      if (filename[0] == '\0') {
1091
#if DEBUG_FILE_UPLOAD
1092
        sapi_module.sapi_error(E_NOTICE, "No file uploaded");
1093
#endif
1094
0
        cancel_upload = PHP_UPLOAD_ERROR_D;
1095
0
      }
1096
1097
0
      offset = 0;
1098
0
      end = 0;
1099
1100
0
      if (!cancel_upload) {
1101
        /* only bother to open temp file if we have data */
1102
0
        blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end);
1103
#if DEBUG_FILE_UPLOAD
1104
        if (blen > 0) {
1105
#else
1106
        /* in non-debug mode we have no problem with 0-length files */
1107
0
        {
1108
0
#endif
1109
0
          fd = php_open_temporary_fd_ex(PG(upload_tmp_dir), "php", &temp_filename, PHP_TMP_FILE_OPEN_BASEDIR_CHECK_ON_FALLBACK);
1110
0
          upload_cnt--;
1111
0
          if (fd == -1) {
1112
0
            EMIT_WARNING_OR_ERROR("File upload error - unable to create a temporary file");
1113
0
            cancel_upload = PHP_UPLOAD_ERROR_E;
1114
0
          }
1115
0
        }
1116
0
      }
1117
1118
0
      while (!cancel_upload && (blen > 0))
1119
0
      {
1120
0
        if (php_rfc1867_callback != NULL) {
1121
0
          multipart_event_file_data event_file_data;
1122
1123
0
          event_file_data.post_bytes_processed = SG(read_post_bytes);
1124
0
          event_file_data.offset = offset;
1125
0
          event_file_data.data = buff;
1126
0
          event_file_data.length = blen;
1127
0
          event_file_data.newlength = &blen;
1128
0
          if (php_rfc1867_callback(MULTIPART_EVENT_FILE_DATA, &event_file_data, &event_extra_data) == FAILURE) {
1129
0
            cancel_upload = PHP_UPLOAD_ERROR_X;
1130
0
            continue;
1131
0
          }
1132
0
          if (ZEND_HAS_OOM()) {
1133
0
            cancel_upload = PHP_UPLOAD_ERROR_X;
1134
0
            continue;
1135
0
          }
1136
0
        }
1137
1138
0
        if (upload_max_filesize > 0 && (zend_long)(total_bytes+blen) > upload_max_filesize) {
1139
#if DEBUG_FILE_UPLOAD
1140
          sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of " ZEND_LONG_FMT " bytes exceeded - file [%s=%s] not saved", upload_max_filesize, param, filename);
1141
#endif
1142
0
          cancel_upload = PHP_UPLOAD_ERROR_A;
1143
0
        } else if (max_file_size && ((zend_long)(total_bytes+blen) > max_file_size)) {
1144
#if DEBUG_FILE_UPLOAD
1145
          sapi_module.sapi_error(E_NOTICE, "MAX_FILE_SIZE of %" PRId64 " bytes exceeded - file [%s=%s] not saved", max_file_size, param, filename);
1146
#endif
1147
0
          cancel_upload = PHP_UPLOAD_ERROR_B;
1148
0
        } else if (blen > 0) {
1149
#ifdef PHP_WIN32
1150
          wlen = write(fd, buff, (unsigned int)blen);
1151
#else
1152
0
          wlen = write(fd, buff, blen);
1153
0
#endif
1154
1155
0
          if (wlen == (size_t)-1) {
1156
            /* write failed */
1157
#if DEBUG_FILE_UPLOAD
1158
            char errstr[256];
1159
            sapi_module.sapi_error(E_NOTICE, "write() failed - %s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1160
#endif
1161
0
            cancel_upload = PHP_UPLOAD_ERROR_F;
1162
0
          } else if (wlen < blen) {
1163
#if DEBUG_FILE_UPLOAD
1164
            sapi_module.sapi_error(E_NOTICE, "Only %zd bytes were written, expected to write %zd", wlen, blen);
1165
#endif
1166
0
            cancel_upload = PHP_UPLOAD_ERROR_F;
1167
0
          } else {
1168
0
            total_bytes += wlen;
1169
0
          }
1170
0
          offset += wlen;
1171
0
        }
1172
1173
        /* read data for next iteration */
1174
0
        blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end);
1175
0
      }
1176
1177
0
      if (fd != -1) { /* may not be initialized if file could not be created */
1178
0
        close(fd);
1179
0
      }
1180
1181
0
      if (!cancel_upload && !end) {
1182
#if DEBUG_FILE_UPLOAD
1183
        sapi_module.sapi_error(E_NOTICE, "Missing mime boundary at the end of the data for file %s", filename[0] != '\0' ? filename : "");
1184
#endif
1185
0
        cancel_upload = PHP_UPLOAD_ERROR_C;
1186
0
      }
1187
#if DEBUG_FILE_UPLOAD
1188
      if (filename[0] != '\0' && total_bytes == 0 && !cancel_upload) {
1189
        EMIT_WARNING_OR_ERROR("Uploaded file size 0 - file [%s=%s] not saved", param, filename);
1190
        cancel_upload = 5;
1191
      }
1192
#endif
1193
0
      if (php_rfc1867_callback != NULL) {
1194
0
        multipart_event_file_end event_file_end;
1195
1196
0
        event_file_end.post_bytes_processed = SG(read_post_bytes);
1197
0
        event_file_end.temp_filename = temp_filename ? ZSTR_VAL(temp_filename) : NULL;
1198
0
        event_file_end.cancel_upload = cancel_upload;
1199
0
        if (php_rfc1867_callback(MULTIPART_EVENT_FILE_END, &event_file_end, &event_extra_data) == FAILURE) {
1200
0
          cancel_upload = PHP_UPLOAD_ERROR_X;
1201
0
        }
1202
0
        if (ZEND_HAS_OOM()) {
1203
0
          cancel_upload = PHP_UPLOAD_ERROR_X;
1204
0
        }
1205
0
      }
1206
1207
0
      if (cancel_upload) {
1208
0
        if (temp_filename) {
1209
0
          if (cancel_upload != PHP_UPLOAD_ERROR_E) { /* file creation failed */
1210
0
            unlink(ZSTR_VAL(temp_filename));
1211
0
          }
1212
0
          zend_string_release_ex(temp_filename, 0);
1213
0
        }
1214
0
        temp_filename = NULL;
1215
0
      } else {
1216
0
        zend_hash_add_ptr(SG(rfc1867_uploaded_files), temp_filename, temp_filename);
1217
0
      }
1218
1219
0
      if (ZEND_HAS_OOM()) {
1220
0
        efree(filename);
1221
0
        efree(param);
1222
0
        goto fileupload_done;
1223
0
      }
1224
1225
      /* is_arr_upload is true when name of file upload field
1226
       * ends in [.*]
1227
       * start_arr is set to point to 1st [ */
1228
0
      is_arr_upload = (start_arr = strchr(param,'[')) && (param[strlen(param)-1] == ']');
1229
1230
0
      if (is_arr_upload) {
1231
0
        array_len = strlen(start_arr);
1232
0
        if (array_index) {
1233
0
          efree(array_index);
1234
0
        }
1235
0
        array_index = estrndup(start_arr + 1, array_len - 2);
1236
1237
0
        if (ZEND_HAS_OOM()) {
1238
0
          efree(filename);
1239
0
          efree(param);
1240
0
          goto fileupload_done;
1241
0
        }
1242
0
      }
1243
1244
      /* Add $foo_name */
1245
0
      if (llen < strlen(param) + MAX_SIZE_OF_INDEX + 1) {
1246
0
        unsigned int new_llen = (int)strlen(param);
1247
0
        char *new_lbuf = (char *) safe_ferealloc(lbuf, new_llen, 1, MAX_SIZE_OF_INDEX + 1, /* fallible */ true);
1248
0
        if (!new_lbuf) {
1249
0
          efree(filename);
1250
0
          efree(param);
1251
0
          goto fileupload_done;
1252
0
        }
1253
0
        llen = new_llen;
1254
0
        lbuf = new_lbuf;
1255
0
        llen += MAX_SIZE_OF_INDEX + 1;
1256
0
      }
1257
1258
0
      if (is_arr_upload) {
1259
0
        if (abuf) efree(abuf);
1260
0
        abuf = estrndup(param, strlen(param)-array_len);
1261
0
        if (ZEND_HAS_OOM()) {
1262
0
          efree(filename);
1263
0
          efree(param);
1264
0
          goto fileupload_done;
1265
0
        }
1266
0
        snprintf(lbuf, llen, "%s_name[%s]", abuf, array_index);
1267
0
      } else {
1268
0
        snprintf(lbuf, llen, "%s_name", param);
1269
0
      }
1270
1271
      /* Pursuant to RFC 7578, strip any path components in the
1272
       * user-supplied file name:
1273
       *  > If a "filename" parameter is supplied ... do not use
1274
       *  > directory path information that may be present."
1275
       */
1276
0
      s = _basename(internal_encoding, filename);
1277
0
      if (!s) {
1278
0
        s = filename;
1279
0
      }
1280
1281
      /* Add $foo[name] */
1282
0
      if (is_arr_upload) {
1283
0
        snprintf(lbuf, llen, "%s[name][%s]", abuf, array_index);
1284
0
      } else {
1285
0
        snprintf(lbuf, llen, "%s[name]", param);
1286
0
      }
1287
0
      if (!register_http_post_files_variable(lbuf, s, &PG(http_globals)[TRACK_VARS_FILES], 0)) {
1288
0
        goto oom_with_filename;
1289
0
      }
1290
0
      s = NULL;
1291
1292
      /* Add full path of supplied file for folder uploads via
1293
       * <input type="file" name="files" multiple webkitdirectory>
1294
       */
1295
      /* Add $foo[full_path] */
1296
0
      if (is_arr_upload) {
1297
0
        snprintf(lbuf, llen, "%s[full_path][%s]", abuf, array_index);
1298
0
      } else {
1299
0
        snprintf(lbuf, llen, "%s[full_path]", param);
1300
0
      }
1301
0
      if (!register_http_post_files_variable(lbuf, filename, &PG(http_globals)[TRACK_VARS_FILES], 0)) {
1302
0
        goto oom_with_filename;
1303
0
      }
1304
0
      efree(filename);
1305
1306
      /* Possible Content-Type: */
1307
0
      if (cancel_upload || !(cd = php_mime_get_hdr_value(header, "Content-Type"))) {
1308
0
        cd = "";
1309
0
      } else {
1310
        /* fix for Opera 6.01 */
1311
0
        s = strchr(cd, ';');
1312
0
        if (s != NULL) {
1313
0
          *s = '\0';
1314
0
        }
1315
0
      }
1316
1317
      /* Add $foo[type] */
1318
0
      if (is_arr_upload) {
1319
0
        snprintf(lbuf, llen, "%s[type][%s]", abuf, array_index);
1320
0
      } else {
1321
0
        snprintf(lbuf, llen, "%s[type]", param);
1322
0
      }
1323
0
      if (!register_http_post_files_variable(lbuf, cd, &PG(http_globals)[TRACK_VARS_FILES], 0)) {
1324
0
        goto oom_with_param;
1325
0
      }
1326
1327
      /* Restore Content-Type Header */
1328
0
      if (s != NULL) {
1329
0
        *s = ';';
1330
0
      }
1331
0
      s = "";
1332
1333
0
      {
1334
        /* store temp_filename as-is (in case upload_tmp_dir
1335
         * contains escapable characters. escape only the variable name.) */
1336
0
        zval zfilename;
1337
1338
        /* Initialize variables */
1339
0
        if (!add_protected_variable(param)) {
1340
0
          goto oom_with_param;
1341
0
        }
1342
1343
        /* Add $foo[tmp_name] */
1344
0
        if (is_arr_upload) {
1345
0
          snprintf(lbuf, llen, "%s[tmp_name][%s]", abuf, array_index);
1346
0
        } else {
1347
0
          snprintf(lbuf, llen, "%s[tmp_name]", param);
1348
0
        }
1349
0
        if (!add_protected_variable(lbuf)) {
1350
0
          goto oom_with_param;
1351
0
        }
1352
0
        if (temp_filename) {
1353
0
          ZVAL_STR_COPY(&zfilename, temp_filename);
1354
0
        } else {
1355
0
          ZVAL_EMPTY_STRING(&zfilename);
1356
0
        }
1357
0
        if (!register_http_post_files_variable_ex(lbuf, &zfilename, &PG(http_globals)[TRACK_VARS_FILES], 1)) {
1358
0
          goto oom_with_param;
1359
0
        }
1360
0
      }
1361
1362
0
      {
1363
0
        zval file_size, error_type;
1364
0
        int size_overflow = 0;
1365
0
        char file_size_buf[65];
1366
1367
0
        ZVAL_LONG(&error_type, cancel_upload);
1368
1369
        /* Add $foo[error] */
1370
0
        if (cancel_upload) {
1371
0
          ZVAL_LONG(&file_size, 0);
1372
0
        } else {
1373
0
          if (total_bytes > ZEND_LONG_MAX) {
1374
#ifdef PHP_WIN32
1375
            if (_i64toa_s(total_bytes, file_size_buf, 65, 10)) {
1376
              file_size_buf[0] = '0';
1377
              file_size_buf[1] = '\0';
1378
            }
1379
#else
1380
0
            {
1381
0
              int __len = snprintf(file_size_buf, 65, "%" PRId64, total_bytes);
1382
0
              file_size_buf[__len] = '\0';
1383
0
            }
1384
0
#endif
1385
0
            size_overflow = 1;
1386
1387
0
          } else {
1388
0
            ZVAL_LONG(&file_size, total_bytes);
1389
0
          }
1390
0
        }
1391
1392
0
        if (is_arr_upload) {
1393
0
          snprintf(lbuf, llen, "%s[error][%s]", abuf, array_index);
1394
0
        } else {
1395
0
          snprintf(lbuf, llen, "%s[error]", param);
1396
0
        }
1397
0
        if (!register_http_post_files_variable_ex(lbuf, &error_type, &PG(http_globals)[TRACK_VARS_FILES], 0)) {
1398
0
          goto oom_with_param;
1399
0
        }
1400
1401
        /* Add $foo[size] */
1402
0
        if (is_arr_upload) {
1403
0
          snprintf(lbuf, llen, "%s[size][%s]", abuf, array_index);
1404
0
        } else {
1405
0
          snprintf(lbuf, llen, "%s[size]", param);
1406
0
        }
1407
0
        if (size_overflow) {
1408
0
          ZVAL_STRING(&file_size, file_size_buf);
1409
0
        }
1410
0
        if (!register_http_post_files_variable_ex(lbuf, &file_size, &PG(http_globals)[TRACK_VARS_FILES], size_overflow)) {
1411
0
          goto oom_with_param;
1412
0
        }
1413
0
      }
1414
0
      efree(param);
1415
0
      continue;
1416
1417
0
oom_with_filename:
1418
0
      efree(filename);
1419
0
oom_with_param:
1420
0
      efree(param);
1421
0
      goto fileupload_done;
1422
0
    }
1423
0
  }
1424
1425
0
fileupload_done:
1426
0
  if (post_max_size > 0 && SG(read_post_bytes) > post_max_size) {
1427
0
    sapi_module.sapi_error(E_WARNING, "Actual POST length does not match Content-Length, and exceeds " ZEND_LONG_FMT " bytes", post_max_size);
1428
0
  }
1429
0
  if (php_rfc1867_callback != NULL) {
1430
0
    multipart_event_end event_end;
1431
1432
0
    event_end.post_bytes_processed = SG(read_post_bytes);
1433
0
    php_rfc1867_callback(MULTIPART_EVENT_END, &event_end, &event_extra_data);
1434
0
  }
1435
1436
0
  if (lbuf) efree(lbuf);
1437
0
  if (abuf) efree(abuf);
1438
0
  if (array_index) efree(array_index);
1439
0
  zend_hash_destroy(&PG(rfc1867_protected_variables));
1440
0
  zend_llist_destroy(&header);
1441
0
  if (mbuff->boundary_next) efree(mbuff->boundary_next);
1442
0
  if (mbuff->boundary) efree(mbuff->boundary);
1443
0
  if (mbuff->buffer) efree(mbuff->buffer);
1444
0
  if (mbuff) efree(mbuff);
1445
1446
0
#undef EMIT_WARNING_OR_ERROR
1447
0
}
1448
/* }}} */
1449
1450
SAPI_API void php_rfc1867_set_multibyte_callbacks(
1451
          php_rfc1867_encoding_translation_t encoding_translation,
1452
          php_rfc1867_get_detect_order_t get_detect_order,
1453
          php_rfc1867_set_input_encoding_t set_input_encoding,
1454
          php_rfc1867_getword_t getword,
1455
          php_rfc1867_getword_conf_t getword_conf,
1456
          php_rfc1867_basename_t basename) /* {{{ */
1457
0
{
1458
0
  php_rfc1867_encoding_translation = encoding_translation;
1459
0
  php_rfc1867_get_detect_order = get_detect_order;
1460
0
  php_rfc1867_set_input_encoding = set_input_encoding;
1461
0
  php_rfc1867_getword = getword;
1462
0
  php_rfc1867_getword_conf = getword_conf;
1463
0
  php_rfc1867_basename = basename;
1464
0
}
1465
/* }}} */