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; }
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; }