xref: /PHP-5.3/main/rfc1867.c (revision a2045ff3)
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