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