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