PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - var/php_gcov/PHP_HEAD/main - rfc1867.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 467
Code covered: 88.0 % Executed lines: 411
Legend: not executed executed

       1                 : /*
       2                 :    +----------------------------------------------------------------------+
       3                 :    | PHP Version 6                                                        |
       4                 :    +----------------------------------------------------------------------+
       5                 :    | Copyright (c) 1997-2009 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 <sniper@php.net>                              |
      17                 :    +----------------------------------------------------------------------+
      18                 :  */
      19                 : 
      20                 : /* $Id: rfc1867.c 290885 2009-11-17 20:33:51Z rasmus $ */
      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                 : 
      36                 : #define DEBUG_FILE_UPLOAD ZEND_DEBUG
      37                 : 
      38                 : PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra TSRMLS_DC) = NULL;
      39                 : 
      40                 : #define SAFE_RETURN { \
      41                 :         if (lbuf) efree(lbuf); \
      42                 :         if (abuf) efree(abuf); \
      43                 :         if (array_index) efree(array_index); \
      44                 :         zend_llist_destroy(&header); \
      45                 :         if (mbuff->boundary_next) efree(mbuff->boundary_next); \
      46                 :         if (mbuff->boundary) efree(mbuff->boundary); \
      47                 :         if (mbuff->buffer) efree(mbuff->buffer); \
      48                 :         if (mbuff) efree(mbuff); \
      49                 :         return; }
      50                 : 
      51                 : /* The longest property name we use in an uploaded file array */
      52                 : #define MAX_SIZE_OF_INDEX sizeof("[tmp_name]")
      53                 : 
      54                 : /* The longest anonymous name */
      55                 : #define MAX_SIZE_ANONNAME 33
      56                 : 
      57                 : /* Errors */
      58                 : #define UPLOAD_ERROR_OK   0  /* File upload succesful */
      59                 : #define UPLOAD_ERROR_A    1  /* Uploaded file exceeded upload_max_filesize */
      60                 : #define UPLOAD_ERROR_B    2  /* Uploaded file exceeded MAX_FILE_SIZE */
      61                 : #define UPLOAD_ERROR_C    3  /* Partially uploaded */
      62                 : #define UPLOAD_ERROR_D    4  /* No file uploaded */
      63                 : #define UPLOAD_ERROR_E    6  /* Missing /tmp or similar directory */
      64                 : #define UPLOAD_ERROR_F    7  /* Failed to write file to disk */
      65                 : #define UPLOAD_ERROR_X    8  /* File upload stopped by extension */
      66                 : 
      67                 : void php_rfc1867_register_constants(TSRMLS_D)
      68           17007 : {
      69           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_OK",         UPLOAD_ERROR_OK, CONST_CS | CONST_PERSISTENT);
      70           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_INI_SIZE",   UPLOAD_ERROR_A,  CONST_CS | CONST_PERSISTENT);
      71           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_FORM_SIZE",  UPLOAD_ERROR_B,  CONST_CS | CONST_PERSISTENT);
      72           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_PARTIAL",    UPLOAD_ERROR_C,  CONST_CS | CONST_PERSISTENT);
      73           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_FILE",    UPLOAD_ERROR_D,  CONST_CS | CONST_PERSISTENT);
      74           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_TMP_DIR", UPLOAD_ERROR_E,  CONST_CS | CONST_PERSISTENT);
      75           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_CANT_WRITE", UPLOAD_ERROR_F,  CONST_CS | CONST_PERSISTENT);
      76           17007 :         REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_EXTENSION",  UPLOAD_ERROR_X,  CONST_CS | CONST_PERSISTENT);
      77           17007 : }
      78                 : 
      79                 : static int unlink_filename(char **filename TSRMLS_DC)
      80              36 : {
      81              36 :         VCWD_UNLINK(*filename);
      82              36 :         return 0;
      83                 : }
      84                 : 
      85                 : void destroy_uploaded_files_hash(TSRMLS_D)
      86              24 : {
      87              24 :         zend_hash_apply(SG(rfc1867_uploaded_files), (apply_func_t) unlink_filename TSRMLS_CC);
      88              24 :         zend_hash_destroy(SG(rfc1867_uploaded_files));
      89              24 :         FREE_HASHTABLE(SG(rfc1867_uploaded_files));
      90              24 : }
      91                 : 
      92                 : /*
      93                 :  *  Following code is based on apache_multipart_buffer.c from libapreq-0.33 package.
      94                 :  *
      95                 :  */
      96                 : 
      97                 : #define FILLUNIT (1024 * 5)
      98                 : 
      99                 : typedef struct {
     100                 : 
     101                 :         /* read buffer */
     102                 :         char *buffer;
     103                 :         char *buf_begin;
     104                 :         int  bufsize;
     105                 :         int  bytes_in_buffer;
     106                 : 
     107                 :         /* boundary info */
     108                 :         char *boundary;
     109                 :         char *boundary_next;
     110                 :         int  boundary_next_len;
     111                 : 
     112                 : } multipart_buffer;
     113                 : 
     114                 : typedef struct {
     115                 :         char *key;
     116                 :         char *value;
     117                 : } mime_header_entry;
     118                 : 
     119                 : /*
     120                 :   fill up the buffer with client data.
     121                 :   returns number of bytes added to buffer.
     122                 : */
     123                 : static int fill_buffer(multipart_buffer *self TSRMLS_DC)
     124             174 : {
     125             174 :         int bytes_to_read, total_read = 0, actual_read = 0;
     126                 :         static zend_bool done = 0;
     127                 : 
     128                 :         /* shift the existing data if necessary */
     129             174 :         if (self->bytes_in_buffer > 0 && self->buf_begin != self->buffer) {
     130             148 :                 memmove(self->buffer, self->buf_begin, self->bytes_in_buffer);
     131                 :         }
     132                 : 
     133             174 :         self->buf_begin = self->buffer;
     134                 : 
     135                 :         /* calculate the free space in the buffer */
     136             174 :         bytes_to_read = self->bufsize - self->bytes_in_buffer;
     137                 : 
     138                 :         /* read the required number of bytes */
     139             372 :         while (bytes_to_read > 0) {
     140                 : 
     141             198 :                 char *buf = self->buffer + self->bytes_in_buffer;
     142                 : 
     143             198 :                 actual_read = sapi_module.read_post(buf, bytes_to_read TSRMLS_CC);
     144                 : 
     145                 :                 /* update the buffer length */
     146             198 :                 if (actual_read > 0) {
     147              24 :                         self->bytes_in_buffer += actual_read;
     148              24 :                         SG(read_post_bytes) += actual_read;
     149              24 :                         total_read += actual_read;
     150              24 :                         bytes_to_read -= actual_read;
     151                 :                 } else {
     152             174 :                         if (!done) {
     153                 : #ifdef DEBUG_FILE_UPLOAD_INTENSIVE
     154                 :                                 fprintf(stderr, "\n###################\n%s\n#################\n", self->buffer);
     155                 : #endif
     156              24 :                                 done = 1;
     157                 :                         }
     158             174 :                         break;
     159                 :                 }
     160                 :         }
     161                 : 
     162             174 :         return total_read;
     163                 : }
     164                 : 
     165                 : /* eof if we are out of bytes, or if we hit the final boundary */
     166                 : static int multipart_buffer_eof(multipart_buffer *self TSRMLS_DC)
     167              92 : {
     168              92 :         if ( (self->bytes_in_buffer == 0 && fill_buffer(self TSRMLS_CC) < 1) ) {
     169               1 :                 return 1;
     170                 :         } else {
     171              91 :                 return 0;
     172                 :         }
     173                 : }
     174                 : 
     175                 : /* create new multipart_buffer structure */
     176                 : static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len)
     177              24 : {
     178              24 :         multipart_buffer *self = (multipart_buffer *) ecalloc(1, sizeof(multipart_buffer));
     179                 : 
     180              24 :         int minsize = boundary_len + 6;
     181              24 :         if (minsize < FILLUNIT) minsize = FILLUNIT;
     182                 : 
     183              24 :         self->buffer = (char *) ecalloc(1, minsize + 1);
     184              24 :         self->bufsize = minsize;
     185                 : 
     186              24 :         spprintf(&self->boundary, 0, "--%s", boundary);
     187                 : 
     188              24 :         self->boundary_next_len = spprintf(&self->boundary_next, 0, "\n--%s", boundary);
     189                 : 
     190              24 :         self->buf_begin = self->buffer;
     191              24 :         self->bytes_in_buffer = 0;
     192                 : 
     193              24 :         return self;
     194                 : }
     195                 : 
     196                 : /*
     197                 :   gets the next CRLF terminated line from the input buffer.
     198                 :   if it doesn't find a CRLF, and the buffer isn't completely full, returns
     199                 :   NULL; otherwise, returns the beginning of the null-terminated line,
     200                 :   minus the CRLF.
     201                 : 
     202                 :   note that we really just look for LF terminated lines. this works
     203                 :   around a bug in internet explorer for the macintosh which sends mime
     204                 :   boundaries that are only LF terminated when you use an image submit
     205                 :   button in a multipart/form-data form.
     206                 :  */
     207                 : static char *next_line(multipart_buffer *self)
     208             340 : {
     209                 :         /* look for LF in the data */
     210             340 :         char* line = self->buf_begin;
     211             340 :         char* ptr = memchr(self->buf_begin, '\n', self->bytes_in_buffer);
     212                 : 
     213             340 :         if (ptr) {      /* LF found */
     214                 : 
     215                 :                 /* terminate the string, remove CRLF */
     216             296 :                 if ((ptr - line) > 0 && *(ptr-1) == '\r') {
     217               0 :                         *(ptr-1) = 0;
     218                 :                 } else {
     219             296 :                         *ptr = 0;
     220                 :                 }
     221                 : 
     222                 :                 /* bump the pointer */
     223             296 :                 self->buf_begin = ptr + 1;
     224             296 :                 self->bytes_in_buffer -= (self->buf_begin - line);
     225                 : 
     226                 :         } else {        /* no LF found */
     227                 : 
     228                 :                 /* buffer isn't completely full, fail */
     229              44 :                 if (self->bytes_in_buffer < self->bufsize) {
     230              44 :                         return NULL;
     231                 :                 }
     232                 :                 /* return entire buffer as a partial line */
     233               0 :                 line[self->bufsize] = 0;
     234               0 :                 self->buf_begin = ptr;
     235               0 :                 self->bytes_in_buffer = 0;
     236                 :         }
     237                 : 
     238             296 :         return line;
     239                 : }
     240                 : 
     241                 : /* returns the next CRLF terminated line from the client */
     242                 : static char *get_line(multipart_buffer *self TSRMLS_DC)
     243             318 : {
     244             318 :         char* ptr = next_line(self);
     245                 : 
     246             318 :         if (!ptr) {
     247              22 :                 fill_buffer(self TSRMLS_CC);
     248              22 :                 ptr = next_line(self);
     249                 :         }
     250                 : 
     251             318 :         return ptr;
     252                 : }
     253                 : 
     254                 : /* Free header entry */
     255                 : static void php_free_hdr_entry(mime_header_entry *h)
     256              89 : {
     257              89 :         if (h->key) {
     258              89 :                 efree(h->key);
     259                 :         }
     260              89 :         if (h->value) {
     261              89 :                 efree(h->value);
     262                 :         }
     263              89 : }
     264                 : 
     265                 : /* finds a boundary */
     266                 : static int find_boundary(multipart_buffer *self, char *boundary TSRMLS_DC)
     267              91 : {
     268                 :         char *line;
     269                 : 
     270                 :         /* loop thru lines */
     271             251 :         while( (line = get_line(self TSRMLS_CC)) )
     272                 :         {
     273                 :                 /* finished if we found the boundary */
     274             138 :                 if (!strcmp(line, boundary)) {
     275              69 :                         return 1;
     276                 :                 }
     277                 :         }
     278                 : 
     279                 :         /* didn't find the boundary */
     280              22 :         return 0;
     281                 : }
     282                 : 
     283                 : /* parse headers */
     284                 : static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header TSRMLS_DC)
     285              91 : {
     286                 :         char *line;
     287                 :         mime_header_entry prev_entry, entry;
     288                 :         int prev_len, cur_len;
     289                 : 
     290                 :         /* didn't find boundary, abort */
     291              91 :         if (!find_boundary(self, self->boundary TSRMLS_CC)) {
     292              22 :                 return 0;
     293                 :         }
     294                 : 
     295                 :         /* get lines of text, or CRLF_CRLF */
     296                 : 
     297             227 :         while( (line = get_line(self TSRMLS_CC)) && strlen(line) > 0 )
     298                 :         {
     299                 :                 /* add header to table */
     300              89 :                 char *key = line;
     301              89 :                 char *value = NULL;
     302                 : 
     303                 :                 /* space in the beginning means same header */
     304              89 :                 if (!isspace(line[0])) {
     305              89 :                         value = strchr(line, ':');
     306                 :                 }
     307                 : 
     308              89 :                 if (value) {
     309              89 :                         *value = 0;
     310             178 :                         do { value++; } while(isspace(*value));
     311                 : 
     312              89 :                         entry.value = estrdup(value);
     313              89 :                         entry.key = estrdup(key);
     314                 : 
     315               0 :                 } else if (zend_llist_count(header)) { /* If no ':' on the line, add to previous line */
     316                 : 
     317               0 :                         prev_len = strlen(prev_entry.value);
     318               0 :                         cur_len = strlen(line);
     319                 : 
     320               0 :                         entry.value = emalloc(prev_len + cur_len + 1);
     321               0 :                         memcpy(entry.value, prev_entry.value, prev_len);
     322               0 :                         memcpy(entry.value + prev_len, line, cur_len);
     323               0 :                         entry.value[cur_len + prev_len] = '\0';
     324                 : 
     325               0 :                         entry.key = estrdup(prev_entry.key);
     326                 : 
     327               0 :                         zend_llist_remove_tail(header);
     328                 :                 } else {
     329               0 :                         continue;
     330                 :                 }
     331                 : 
     332              89 :                 zend_llist_add_element(header, &entry);
     333              89 :                 prev_entry = entry;
     334                 :         }
     335                 : 
     336              69 :         return 1;
     337                 : }
     338                 : 
     339                 : static char *php_mime_get_hdr_value(zend_llist header, char *key)
     340             105 : {
     341                 :         mime_header_entry *entry;
     342                 : 
     343             105 :         if (key == NULL) {
     344               0 :                 return NULL;
     345                 :         }
     346                 : 
     347             105 :         entry = zend_llist_get_first(&header);
     348             246 :         while (entry) {
     349             119 :                 if (!strcasecmp(entry->key, key)) {
     350              83 :                         return entry->value;
     351                 :                 }
     352              36 :                 entry = zend_llist_get_next(&header);
     353                 :         }
     354                 : 
     355              22 :         return NULL;
     356                 : }
     357                 : 
     358                 : static char *php_ap_getword(char **line, char stop)
     359             285 : {
     360             285 :         char *pos = *line, quote;
     361                 :         char *res;
     362                 : 
     363            2607 :         while (*pos && *pos != stop) {
     364                 : 
     365            2145 :                 if ((quote = *pos) == '"' || quote == '\'') {
     366             108 :                         ++pos;
     367            1154 :                         while (*pos && *pos != quote) {
     368             938 :                                 if (*pos == '\\' && pos[1] && pos[1] == quote) {
     369               0 :                                         pos += 2;
     370                 :                                 } else {
     371             938 :                                         ++pos;
     372                 :                                 }
     373                 :                         }
     374             108 :                         if (*pos) {
     375             108 :                                 ++pos;
     376                 :                         }
     377            1929 :                 } else ++pos;
     378                 : 
     379                 :         }
     380             285 :         if (*pos == '\0') {
     381              69 :                 res = estrdup(*line);
     382              69 :                 *line += strlen(*line);
     383              69 :                 return res;
     384                 :         }
     385                 : 
     386             216 :         res = estrndup(*line, pos - *line);
     387                 : 
     388             648 :         while (*pos == stop) {
     389             216 :                 ++pos;
     390                 :         }
     391                 : 
     392             216 :         *line = pos;
     393             216 :         return res;
     394                 : }
     395                 : 
     396                 : static char *substring_conf(char *start, int len, char quote TSRMLS_DC)
     397             108 : {
     398             108 :         char *result = emalloc(len + 2);
     399             108 :         char *resp = result;
     400                 :         int i;
     401                 : 
     402            1046 :         for (i = 0; i < len; ++i) {
     403             938 :                 if (start[i] == '\\' && (start[i + 1] == '\\' || (quote && start[i + 1] == quote))) {
     404               0 :                         *resp++ = start[++i];
     405                 :                 } else {
     406             938 :                         *resp++ = start[i];
     407                 :                 }
     408                 :         }
     409                 : 
     410             108 :         *resp = '\0';
     411             108 :         return result;
     412                 : }
     413                 : 
     414                 : static char *php_ap_getword_conf(char **line TSRMLS_DC)
     415             108 : {
     416             108 :         char *str = *line, *strend, *res, quote;
     417                 : 
     418             216 :         while (*str && isspace(*str)) {
     419               0 :                 ++str;
     420                 :         }
     421                 : 
     422             108 :         if (!*str) {
     423               0 :                 *line = str;
     424               0 :                 return estrdup("");
     425                 :         }
     426                 : 
     427             216 :         if ((quote = *str) == '"' || quote == '\'') {
     428             108 :                 strend = str + 1;
     429             108 : look_for_quote:
     430            1154 :                 while (*strend && *strend != quote) {
     431             938 :                         if (*strend == '\\' && strend[1] && strend[1] == quote) {
     432               0 :                                 strend += 2;
     433                 :                         } else {
     434             938 :                                 ++strend;
     435                 :                         }
     436                 :                 }
     437             108 :                 if (*strend && *strend == quote) {
     438             108 :                         char p = *(strend + 1);
     439             108 :                         if (p != '\r' && p != '\n' && p != '\0') {
     440               0 :                                 strend++;
     441               0 :                                 goto look_for_quote;
     442                 :                         }
     443                 :                 }
     444                 : 
     445             108 :                 res = substring_conf(str + 1, strend - str - 1, quote TSRMLS_CC);
     446                 : 
     447             108 :                 if (*strend == quote) {
     448             108 :                         ++strend;
     449                 :                 }
     450                 : 
     451                 :         } else {
     452                 : 
     453               0 :                 strend = str;
     454               0 :                 while (*strend && !isspace(*strend)) {
     455               0 :                         ++strend;
     456                 :                 }
     457               0 :                 res = substring_conf(str, strend - str, 0 TSRMLS_CC);
     458                 :         }
     459                 : 
     460             216 :         while (*strend && isspace(*strend)) {
     461               0 :                 ++strend;
     462                 :         }
     463                 : 
     464             108 :         *line = strend;
     465             108 :         return res;
     466                 : }
     467                 : 
     468                 : /*
     469                 :   search for a string in a fixed-length byte string.
     470                 :   if partial is true, partial matches are allowed at the end of the buffer.
     471                 :   returns NULL if not found, or a pointer to the start of the first match.
     472                 : */
     473                 : static void *php_ap_memstr(char *haystack, int haystacklen, char *needle, int needlen, int partial)
     474             201 : {
     475             201 :         int len = haystacklen;
     476             201 :         char *ptr = haystack;
     477                 : 
     478                 :         /* iterate through first character matches */
     479             402 :         while( (ptr = memchr(ptr, needle[0], len)) ) {
     480                 : 
     481                 :                 /* calculate length after match */
     482             199 :                 len = haystacklen - (ptr - (char *)haystack);
     483                 : 
     484                 :                 /* done if matches up to capacity of buffer */
     485             199 :                 if (memcmp(needle, ptr, needlen < len ? needlen : len) == 0 && (partial || len >= needlen)) {
     486                 :                         break;
     487                 :                 }
     488                 : 
     489                 :                 /* next character */
     490               0 :                 ptr++; len--;
     491                 :         }
     492                 : 
     493             201 :         return ptr;
     494                 : }
     495                 : 
     496                 : /* read until a boundary condition */
     497                 : static int multipart_buffer_read(multipart_buffer *self, char *buf, int bytes, int *end TSRMLS_DC)
     498             127 : {
     499                 :         int len, max;
     500                 :         char *bound;
     501                 : 
     502                 :         /* fill buffer if needed */
     503             127 :         if (bytes > self->bytes_in_buffer) {
     504             127 :                 fill_buffer(self TSRMLS_CC);
     505                 :         }
     506                 : 
     507                 :         /* look for a potential boundary match, only read data up to that point */
     508             127 :         if ((bound = php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 1))) {
     509             125 :                 max = bound - self->buf_begin;
     510             125 :                 if (end && php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 0)) {
     511              74 :                         *end = 1;
     512                 :                 }
     513                 :         } else {
     514               2 :                 max = self->bytes_in_buffer;
     515                 :         }
     516                 : 
     517                 :         /* maximum number of bytes we are reading */
     518             127 :         len = max < bytes-1 ? max : bytes-1;
     519                 : 
     520                 :         /* if we read any data... */
     521             127 :         if (len > 0) {
     522                 : 
     523                 :                 /* copy the data */
     524              64 :                 memcpy(buf, self->buf_begin, len);
     525              64 :                 buf[len] = 0;
     526                 : 
     527              64 :                 if (bound && len > 0 && buf[len-1] == '\r') {
     528               0 :                         buf[--len] = 0;
     529                 :                 }
     530                 : 
     531                 :                 /* update the buffer */
     532              64 :                 self->bytes_in_buffer -= len;
     533              64 :                 self->buf_begin += len;
     534                 :         }
     535                 : 
     536             127 :         return len;
     537                 : }
     538                 : 
     539                 : /*
     540                 :   XXX: this is horrible memory-usage-wise, but we only expect
     541                 :   to do this on small pieces of form data.
     542                 : */
     543                 : static char *multipart_buffer_read_body(multipart_buffer *self, unsigned int *len TSRMLS_DC)
     544              26 : {
     545              26 :         char buf[FILLUNIT], *out=NULL;
     546              26 :         int total_bytes=0, read_bytes=0;
     547                 : 
     548              77 :         while((read_bytes = multipart_buffer_read(self, buf, sizeof(buf), NULL TSRMLS_CC))) {
     549              25 :                 out = erealloc(out, total_bytes + read_bytes + 1);
     550              25 :                 memcpy(out + total_bytes, buf, read_bytes);
     551              25 :                 total_bytes += read_bytes;
     552                 :         }
     553                 : 
     554              26 :         if (out) {
     555              25 :                 out[total_bytes] = '\0';
     556                 :         }
     557              26 :         *len = total_bytes;
     558                 : 
     559              26 :         return out;
     560                 : }
     561                 : 
     562                 : static void register_raw_var_ex(char *var, zval *value, HashTable *array)
     563             226 : {
     564             226 :         zend_hash_update(array, var, strlen(var) + 1, &value, sizeof(zval *), NULL);
     565             226 : }
     566                 : 
     567                 : static void register_raw_var(char *var, char *str, int str_len, HashTable *array)
     568             146 : {
     569                 :         zval *new_entry;
     570                 :         assert(str != NULL);
     571                 : 
     572                 :         /* Prepare value */
     573             146 :         MAKE_STD_ZVAL(new_entry);
     574             146 :         ZVAL_STRINGL(new_entry, str, str_len, 1);
     575                 : 
     576             146 :         register_raw_var_ex(var, new_entry, array);
     577             146 : }
     578                 : 
     579                 : /*
     580                 :  * The combined READER/HANDLER
     581                 :  *
     582                 :  */
     583                 : 
     584                 : SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)
     585              30 : {
     586              30 :         char *boundary, *s=NULL, *boundary_end = NULL, *start_arr=NULL, *array_index=NULL;
     587              30 :         char *temp_filename=NULL, *lbuf=NULL, *abuf=NULL;
     588              30 :         int boundary_len=0, total_bytes=0, cancel_upload=0, is_arr_upload=0, array_len=0;
     589              30 :         int max_file_size=0, skip_upload=0, anonindex=0, is_anonymous;
     590              30 :         HashTable *uploaded_files=NULL;
     591                 :         HashTable *post_vars, *files_vars;
     592                 :         multipart_buffer *mbuff;
     593              30 :         int fd=-1;
     594                 :         zend_llist header;
     595              30 :         void *event_extra_data = NULL;
     596              30 :         int llen = 0;
     597              30 :         int upload_cnt = INI_INT("max_file_uploads");
     598                 : 
     599              30 :         if (SG(post_max_size) > 0 && SG(request_info).content_length > SG(post_max_size)) {
     600               2 :                 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));
     601               2 :                 return;
     602                 :         }
     603                 : 
     604                 :         /* Get the boundary */
     605              28 :         boundary = strstr(content_type_dup, "boundary");
     606              28 :         if (!boundary || !(boundary=strchr(boundary, '='))) {
     607               2 :                 sapi_module.sapi_error(E_WARNING, "Missing boundary in multipart/form-data POST data");
     608               2 :                 return;
     609                 :         }
     610                 : 
     611              26 :         boundary++;
     612              26 :         boundary_len = strlen(boundary);
     613                 : 
     614              26 :         if (boundary[0] == '"') {
     615               3 :                 boundary++;
     616               3 :                 boundary_end = strchr(boundary, '"');
     617               3 :                 if (!boundary_end) {
     618               2 :                         sapi_module.sapi_error(E_WARNING, "Invalid boundary in multipart/form-data POST data");
     619               2 :                         return;
     620                 :                 }
     621                 :         } else {
     622                 :                 /* search for the end of the boundary */
     623              23 :                 boundary_end = strchr(boundary, ',');
     624                 :         }
     625              24 :         if (boundary_end) {
     626               2 :                 boundary_end[0] = '\0';
     627               2 :                 boundary_len = boundary_end-boundary;
     628                 :         }
     629                 : 
     630                 :         /* Initialize the buffer */
     631              24 :         if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) {
     632               0 :                 sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer");
     633               0 :                 return;
     634                 :         }
     635                 : 
     636              24 :         ALLOC_HASHTABLE(uploaded_files);
     637              24 :         zend_hash_init(uploaded_files, 5, NULL, (dtor_func_t) free_estring, 0);
     638              24 :         SG(rfc1867_uploaded_files) = uploaded_files;
     639                 : 
     640              24 :         ALLOC_HASHTABLE(post_vars);
     641              24 :         zend_hash_init(post_vars, 5, NULL, ZVAL_PTR_DTOR, 0);
     642              24 :         SG(rfc1867_vars) = post_vars;
     643                 : 
     644              24 :         ALLOC_HASHTABLE(files_vars);
     645              24 :         zend_hash_init(files_vars, 5, NULL, ZVAL_PTR_DTOR, 0);
     646              24 :         SG(rfc1867_files_vars) = files_vars;
     647                 : 
     648              24 :         zend_llist_init(&header, sizeof(mime_header_entry), (llist_dtor_func_t) php_free_hdr_entry, 0);
     649                 : 
     650              24 :         if (php_rfc1867_callback != NULL) {
     651                 :                 multipart_event_start event_start;
     652                 : 
     653              24 :                 event_start.content_length = SG(request_info).content_length;
     654              24 :                 if (php_rfc1867_callback(MULTIPART_EVENT_START, &event_start, &event_extra_data TSRMLS_CC) == FAILURE) {
     655               0 :                         goto fileupload_done;
     656                 :                 }
     657                 :         }
     658                 : 
     659             116 :         while (!multipart_buffer_eof(mbuff TSRMLS_CC))
     660                 :         {
     661                 :                 char buff[FILLUNIT];
     662              91 :                 char *cd = NULL, *param = NULL, *filename = NULL, *tmp = NULL;
     663              91 :                 size_t blen = 0, wlen = 0;
     664                 :                 off_t offset;
     665                 : 
     666              91 :                 zend_llist_clean(&header);
     667                 : 
     668              91 :                 if (!multipart_buffer_headers(mbuff, &header TSRMLS_CC)) {
     669              22 :                         goto fileupload_done;
     670                 :                 }
     671                 : 
     672              69 :                 if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) {
     673              69 :                         char *pair = NULL;
     674              69 :                         int end = 0;
     675                 : 
     676             138 :                         while (isspace(*cd)) {
     677               0 :                                 ++cd;
     678                 :                         }
     679                 : 
     680             315 :                         while (*cd && (pair = php_ap_getword(&cd, ';')))
     681                 :                         {
     682             177 :                                 char *key = NULL, *word = pair;
     683                 : 
     684             462 :                                 while (isspace(*cd)) {
     685             108 :                                         ++cd;
     686                 :                                 }
     687                 : 
     688             177 :                                 if (strchr(pair, '=')) {
     689             108 :                                         key = php_ap_getword(&pair, '=');
     690                 : 
     691             108 :                                         if (!strcasecmp(key, "name")) {
     692              66 :                                                 if (param) {
     693               0 :                                                         efree(param);
     694                 :                                                 }
     695              66 :                                                 param = php_ap_getword_conf(&pair TSRMLS_CC);
     696              42 :                                         } else if (!strcasecmp(key, "filename")) {
     697              42 :                                                 if (filename) {
     698               0 :                                                         efree(filename);
     699                 :                                                 }
     700              42 :                                                 filename = php_ap_getword_conf(&pair TSRMLS_CC);
     701                 :                                         }
     702                 :                                 }
     703             177 :                                 if (key) {
     704             108 :                                         efree(key);
     705                 :                                 }
     706             177 :                                 efree(word);
     707                 :                         }
     708                 : 
     709                 :                         /* Normal form variable, safe to read all data into memory */
     710              69 :                         if (!filename && param) {
     711                 :                                 unsigned int value_len;
     712              26 :                                 char *value = multipart_buffer_read_body(mbuff, &value_len TSRMLS_CC);
     713                 : 
     714              26 :                                 if (!value) {
     715               1 :                                         value = estrdup("");
     716               1 :                                         value_len = 0;
     717                 :                                 }
     718                 : 
     719              26 :                 register_raw_var(param, value, value_len, post_vars);
     720                 : 
     721              26 :                                 if (php_rfc1867_callback != NULL) {
     722                 :                                         multipart_event_formdata event_formdata;
     723                 : 
     724              26 :                                         event_formdata.post_bytes_processed = SG(read_post_bytes);
     725              26 :                                         event_formdata.name = ZSTR(param);
     726              26 :                                         event_formdata.value = PZSTR(value);
     727              26 :                                         event_formdata.length = value_len;
     728              26 :                                         event_formdata.newlength = NULL;
     729              26 :                                         php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data TSRMLS_CC);
     730                 :                                 }
     731                 : 
     732              26 :                                 if (!strcasecmp(param, "MAX_FILE_SIZE")) {
     733               1 :                                         max_file_size = atol(value);
     734                 :                                 }
     735                 : 
     736              26 :                                 efree(param);
     737              26 :                                 efree(value);
     738              26 :                                 continue;
     739                 :                         }
     740                 : 
     741                 :                         /* If file_uploads=off, skip the file part */
     742              43 :                         if (!PG(file_uploads)) {
     743               1 :                                 skip_upload = 1;
     744              42 :                         } else if (upload_cnt <= 0) {
     745               0 :                                 skip_upload = 1;
     746               0 :                                 sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded");
     747                 :                         }
     748                 : 
     749                 :                         /* Return with an error if the posted data is garbled */
     750              43 :                         if (!param && !filename) {
     751               1 :                                 sapi_module.sapi_error(E_WARNING, "File Upload Mime headers garbled");
     752               1 :                                 goto fileupload_done;
     753                 :                         }
     754                 : 
     755              42 :                         if (!param) {
     756               2 :                                 is_anonymous = 1;
     757               2 :                                 param = emalloc(MAX_SIZE_ANONNAME);
     758               2 :                                 snprintf(param, MAX_SIZE_ANONNAME, "%u", anonindex++);
     759                 :                         } else {
     760              40 :                                 is_anonymous = 0;
     761                 :                         }
     762                 : 
     763                 :                         /* New Rule: never repair potential malicious user input */
     764              42 :                         if (!skip_upload) {
     765              41 :                                 char *tmp = param;
     766              41 :                                 long c = 0;
     767                 : 
     768             281 :                                 while (*tmp) {
     769             200 :                                         if (*tmp == '[') {
     770               4 :                                                 c++;
     771             196 :                                         } else if (*tmp == ']') {
     772               4 :                                                 c--;
     773               4 :                                                 if (tmp[1] && tmp[1] != '[') {
     774               1 :                                                         skip_upload = 1;
     775               1 :                                                         break;
     776                 :                                                 }
     777                 :                                         }
     778             199 :                                         if (c < 0) {
     779               0 :                                                 skip_upload = 1;
     780               0 :                                                 break;
     781                 :                                         }
     782             199 :                                         tmp++;
     783                 :                                 }
     784                 :                         }
     785                 : 
     786              42 :                         total_bytes = cancel_upload = 0;
     787                 : 
     788              42 :                         if (!skip_upload) {
     789                 :                                 /* Handle file */
     790              40 :                                 fd = php_open_temporary_fd(PG(upload_tmp_dir), "php", &temp_filename TSRMLS_CC);
     791              40 :                                 upload_cnt--;
     792              40 :                                 if (fd==-1) {
     793               0 :                                         sapi_module.sapi_error(E_WARNING, "File upload error - unable to create a temporary file");
     794               0 :                                         cancel_upload = UPLOAD_ERROR_E;
     795                 :                                 }
     796                 :                         }
     797                 : 
     798              42 :                         if (!skip_upload && php_rfc1867_callback != NULL) {
     799                 :                                 multipart_event_file_start event_file_start;
     800                 : 
     801              40 :                                 event_file_start.post_bytes_processed = SG(read_post_bytes);
     802              40 :                                 event_file_start.name = ZSTR(param);
     803              40 :                                 event_file_start.filename = PZSTR(filename);
     804              40 :                                 if (php_rfc1867_callback(MULTIPART_EVENT_FILE_START, &event_file_start, &event_extra_data TSRMLS_CC) == FAILURE) {
     805               0 :                                         if (temp_filename) {
     806               0 :                                                 if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */
     807               0 :                                                         close(fd);
     808               0 :                                                         unlink(temp_filename);
     809                 :                                                 }
     810               0 :                                                 efree(temp_filename);
     811                 :                                         }
     812               0 :                                         temp_filename="";
     813               0 :                                         efree(param);
     814               0 :                                         efree(filename);
     815               0 :                                         continue;
     816                 :                                 }
     817                 :                         }
     818                 : 
     819              42 :                         if (skip_upload) {
     820               2 :                                 efree(param);
     821               2 :                                 efree(filename);
     822               2 :                                 continue;
     823                 :                         }
     824                 : 
     825              40 :                         if (strlen(filename) == 0) {
     826                 : #if DEBUG_FILE_UPLOAD
     827                 :                                 sapi_module.sapi_error(E_NOTICE, "No file uploaded");
     828                 : #endif
     829               1 :                                 cancel_upload = UPLOAD_ERROR_D;
     830                 :                         }
     831                 : 
     832              40 :                         offset = 0;
     833              40 :                         end = 0;
     834             119 :                         while (!cancel_upload && (blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end TSRMLS_CC)))
     835                 :                         {
     836              39 :                                 if (php_rfc1867_callback != NULL) {
     837                 :                                         multipart_event_file_data event_file_data;
     838                 : 
     839              39 :                                         event_file_data.post_bytes_processed = SG(read_post_bytes);
     840              39 :                                         event_file_data.offset = offset;
     841              39 :                                         event_file_data.data = buff;
     842              39 :                                         event_file_data.length = blen;
     843              39 :                                         event_file_data.newlength = &blen;
     844              39 :                                         if (php_rfc1867_callback(MULTIPART_EVENT_FILE_DATA, &event_file_data, &event_extra_data TSRMLS_CC) == FAILURE) {
     845               0 :                                                 cancel_upload = UPLOAD_ERROR_X;
     846               0 :                                                 continue;
     847                 :                                         }
     848                 :                                 }
     849                 : 
     850              40 :                                 if (PG(upload_max_filesize) > 0 && (int)(total_bytes+blen) > PG(upload_max_filesize)) {
     851                 : #if DEBUG_FILE_UPLOAD
     852                 :                                         sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of %ld bytes exceeded - file [%s=%s] not saved", PG(upload_max_filesize), param, filename);
     853                 : #endif
     854               1 :                                         cancel_upload = UPLOAD_ERROR_A;
     855              39 :                                 } else if (max_file_size && ((int)(total_bytes+blen) > max_file_size)) {
     856                 : #if DEBUG_FILE_UPLOAD
     857                 :                                         sapi_module.sapi_error(E_NOTICE, "MAX_FILE_SIZE of %ld bytes exceeded - file [%s=%s] not saved", max_file_size, param, filename);
     858                 : #endif
     859               1 :                                         cancel_upload = UPLOAD_ERROR_B;
     860              37 :                                 } else if (blen > 0) {
     861              37 :                                         wlen = write(fd, buff, blen);
     862                 : 
     863              37 :                                         if (wlen == -1) {
     864                 :                                                 /* write failed */
     865                 : #if DEBUG_FILE_UPLOAD
     866                 :                                                 sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno));
     867                 : #endif
     868               0 :                                                 cancel_upload = UPLOAD_ERROR_F;
     869              37 :                                         } else if (wlen < blen) {
     870                 : #if DEBUG_FILE_UPLOAD
     871                 :                                                 sapi_module.sapi_error(E_NOTICE, "Only %d bytes were written, expected to write %d", wlen, blen);
     872                 : #endif
     873               0 :                                                 cancel_upload = UPLOAD_ERROR_F;
     874                 :                                         } else {
     875              37 :                                                 total_bytes += wlen;
     876                 :                                         }
     877              37 :                                         offset += wlen;
     878                 :                                 }
     879                 :                         }
     880              40 :                         if (fd!=-1) { /* may not be initialized if file could not be created */
     881              40 :                                 close(fd);
     882                 :                         }
     883              40 :                         if (!cancel_upload && !end) {
     884                 : #if DEBUG_FILE_UPLOAD
     885                 :                                 sapi_module.sapi_error(E_NOTICE, "Missing mime boundary at the end of the data for file %s", strlen(filename) > 0 ? filename : "");
     886                 : #endif
     887               1 :                                 cancel_upload = UPLOAD_ERROR_C;
     888                 :                         }
     889                 : #if DEBUG_FILE_UPLOAD
     890                 :                         if (strlen(filename) > 0 && total_bytes == 0 && !cancel_upload) {
     891                 :                                 sapi_module.sapi_error(E_WARNING, "Uploaded file size 0 - file [%s=%s] not saved", param, filename);
     892                 :                                 cancel_upload = 5;
     893                 :                         }
     894                 : #endif
     895              40 :                         if (php_rfc1867_callback != NULL) {
     896                 :                                 multipart_event_file_end event_file_end;
     897                 : 
     898              40 :                                 event_file_end.post_bytes_processed = SG(read_post_bytes);
     899              40 :                                 event_file_end.temp_filename = ZSTR(temp_filename);
     900              40 :                                 event_file_end.cancel_upload = cancel_upload;
     901              40 :                                 if (php_rfc1867_callback(MULTIPART_EVENT_FILE_END, &event_file_end, &event_extra_data TSRMLS_CC) == FAILURE) {
     902               0 :                                         cancel_upload = UPLOAD_ERROR_X;
     903                 :                                 }
     904                 :                         }
     905                 : 
     906              40 :                         if (cancel_upload) {
     907               4 :                                 if (temp_filename) {
     908               4 :                                         if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */
     909               4 :                                                 unlink(temp_filename);
     910                 :                                         }
     911               4 :                                         efree(temp_filename);
     912                 :                                 }
     913               4 :                                 temp_filename="";
     914                 :                         } else {
     915              36 :                                 zend_hash_add(SG(rfc1867_uploaded_files), temp_filename, strlen(temp_filename) + 1, &temp_filename, sizeof(char *), NULL);
     916                 :                         }
     917                 : 
     918                 :                         /* is_arr_upload is true when name of file upload field
     919                 :                          * ends in [.*]
     920                 :                          * start_arr is set to point to 1st [
     921                 :                          */
     922              40 :                         is_arr_upload = (start_arr = strchr(param,'[')) && (param[strlen(param)-1] == ']');
     923                 : 
     924              40 :                         if (is_arr_upload) {
     925               3 :                                 array_len = strlen(start_arr);
     926               3 :                                 if (array_index) {
     927               2 :                                         efree(array_index);
     928                 :                                 }
     929               3 :                                 array_index = estrndup(start_arr + 1, array_len - 2);
     930                 :                         }
     931                 : 
     932                 :                         /* Add $foo_name */
     933              40 :                         if (lbuf) {
     934              21 :                                 efree(lbuf);
     935                 :                         }
     936              40 :                         llen = strlen(param) + MAX_SIZE_OF_INDEX + 1;
     937              40 :                         lbuf = (char *) emalloc(llen);
     938                 : 
     939                 :                         /* The \ check should technically be needed for win32 systems only where
     940                 :                          * it is a valid path separator. However, IE in all it's wisdom always sends
     941                 :                          * the full path of the file on the user's filesystem, which means that unless
     942                 :                          * the user does basename() they get a bogus file name. Until IE's user base drops
     943                 :                          * to nill or problem is fixed this code must remain enabled for all systems.
     944                 :                          */
     945              40 :                         s = strrchr(filename, '\\');
     946              40 :                         if ((tmp = strrchr(filename, '/')) > s) {
     947               1 :                                 s = tmp;
     948                 :                         }
     949                 : 
     950                 :                         /* Add $foo[name] */
     951              40 :                         if (is_arr_upload) {
     952               3 :                                 snprintf(lbuf, llen, "%s[name][%s]", abuf, array_index);
     953                 :                         } else {
     954              37 :                                 snprintf(lbuf, llen, "%s[name]", param);
     955                 :                         }
     956              41 :                         if (s && s > filename) {
     957               1 :                                 register_raw_var(lbuf, s+1, strlen(s+1), files_vars);
     958                 :                         } else {
     959              39 :                                 register_raw_var(lbuf, filename, strlen(filename), files_vars);
     960                 :                         }
     961              40 :                         efree(filename);
     962              40 :                         s = NULL;
     963                 : 
     964                 :                         /* Possible Content-Type: */
     965              66 :                         if (cancel_upload || !(cd = php_mime_get_hdr_value(header, "Content-Type"))) {
     966              26 :                                 cd = "";
     967                 :                         } else {
     968                 :                                 /* fix for Opera 6.01 */
     969              14 :                                 s = strchr(cd, ';');
     970              14 :                                 if (s != NULL) {
     971               1 :                                         *s = '\0';
     972                 :                                 }
     973                 :                         }
     974                 : 
     975                 :                         /* Add $foo[type] */
     976              40 :                         if (is_arr_upload) {
     977               3 :                                 snprintf(lbuf, llen, "%s[type][%s]", abuf, array_index);
     978                 :                         } else {
     979              37 :                                 snprintf(lbuf, llen, "%s[type]", param);
     980                 :                         }
     981              40 :                         register_raw_var(lbuf, cd, strlen(cd), files_vars);
     982                 : 
     983                 :                         /* Restore Content-Type Header */
     984              40 :                         if (s != NULL) {
     985               1 :                                 *s = ';';
     986                 :                         }
     987              40 :                         s = "";
     988                 : 
     989                 :                         /* Add $foo[tmp_name] */
     990              40 :                         if (is_arr_upload) {
     991               3 :                                 snprintf(lbuf, llen, "%s[tmp_name][%s]", abuf, array_index);
     992                 :                         } else {
     993              37 :                                 snprintf(lbuf, llen, "%s[tmp_name]", param);
     994                 :                         }
     995              40 :                         register_raw_var(lbuf, temp_filename, strlen(temp_filename), files_vars);
     996                 : 
     997                 :                         {
     998                 :                                 zval *file_size, *error_type;
     999                 : 
    1000              40 :                                 MAKE_STD_ZVAL(error_type);
    1001              40 :                                 ZVAL_LONG(error_type, cancel_upload);
    1002                 : 
    1003              40 :                                 MAKE_STD_ZVAL(file_size);
    1004                 : 
    1005                 :                                 /* Add $foo[error] */
    1006              40 :                                 if (cancel_upload) {
    1007               4 :                                         ZVAL_LONG(file_size, 0);
    1008                 :                                 } else {
    1009              36 :                                         ZVAL_LONG(file_size, total_bytes);
    1010                 :                                 }
    1011                 : 
    1012              40 :                                 if (is_arr_upload) {
    1013               3 :                                         snprintf(lbuf, llen, "%s[error][%s]", abuf, array_index);
    1014                 :                                 } else {
    1015              37 :                                         snprintf(lbuf, llen, "%s[error]", param);
    1016                 :                                 }
    1017              40 :                                 register_raw_var_ex(lbuf, error_type, files_vars);
    1018                 : 
    1019                 :                                 /* Add $foo[size] */
    1020              40 :                                 if (is_arr_upload) {
    1021               3 :                                         snprintf(lbuf, llen, "%s[size][%s]", abuf, array_index);
    1022                 :                                 } else {
    1023              37 :                                         snprintf(lbuf, llen, "%s[size]", param);
    1024                 :                                 }
    1025              40 :                                 register_raw_var_ex(lbuf, file_size, files_vars);
    1026                 :                         }
    1027              40 :                         efree(param);
    1028                 :                 }
    1029                 :         }
    1030                 : 
    1031              24 : fileupload_done:
    1032              24 :         if (php_rfc1867_callback != NULL) {
    1033                 :                 multipart_event_end event_end;
    1034                 : 
    1035              24 :                 event_end.post_bytes_processed = SG(read_post_bytes);
    1036              24 :                 php_rfc1867_callback(MULTIPART_EVENT_END, &event_end, &event_extra_data TSRMLS_CC);
    1037                 :         }
    1038                 : 
    1039              24 :         SAFE_RETURN;
    1040                 : }
    1041                 : 
    1042                 : /*
    1043                 :  * Local variables:
    1044                 :  * tab-width: 4
    1045                 :  * c-basic-offset: 4
    1046                 :  * End:
    1047                 :  * vim600: sw=4 ts=4 fdm=marker
    1048                 :  * vim<600: sw=4 ts=4
    1049                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Mon, 23 Nov 2009 17:39:46 +0000 (35 hours ago)

Copyright © 2005-2009 The PHP Group
All rights reserved.