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;
}
Example #2
0
static void parse_address_tokens(php_rfc822_tokenized_t *toks,
                                 php_rfc822_addresses_t *addrs, int *naddrs) {
  int start_tok = 0, iaddr = 0, i, in_group = 0, group_lbl_start = 0,
    group_lbl_end = 0;
  int a_start, a_count; /* position and count for address part of a name */
  std::string group_addrs;
  char *address_value = NULL;

address:  /* mailbox / group */

  if (start_tok >= toks->ntokens) {
    /* the end */
    *naddrs = iaddr;
    return;
  }

  /* look ahead to determine if we are dealing with a group */
  for (i = start_tok; i < toks->ntokens; i++)
    if (toks->tokens[i].token != 0 && toks->tokens[i].token != '"')
      break;

  if (i < toks->ntokens && toks->tokens[i].token == ':') {
    /* it's a group */
    in_group = 1;
    group_lbl_start = start_tok;
    group_lbl_end = i;

    /* we want the address for the group to include the leading ":" and the
       trailing ";" */
    start_tok = i;
  }

 mailbox:  /* addr-spec / phrase route-addr */
  if (start_tok >= toks->ntokens) {
    /* the end */
    *naddrs = iaddr;
    return;
  }

  /* skip spurious commas */
  while (start_tok < toks->ntokens && (toks->tokens[start_tok].token == ','
      || toks->tokens[start_tok].token == ';'))
    start_tok++;

  /* look ahead: if we find a '<' before we find an '@', we are dealing with
     a route-addr, otherwise we have an addr-spec */
  for (i = start_tok; i < toks->ntokens && toks->tokens[i].token != ';'
    && toks->tokens[i].token != ',' && toks->tokens[i].token != '<'; i++)
    ;

  /* the stuff from start_tok to i - 1 is the display name part */
  if (addrs && !in_group && i - start_tok > 0) {
    int j, has_comments = 0, has_strings = 0;
    switch (toks->tokens[i].token) {
    case ';': case ',': case '<':
      addrs->addrs[iaddr].name =
        php_rfc822_recombine_tokens(toks, start_tok, i - start_tok,
                                    PHP_RFC822_RECOMBINE_SPACE_ATOMS);
      break;
    default:
      /* it's only the display name if there are quoted strings or comments
         in there */
      for (j = start_tok; j < i; j++) {
        if (toks->tokens[j].token == '(')
          has_comments = 1;
        if (toks->tokens[j].token == '"')
          has_strings = 1;
      }
      if (has_comments && !has_strings) {
        addrs->addrs[iaddr].name =
          php_rfc822_recombine_tokens(toks, start_tok,
                                      i - start_tok,
                                      PHP_RFC822_RECOMBINE_SPACE_ATOMS |
                                      PHP_RFC822_RECOMBINE_COMMENTS_ONLY |
                                      PHP_RFC822_RECOMBINE_COMMENTS_TO_QUOTES
                                      );
      } else if (has_strings) {
        addrs->addrs[iaddr].name =
          php_rfc822_recombine_tokens(toks, start_tok, i - start_tok,
                                      PHP_RFC822_RECOMBINE_SPACE_ATOMS);
      }
    }
  }

  if (i < toks->ntokens && toks->tokens[i].token == '<') {
    int j;
    /* RFC822: route-addr = "<" [route] addr-spec ">" */
    /* look for the closing '>' and recombine as the address part */

    for (j = i; j < toks->ntokens && toks->tokens[j].token != '>'; j++)
      ;

    if (addrs) {
      a_start = i;
      a_count = j-i;
      /* if an address is enclosed in <>, leave them out of the the
       * address value that we return */
      if (toks->tokens[a_start].token == '<') {
        a_start++;
        a_count--;
      }
      address_value =
        php_rfc822_recombine_tokens(toks, a_start, a_count,
                                    PHP_RFC822_RECOMBINE_SPACE_ATOMS|
                                    PHP_RFC822_RECOMBINE_IGNORE_COMMENTS|
                                    PHP_RFC822_RECOMBINE_INCLUDE_QUOTES);
    }

    start_tok = ++j;
  } else {
    /* RFC822: addr-spec = local-part "@" domain */
    if (addrs) {
      a_start = start_tok;
      a_count = i - start_tok;
      /* if an address is enclosed in <>, leave them out of the the
       * address value that we return */
      if (toks->tokens[a_start].token == '<') {
        a_start++;
        a_count--;
      }

      address_value =
        php_rfc822_recombine_tokens(toks, a_start, a_count,
                                    PHP_RFC822_RECOMBINE_SPACE_ATOMS|
                                    PHP_RFC822_RECOMBINE_IGNORE_COMMENTS|
                                    PHP_RFC822_RECOMBINE_INCLUDE_QUOTES);
    }
    start_tok = i;
  }

  if (addrs && address_value) {

    /* if no display name has been given, use the address */
    if (addrs->addrs[iaddr].name == NULL) {
      addrs->addrs[iaddr].name = strdup(address_value);
    }

    if (in_group) {
      if (!group_addrs.empty()) group_addrs += ',';
      group_addrs += address_value;
      free(address_value);
    } else {
      addrs->addrs[iaddr].address = address_value;
    }
    address_value = NULL;
  }

  if (!in_group) {
    iaddr++;
    goto address;
  }
  /* still dealing with a group. If we find a ";", that's the end of the
     group */
  if ((start_tok < toks->ntokens && toks->tokens[start_tok].token == ';') ||
      start_tok == toks->ntokens) {
    /* end of group */

    if (addrs) {
      group_addrs += ';';
      addrs->addrs[iaddr].address = strdup(group_addrs.c_str());

      free(addrs->addrs[iaddr].name);
      addrs->addrs[iaddr].name =
        php_rfc822_recombine_tokens(toks, group_lbl_start,
                                    group_lbl_end - group_lbl_start,
                                    PHP_RFC822_RECOMBINE_SPACE_ATOMS);

      addrs->addrs[iaddr].is_group = 1;
    }

    iaddr++;
    in_group = 0;
    start_tok++;
    goto address;
  }
  /* look for more mailboxes in this group */
  goto mailbox;
}
static struct php_mimeheader_with_attributes *php_mimeheader_alloc_from_tok(php_rfc822_tokenized_t *toks)
{
	struct php_mimeheader_with_attributes *attr;
	int i, first_semi, next_semi, comments_before_semi, netscape_bug = 0;
	char *name_buf = NULL;
	smart_string value_buf = {0};
	int is_rfc2231_name = 0;
	char *check_name;
	int charset_p, prevcharset_p = 0;
	int namechanged, currentencoded = 0;

	attr = ecalloc(1, sizeof(struct php_mimeheader_with_attributes));

	array_init(&attr->attributes);

/*  php_rfc822_print_tokens(toks); */

	/* look for optional ; which separates optional attributes from the main value */
	for (first_semi = 2; first_semi < toks->ntokens; first_semi++)
		if (toks->tokens[first_semi].token == ';')
			break;

	attr->value = php_rfc822_recombine_tokens(toks, 2, first_semi - 2,
			PHP_RFC822_RECOMBINE_STRTOLOWER | PHP_RFC822_RECOMBINE_IGNORE_COMMENTS);

	if (first_semi < toks->ntokens)
		first_semi++;

	/* Netscape Bug: Messenger sometimes omits the semi when wrapping the
     * the header.
	 * That means we have to be even more clever than the spec says that
	 * we need to :-/
	 * */

	while (first_semi < toks->ntokens) {
		/* find the next ; */
		comments_before_semi = 0;
		for (next_semi = first_semi; next_semi < toks->ntokens; next_semi++) {
			if (toks->tokens[next_semi].token == ';')
				break;
			if (toks->tokens[next_semi].token == '(')
				comments_before_semi++;
		}


		i = first_semi;
		if (i < next_semi)	{
			i++;

			/* ignore comments */
			while (i < next_semi && toks->tokens[i].token == '(')
				i++;

			if (i < next_semi && toks->tokens[i].token == '=') {
				char *name, *value;

				/* Here, next_semi --> "name" and i --> "=", so skip "=" sign */
				i++;

				/* count those tokens; we expect "token = token" (3 tokens); if there are
				 * more than that, then something is quite possibly wrong - Netscape Bug! */
				if (next_semi < toks->ntokens
						&& toks->tokens[next_semi].token != ';'
						&& next_semi - first_semi - comments_before_semi > 3) {
					next_semi = i + 1;
					netscape_bug = 1;
				}

				name = php_rfc822_recombine_tokens(toks, first_semi, 1,
						PHP_RFC822_RECOMBINE_STRTOLOWER|PHP_RFC822_RECOMBINE_IGNORE_COMMENTS);
				value = php_rfc822_recombine_tokens(toks, i, next_semi - i,
						PHP_RFC822_RECOMBINE_IGNORE_COMMENTS);

				/* support rfc2231 mime parameter value
				 *
				 * Parameter Value Continuations:
				 *
				 * Content-Type: message/external-body; access-type=URL;
				 *	URL*0="ftp://";
				 *	URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"
				 *
				 * is semantically identical to
				 *
				 * Content-Type: message/external-body; access-type=URL;
				 *	URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"
				 *
				 * Original rfc2231 support by IceWarp Ltd. <*****@*****.**>
				 */
				check_name = strchr(name, '*');
				if (check_name) {
				  currentencoded = 1;

					/* Is last char * - charset encoding */
					charset_p = *(name+strlen(name)-1) == '*';

					/* Leave only attribute name without * */
					*check_name = 0;

					/* New item or continuous */
					if (NULL == name_buf) {
						namechanged = 0;
						name_buf = name;
					} else {
						namechanged = (strcmp(name_buf, name) != 0);
						if (!namechanged) {
							efree(name);
							name = 0;
						}
					}

					/* Check if name changed*/
					if (!namechanged) {

						/* Append string to buffer - check if to be encoded...	*/
						rfc2231_to_mime(&value_buf, value, charset_p, prevcharset_p);

						/* Mark previous */
						prevcharset_p = charset_p;
					}

					is_rfc2231_name = 1;
				}

				/* Last item was encoded	*/
				if (1 == is_rfc2231_name) {
					/* Name not null and name differs with new name*/
					if (name && strcmp(name_buf, name) != 0) {
						/* Finalize packet */
						rfc2231_to_mime(&value_buf, NULL, 0, prevcharset_p);

						add_assoc_stringl(&attr->attributes, name_buf, value_buf.c, value_buf.len);
						efree(name_buf);
						smart_string_free(&value_buf);

						prevcharset_p = 0;
						is_rfc2231_name = 0;
						name_buf = NULL;

						/* New non encoded name*/
						if (!currentencoded) {
							/* Add string*/
							add_assoc_string(&attr->attributes, name, value);
							efree(name);
						} else {		/* Encoded name changed*/
							if (namechanged) {
								/* Append string to buffer - check if to be encoded...	*/
								rfc2231_to_mime(&value_buf, value, charset_p, prevcharset_p);

								/* Mark */
								is_rfc2231_name = 1;
								name_buf = name;
								prevcharset_p = charset_p;
							}
						}

						namechanged = 0;
					}
				} else {
					add_assoc_string(&attr->attributes, name, value);
					efree(name);
				}

				efree(value);
			}
		}

		if (next_semi < toks->ntokens && !netscape_bug) {
			next_semi++;
		}

		first_semi = next_semi;
		netscape_bug = 0;
	}

	if (1 == is_rfc2231_name) {
		/* Finalize packet */
		rfc2231_to_mime(&value_buf, NULL, 0, prevcharset_p);

		add_assoc_stringl(&attr->attributes, name_buf, value_buf.c, value_buf.len);
		efree(name_buf);
		smart_string_free(&value_buf);
	}


	return attr;
}