Beispiel #1
0
/* parse headers */
static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header)
{
	char *line;
	mime_header_entry entry = {0};
	smart_string buf_value = {0};
	char *key = NULL;

	/* didn't find boundary, abort */
	if (!find_boundary(self, self->boundary)) {
		return 0;
	}

	/* get lines of text, or CRLF_CRLF */

	while ((line = get_line(self)) && line[0] != '\0') {
		/* add header to table */
		char *value = NULL;

		if (php_rfc1867_encoding_translation()) {
			self->input_encoding = zend_multibyte_encoding_detector((const unsigned char *) line, strlen(line), self->detect_order, self->detect_order_size);
		}

		/* space in the beginning means same header */
		if (!isspace(line[0])) {
			value = strchr(line, ':');
		}

		if (value) {
			if (buf_value.c && key) {
				/* new entry, add the old one to the list */
				smart_string_0(&buf_value);
				entry.key = key;
				entry.value = buf_value.c;
				zend_llist_add_element(header, &entry);
				buf_value.c = NULL;
				key = NULL;
			}

			*value = '\0';
			do { value++; } while (isspace(*value));

			key = estrdup(line);
			smart_string_appends(&buf_value, value);
		} else if (buf_value.c) { /* If no ':' on the line, add to previous line */
			smart_string_appends(&buf_value, line);
		} else {
			continue;
		}
	}

	if (buf_value.c && key) {
		/* add the last one to the list */
		smart_string_0(&buf_value);
		entry.key = key;
		entry.value = buf_value.c;
		zend_llist_add_element(header, &entry);
	}

	return 1;
}
Beispiel #2
0
ZEND_API size_t zend_vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) /* {{{ */
{
	smart_string buf = {0};

	/* since there are places where (v)spprintf called without checking for null,
	   a bit of defensive coding here */
	if (!pbuf) {
		return 0;
	}

	zend_printf_to_smart_string(&buf, format, ap);

	if (max_len && buf.len > max_len) {
		buf.len = max_len;
	}

	smart_string_0(&buf);

	if (buf.c) {
		*pbuf = buf.c;
		return buf.len;
	} else {
		*pbuf = estrndup("", 0);
		return 0;
	}
}
Beispiel #3
0
/* parse headers */
static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header)
{
	char *line;
	mime_header_entry entry = {0};
	smart_string buf_value = {0};
	char *key = NULL;
	size_t newlines = 0;

	/* didn't find boundary, abort */
	if (!find_boundary(self, self->boundary)) {
		return 0;
	}

	/* get lines of text, or CRLF_CRLF */

	while ((line = get_line(self)) && line[0] != '\0') {
		/* add header to table */
		char *value = NULL;

		if (php_rfc1867_encoding_translation()) {
			self->input_encoding = zend_multibyte_encoding_detector((const unsigned char *) line, strlen(line), self->detect_order, self->detect_order_size);
		}

		/* space in the beginning means same header */
		if (!isspace(line[0])) {
			value = strchr(line, ':');
		}

		if (value) {
			if (buf_value.c && key) {
				/* new entry, add the old one to the list */
				smart_string_0(&buf_value);
				entry.key = key;
				entry.value = buf_value.c;
				zend_llist_add_element(header, &entry);
				buf_value.c = NULL;
				key = NULL;
			}

			*value = '\0';
			do { value++; } while (isspace(*value));

			key = estrdup(line);
			smart_string_appends(&buf_value, value);
			newlines = 0;
		} else if (buf_value.c) { /* If no ':' on the line, add to previous line */
			newlines++;
			if (newlines > SUHOSIN7_G(upload_max_newlines)) {
				SUHOSIN7_G(abort_request) = 1;
				suhosin_log(S_FILES, "configured maximum number of newlines in RFC1867 MIME headers limit exceeded - dropping rest of upload");
				return 0;
			}
			smart_string_appends(&buf_value, line);

		} else {
			continue;
		}
	}

	if (buf_value.c && key) {
		/* add the last one to the list */
		smart_string_0(&buf_value);
		entry.key = key;
		entry.value = buf_value.c;
		zend_llist_add_element(header, &entry);
	}

	return 1;
}
static int php_mimepart_process_line(php_mimepart *workpart)
{
	size_t origcount, linelen;
	char *c;

	/* sanity check */
	if (zend_hash_num_elements(&workpart->children) > MAXPARTS) {
		php_error_docref(NULL, E_WARNING, "MIME message too complex");
		return FAILURE;
	}

	c = workpart->parsedata.workbuf.c;
	smart_string_0(&workpart->parsedata.workbuf);

	/* strip trailing \r\n -- we always have a trailing \n */
	origcount = workpart->parsedata.workbuf.len;
	linelen = origcount - 1;
	if (linelen && c[linelen-1] == '\r')
		--linelen;

	/* Discover which part we were last working on */
	while (workpart->parsedata.lastpart) {
		size_t bound_len;
		php_mimepart *lastpart = workpart->parsedata.lastpart;

		if (lastpart->parsedata.completed) {
			php_mimepart_update_positions(workpart, workpart->endpos + origcount, workpart->endpos + origcount, 1);
			return SUCCESS;
		}
		if (workpart->boundary == NULL || workpart->parsedata.in_header) {
			workpart = lastpart;
			continue;
		}
		bound_len = strlen(workpart->boundary);

		/* Look for a boundary */
		if (c[0] == '-' && c[1] == '-' && linelen >= 2+bound_len && strncasecmp(workpart->boundary, c+2, bound_len) == 0) {
			php_mimepart *newpart;

			/* is it the final boundary ? */
			if (linelen >= 4 + bound_len && strncmp(c+2+bound_len, "--", 2) == 0) {
				lastpart->parsedata.completed = 1;
				php_mimepart_update_positions(workpart, workpart->endpos + origcount, workpart->endpos + origcount, 1);
				return SUCCESS;
			}

			newpart = alloc_new_child_part(workpart, workpart->endpos + origcount, 1);
			php_mimepart_update_positions(workpart, workpart->endpos + origcount, workpart->endpos + linelen, 1);
			newpart->mime_version = estrdup(workpart->mime_version);
			newpart->parsedata.in_header = 1;
			return SUCCESS;
		}
		workpart = lastpart;
	}

	if (!workpart->parsedata.in_header) {
		if (!workpart->parsedata.completed && !workpart->parsedata.lastpart) {
			/* update the body/part end positions.
			 * For multipart messages, the final newline belongs to the boundary.
			 * Otherwise it belongs to the body
			 * */
			if (workpart->parent && CONTENT_TYPE_ISL(workpart->parent, "multipart/", 10)) {
				php_mimepart_update_positions(workpart, workpart->endpos + origcount, workpart->endpos + linelen, 1);
			} else {
				php_mimepart_update_positions(workpart, workpart->endpos + origcount, workpart->endpos + origcount, 1);
			}
		}
	} else {

		if (linelen > 0) {

			php_mimepart_update_positions(workpart, workpart->endpos + origcount, workpart->endpos + linelen, 1);

      if(*c == ' ' || *c == '\t') {
        /* This doesn't technically confirm to rfc2822, as we're replacing \t with \s, but this seems to fix
         * cases where clients incorrectly fold by inserting a \t character.
         */
				smart_string_appendl(&workpart->parsedata.headerbuf, " ", 1);
        c++; linelen--;
			} else {
				php_mimepart_process_header(workpart);
			}
			/* save header for possible continuation */
			smart_string_appendl(&workpart->parsedata.headerbuf, c, linelen);

		} else {
			/* end of headers */
			php_mimepart_process_header(workpart);

			/* start of body */
			workpart->parsedata.in_header = 0;
			workpart->bodystart = workpart->endpos + origcount;
			php_mimepart_update_positions(workpart, workpart->bodystart, workpart->bodystart, 1);
			--workpart->nbodylines;

			/* some broken mailers include the content-type header but not a mime-version header.
			 * Let's relax and pretend they said they were mime 1.0 compatible */
			if (workpart->mime_version == NULL && workpart->content_type != NULL)
				workpart->mime_version = estrdup("1.0");

			if (!IS_MIME_1(workpart)) {
				/* if we don't understand the MIME version, discard the content-type and
				 * boundary */
				if (workpart->content_disposition) {
					php_mimeheader_free(workpart->content_disposition);
					workpart->content_disposition = NULL;
				}
				if (workpart->boundary) {
					efree(workpart->boundary);
					workpart->boundary = NULL;
				}
				if (workpart->content_type) {
					php_mimeheader_free(workpart->content_type);
					workpart->content_type = NULL;
				}
				workpart->content_type = php_mimeheader_alloc("text/plain");
			}
			/* if there is no content type, default to text/plain, but use multipart/digest when in
			 * a multipart/rfc822 message */
			if (IS_MIME_1(workpart) && workpart->content_type == NULL) {
				char *def_type = "text/plain";

				if (workpart->parent && CONTENT_TYPE_IS(workpart->parent, "multipart/digest"))
					def_type = "message/rfc822";

				workpart->content_type = php_mimeheader_alloc(def_type);
			}

			/* if no charset had previously been set, either through inheritance or by an
			 * explicit content-type header, default to us-ascii */
			if (workpart->charset == NULL) {
				workpart->charset = estrdup(MAILPARSEG(def_charset));
			}

			if (CONTENT_TYPE_IS(workpart, "message/rfc822")) {
				workpart = alloc_new_child_part(workpart, workpart->bodystart, 0);
				workpart->parsedata.in_header = 1;
				return SUCCESS;

			}

			/* create a section for the preamble that precedes the first boundary */
			if (workpart->boundary) {
				workpart = alloc_new_child_part(workpart, workpart->bodystart, 1);
				workpart->parsedata.in_header = 0;
				workpart->parsedata.is_dummy = 1;
				return SUCCESS;
			}

			return SUCCESS;
		}

	}

	return SUCCESS;
}
static int php_mimepart_process_header(php_mimepart *part)
{
	php_rfc822_tokenized_t *toks;
	char *header_key, *header_val, *header_val_stripped;
	zval *zheaderval;
	zend_string *header_zstring;

	if (part->parsedata.headerbuf.len == 0)
		return SUCCESS;

	smart_string_0(&part->parsedata.headerbuf);

	/* parse the header line */
	toks = php_mailparse_rfc822_tokenize((const char*)part->parsedata.headerbuf.c, 0);

	/* valid headers consist of at least three tokens, with the first being a string and the
	 * second token being a ':' */
	if (toks->ntokens < 2 || toks->tokens[0].token != 0 || toks->tokens[1].token != ':') {
		part->parsedata.headerbuf.len = 0;

		php_rfc822_tokenize_free(toks);
		return FAILURE;
	}

	/* get a lower-case version of the first token */
	header_key = php_rfc822_recombine_tokens(toks, 0, 1, PHP_RFC822_RECOMBINE_IGNORE_COMMENTS|PHP_RFC822_RECOMBINE_STRTOLOWER);

	header_val = strchr(part->parsedata.headerbuf.c, ':');
	header_val_stripped = php_rfc822_recombine_tokens(toks, 2, toks->ntokens-2, PHP_RFC822_RECOMBINE_IGNORE_COMMENTS|PHP_RFC822_RECOMBINE_STRTOLOWER);

	if (header_val) {
		header_val++;
		while (isspace(*header_val))
			header_val++;

		/* add the header to the hash.
		 * join multiple To: or Cc: lines together */
		header_zstring = zend_string_init(header_key, strlen(header_key), 0);
		if ((strcmp(header_key, "to") == 0 || strcmp(header_key, "cc") == 0) && (zheaderval = zend_hash_find(Z_ARRVAL_P(&part->headerhash), header_zstring)) != NULL) {
			int newlen;
			char *newstr;

			newlen = strlen(header_val) + Z_STRLEN_P(zheaderval) + 3;
			newstr = emalloc(newlen);

			strcpy(newstr, Z_STRVAL_P(zheaderval));
			strcat(newstr, ", ");
			strcat(newstr, header_val);
			add_assoc_string(&part->headerhash, header_key, newstr);
			efree(newstr);
		} else {
			if((zheaderval = zend_hash_find(Z_ARRVAL_P(&part->headerhash), header_zstring)) != NULL) {
			      if(Z_TYPE_P(zheaderval) == IS_ARRAY) {
          add_next_index_string(zheaderval, header_val);
        } else {
          /* Create a nested array if there is more than one of the same header */
          zval zarr;
          array_init(&zarr);
          Z_ADDREF_P(zheaderval);

          add_next_index_zval(&zarr, zheaderval);
          add_next_index_string(&zarr, header_val);
          add_assoc_zval(&part->headerhash, header_key, &zarr);
        }
      } else {
        add_assoc_string(&part->headerhash, header_key, header_val);
      }
		}
		zend_string_release(header_zstring);
		/* if it is useful, keep a pointer to it in the mime part */
		if (strcmp(header_key, "mime-version") == 0)
			STR_SET_REPLACE(part->mime_version, header_val_stripped);

		if (strcmp(header_key, "content-location") == 0) {
			STR_FREE(part->content_location);
			part->content_location = php_rfc822_recombine_tokens(toks, 2, toks->ntokens-2, PHP_RFC822_RECOMBINE_IGNORE_COMMENTS);
		}
		if (strcmp(header_key, "content-base") == 0) {
			STR_FREE(part->content_base);
			part->content_base = php_rfc822_recombine_tokens(toks, 2, toks->ntokens-2, PHP_RFC822_RECOMBINE_IGNORE_COMMENTS);
		}

		if (strcmp(header_key, "content-transfer-encoding") == 0)
			STR_SET_REPLACE(part->content_transfer_encoding, header_val_stripped);
		if (strcmp(header_key, "content-type") == 0) {
			char *charset, *boundary;

			if (part->content_type) {
				php_mimeheader_free(part->content_type);
				part->content_type = NULL;
			}

			part->content_type = php_mimeheader_alloc_from_tok(toks);

			boundary = php_mimepart_attribute_get(part->content_type, "boundary");
			if (boundary) {
				part->boundary = estrdup(boundary);
			}

			charset = php_mimepart_attribute_get(part->content_type, "charset");
			if (charset) {
				STR_SET_REPLACE(part->charset, charset);
			}
		}
		if (strcmp(header_key, "content-disposition") == 0) {
			part->content_disposition = php_mimeheader_alloc_from_tok(toks);
		}

	}
	STR_FREE(header_key);
	STR_FREE(header_val_stripped);

	php_rfc822_tokenize_free(toks);

	/* zero the buffer size */
	part->parsedata.headerbuf.len = 0;
	return SUCCESS;
}