/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
*/
/* $Id: php_mailparse_mime.c,v 1.20 2007/09/08 08:49:38 shire Exp $ */
#include "php.h"
#include "php_mailparse.h"
#include "php_mailparse_mime.h"
#include "php_mailparse_rfc822.h"
#define MAXLEVELS 20
#define MAXPARTS 300
#define IS_MIME_1(part) (((part)->mime_version && strcmp("1.0", (part)->mime_version) == 0) || ((part)->parent))
#define CONTENT_TYPE_IS(part, contenttypevalue) ((part)->content_type && strcasecmp((part)->content_type->value, contenttypevalue) == 0)
#define CONTENT_TYPE_ISL(part, contenttypevalue, len) ((part)->content_type && strncasecmp((part)->content_type->value, contenttypevalue, len) == 0)
static void php_mimeheader_free(struct php_mimeheader_with_attributes *attr)
{
STR_FREE(attr->value);
zval_dtor(attr->attributes);
efree(attr->attributes);
efree(attr);
}
static struct php_mimeheader_with_attributes * php_mimeheader_alloc(char *value)
{
struct php_mimeheader_with_attributes *attr;
attr = ecalloc(1, sizeof(struct php_mimeheader_with_attributes));
MAKE_STD_ZVAL(attr->attributes);
array_init(attr->attributes);
attr->value = estrdup(value);
return attr;
}
void rfc2231_to_mime(smart_str* value_buf, char* value, int charset_p, int prevcharset_p)
{
char *strp, *startofvalue = NULL;
int quotes=0;
int valuepos;
int i;
/* Process string, get positions and replace */
/* Set to start of buffer*/
if (charset_p) {
/* Previous charset already set so only convert %nn to =nn*/
if (prevcharset_p) quotes=2;
strp = value;
while (*strp) {
/* Quote handling*/
if (*strp == '\'')
{
if (quotes <= 1) {
/* End of charset*/
if (quotes == 0) {
*strp=0;
} else {
startofvalue = strp+1;
valuepos = i;
}
quotes++;
}
} else {
/* Replace % with = - quoted printable*/
if (*strp == '%' && quotes==2)
{
*strp = '=';
}
}
strp++;
}
}
/* If first encoded token*/
if (charset_p && !prevcharset_p && startofvalue) {
smart_str_appends(value_buf, "=?");
smart_str_appends(value_buf, value);
smart_str_appends(value_buf, "?Q?");
smart_str_appends(value_buf, startofvalue);
}
/* If last encoded token*/
if (prevcharset_p && !charset_p)
{
smart_str_appends(value_buf, "?=");
}
/* Append value*/
if ((!charset_p || (prevcharset_p && charset_p)) && value)
{
smart_str_appends(value_buf, value);
}
}
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_str value_buf = {0};
int is_rfc2231_name = 0;
char *check_name, *check_end_name;
int charset_p, prevcharset_p = 0;
int namechanged, currentencoded = 0;
attr = ecalloc(1, sizeof(struct php_mimeheader_with_attributes));
MAKE_STD_ZVAL(attr->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. <info@icewarp.com>
*/
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);
efree(value);
/* 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_string(attr->attributes, name_buf, estrndup(value_buf.c, value_buf.len), 0);
efree(name_buf);
smart_str_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, 0);
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);
efree(value);
/* Mark */
is_rfc2231_name = 1;
name_buf = name;
prevcharset_p = charset_p;
}
}
namechanged = 0;
}
} else {
add_assoc_string(attr->attributes, name, value, 0);
efree(name);
}
}
}
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_string(attr->attributes, name_buf, estrndup(value_buf.c, value_buf.len), 0);
efree(name_buf);
smart_str_free(&value_buf);
}
return attr;
}
static void php_mimepart_free_child(php_mimepart **part)
{
TSRMLS_FETCH();
php_mimepart_free(*part TSRMLS_CC);
}
PHP_MAILPARSE_API php_mimepart *php_mimepart_alloc(void)
{
php_mimepart *part = ecalloc(1, sizeof(php_mimepart));
part->part_index = 1;
zend_hash_init(&part->children, 0, NULL, (dtor_func_t)php_mimepart_free_child, 0);
MAKE_STD_ZVAL(part->headerhash);
array_init(part->headerhash);
MAKE_STD_ZVAL(part->source.zval);
/* begin in header parsing mode */
part->parsedata.in_header = 1;
part->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, part, php_mailparse_le_mime_part());
return part;
}
PHP_MAILPARSE_API void php_mimepart_free(php_mimepart *part TSRMLS_DC)
{
if (part->rsrc_id) {
long tmp = part->rsrc_id;
part->rsrc_id = 0;
zend_list_delete(tmp);
if (part->parent != NULL && part->parent->rsrc_id > 0)
return;
}
/* free contained parts */
zend_hash_destroy(&part->children);
STR_FREE(part->mime_version);
STR_FREE(part->content_transfer_encoding);
STR_FREE(part->charset);
STR_FREE(part->boundary);
STR_FREE(part->content_base);
STR_FREE(part->content_location);
if (part->content_type) {
php_mimeheader_free(part->content_type);
part->content_type = NULL;
}
if (part->content_disposition) {
php_mimeheader_free(part->content_disposition);
part->content_disposition = NULL;
}
smart_str_free(&part->parsedata.workbuf);
smart_str_free(&part->parsedata.headerbuf);
FREE_ZVAL(part->source.zval);
zval_ptr_dtor(&part->headerhash);
efree(part);
}
static void php_mimepart_update_positions(php_mimepart *part, size_t newendpos, size_t newbodyend, size_t deltanlines)
{
while(part) {
part->endpos = newendpos;
part->bodyend = newbodyend;
part->nlines += deltanlines;
if (!part->parsedata.in_header)
part->nbodylines += deltanlines;
part = part->parent;
}
}
PHP_MAILPARSE_API char *php_mimepart_attribute_get(struct php_mimeheader_with_attributes *attr, char *attrname)
{
zval **attrval;
if (SUCCESS == zend_hash_find(Z_ARRVAL_P(attr->attributes), attrname, strlen(attrname)+1, (void**)&attrval))
return Z_STRVAL_PP(attrval);
return NULL;
}
#define STR_SET_REPLACE(ptr, newval) do { STR_FREE(ptr); ptr = estrdup(newval); } while(0)
static int php_mimepart_process_header(php_mimepart *part TSRMLS_DC)
{
php_rfc822_tokenized_t *toks;
char *header_key, *header_val, *header_val_stripped;
zval **zheaderval;
if (part->parsedata.headerbuf.len == 0)
return SUCCESS;
smart_str_0(&part->parsedata.headerbuf);
/* parse the header line */
toks = php_mailparse_rfc822_tokenize((const char*)part->parsedata.headerbuf.c, 0 TSRMLS_CC);
/* 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 */
if ((strcmp(header_key, "to") == 0 || strcmp(header_key, "cc") == 0) &&
SUCCESS == zend_hash_find(Z_ARRVAL_P(part->headerhash), header_key, strlen(header_key)+1, (void**)&zheaderval)) {
int newlen;
char *newstr;
newlen = strlen(header_val) + Z_STRLEN_PP(zheaderval) + 3;
newstr = emalloc(newlen);
strcpy(newstr, Z_STRVAL_PP(zheaderval));
strcat(newstr, ", ");
strcat(newstr, header_val);
add_assoc_string(part->headerhash, header_key, newstr, 0);
} else {
add_assoc_string(part->headerhash, header_key, header_val, 1);
}
/* 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 php_mimepart *alloc_new_child_part(php_mimepart *parentpart, size_t startpos, int inherit)
{
php_mimepart *child = php_mimepart_alloc();
int ret;
parentpart->parsedata.lastpart = child;
child->parent = parentpart;
child->source.kind = parentpart->source.kind;
if (parentpart->source.kind != mpNONE) {
*child->source.zval = *parentpart->source.zval;
zval_copy_ctor(child->source.zval);
}
ret = zend_hash_next_index_insert(&parentpart->children, (void*)&child, sizeof(php_mimepart *), NULL);
child->startpos = child->endpos = child->bodystart = child->bodyend = startpos;
if (inherit) {
if (parentpart->content_transfer_encoding)
child->content_transfer_encoding = estrdup(parentpart->content_transfer_encoding);
if (parentpart->charset)
child->charset = estrdup(parentpart->charset);
}
return child;
}
PHP_MAILPARSE_API void php_mimepart_get_offsets(php_mimepart *part, off_t *start, off_t *end, off_t *start_body, int *nlines, int *nbodylines)
{
*start = part->startpos;
*end = part->endpos;
*nlines = part->nlines;
*nbodylines = part->nbodylines;
*start_body = part->bodystart;
/* Adjust for newlines in mime parts */
if (part->parent) {
*end = part->bodyend;
if (*nlines)
--*nlines;
if (*nbodylines)
--*nbodylines;
}
}
static int php_mimepart_process_line(php_mimepart *workpart TSRMLS_DC)
{
size_t origcount, linelen;
char *c;
/* sanity check */
if (zend_hash_num_elements(&workpart->children) > MAXPARTS) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "MIME message too complex");
return FAILURE;
}
c = workpart->parsedata.workbuf.c;
smart_str_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) {
int 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 (isspace((int)(unsigned char)*c)) {
/* save header for possible continuation without the first char */
smart_str_appendl(&workpart->parsedata.headerbuf, c+1, linelen-1);
} else {
php_mimepart_process_header(workpart TSRMLS_CC);
/* save header for possible continuation */
smart_str_appendl(&workpart->parsedata.headerbuf, c, linelen);
}
} else {
/* end of headers */
php_mimepart_process_header(workpart TSRMLS_CC);
/* 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;
}
PHP_MAILPARSE_API int php_mimepart_parse(php_mimepart *part, const char *buf, size_t bufsize TSRMLS_DC)
{
size_t len;
while(bufsize > 0) {
/* look for EOL */
for (len = 0; len < bufsize; len++)
if (buf[len] == '\n')
break;
if (len < bufsize && buf[len] == '\n') {
++len;
smart_str_appendl(&part->parsedata.workbuf, buf, len);
php_mimepart_process_line(part TSRMLS_CC);
part->parsedata.workbuf.len = 0;
} else {
smart_str_appendl(&part->parsedata.workbuf, buf, len);
}
buf += len;
bufsize -= len;
}
return SUCCESS;
}
static int enum_parts_recurse(php_mimepart_enumerator *top, php_mimepart_enumerator **child,
php_mimepart *part, mimepart_enumerator_func callback, void *ptr TSRMLS_DC)
{
php_mimepart_enumerator next;
php_mimepart **childpart;
HashPosition pos;
*child = NULL;
if (FAILURE == (*callback)(part, top, ptr TSRMLS_CC))
return FAILURE;
*child = &next;
next.id = 1;
if (CONTENT_TYPE_ISL(part, "multipart/", 10))
next.id = 0;
zend_hash_internal_pointer_reset_ex(&part->children, &pos);
while (SUCCESS == zend_hash_get_current_data_ex(&part->children, (void**)&childpart, &pos)) {
if (next.id)
if (FAILURE == enum_parts_recurse(top, &next.next, *childpart, callback, ptr TSRMLS_CC))
return FAILURE;
next.id++;
zend_hash_move_forward_ex(&part->children, &pos);
}
return SUCCESS;
}
PHP_MAILPARSE_API void php_mimepart_enum_parts(php_mimepart *part, mimepart_enumerator_func callback, void *ptr TSRMLS_DC)
{
php_mimepart_enumerator top;
top.id = 1;
enum_parts_recurse(&top, &top.next, part, callback, ptr TSRMLS_CC);
}
PHP_MAILPARSE_API void php_mimepart_enum_child_parts(php_mimepart *part, mimepart_child_enumerator_func callback, void *ptr TSRMLS_DC)
{
HashPosition pos;
php_mimepart **childpart;
int index = 0;
zend_hash_internal_pointer_reset_ex(&part->children, &pos);
while (SUCCESS == zend_hash_get_current_data_ex(&part->children, (void**)&childpart, &pos)) {
if (FAILURE == (*callback)(part, *childpart, index, ptr TSRMLS_CC))
return;
zend_hash_move_forward_ex(&part->children, &pos);
index++;
}
}
struct find_part_struct {
const char *searchfor;
php_mimepart *foundpart;
};
static int find_part_callback(php_mimepart *part, php_mimepart_enumerator *id, void *ptr TSRMLS_DC)
{
struct find_part_struct *find = ptr;
const unsigned char *num = (const unsigned char*)find->searchfor;
unsigned int n;
while (id) {
if (!isdigit((int)*num))
return SUCCESS;
/* convert from decimal to int */
n = 0;
while (isdigit((int)*num))
n = (n * 10) + (*num++ - '0');
if (*num) {
if (*num != '.')
return SUCCESS;
num++;
}
if (n != (unsigned int)id->id) {
return SUCCESS;
}
id = id->next;
}
if (*num == 0)
find->foundpart = part;
return SUCCESS;
}
PHP_MAILPARSE_API php_mimepart *php_mimepart_find_by_name(php_mimepart *parent, const char *name TSRMLS_DC)
{
struct find_part_struct find = { name, NULL };
php_mimepart_enum_parts(parent, find_part_callback, &find TSRMLS_CC);
return find.foundpart;
}
PHP_MAILPARSE_API php_mimepart *php_mimepart_find_child_by_position(php_mimepart *parent, int position TSRMLS_DC)
{
HashPosition pos;
php_mimepart **childpart = NULL;
zend_hash_internal_pointer_reset_ex(&parent->children, &pos);
while(position-- > 0)
if (FAILURE == zend_hash_move_forward_ex(&parent->children, &pos))
return NULL;
if (FAILURE == zend_hash_get_current_data_ex(&parent->children, (void**)&childpart, &pos))
return NULL;
if(childpart) {
return *childpart;
} else {
return NULL;
}
}
static int filter_into_work_buffer(int c, void *dat MAILPARSE_MBSTRING_TSRMLS_DC)
{
php_mimepart *part = dat;
MAILPARSE_MBSTRING_TSRMLS_FETCH_IF_BRAIN_DEAD();
smart_str_appendc(&part->parsedata.workbuf, c);
if (part->parsedata.workbuf.len >= 4096) {
part->extract_func(part, part->extract_context, part->parsedata.workbuf.c, part->parsedata.workbuf.len TSRMLS_CC);
part->parsedata.workbuf.len = 0;
}
return c;
}
PHP_MAILPARSE_API void php_mimepart_decoder_prepare(php_mimepart *part, int do_decode, php_mimepart_extract_func_t decoder, void *ptr TSRMLS_DC)
{
enum mbfl_no_encoding from = mbfl_no_encoding_8bit;
if (do_decode && part->content_transfer_encoding) {
from = mbfl_name2no_encoding(part->content_transfer_encoding);
if (from == mbfl_no_encoding_invalid) {
if (strcasecmp("binary", part->content_transfer_encoding) != 0) {
zend_error(E_WARNING, "%s(): mbstring doesn't know how to decode %s transfer encoding!",
get_active_function_name(TSRMLS_C),
part->content_transfer_encoding);
}
from = mbfl_no_encoding_8bit;
}
}
part->extract_func = decoder;
part->extract_context = ptr;
part->parsedata.workbuf.len = 0;
if (do_decode) {
if (from == mbfl_no_encoding_8bit || from == mbfl_no_encoding_7bit) {
part->extract_filter = NULL;
} else {
part->extract_filter = mbfl_convert_filter_new(
from, mbfl_no_encoding_8bit,
filter_into_work_buffer,
NULL,
part
MAILPARSE_MBSTRING_TSRMLS_CC
);
}
}
}
PHP_MAILPARSE_API void php_mimepart_decoder_finish(php_mimepart *part TSRMLS_DC)
{
if (part->extract_filter) {
mbfl_convert_filter_flush(part->extract_filter MAILPARSE_MBSTRING_TSRMLS_CC);
mbfl_convert_filter_delete(part->extract_filter MAILPARSE_MBSTRING_TSRMLS_CC);
}
if (part->extract_func && part->parsedata.workbuf.len > 0) {
part->extract_func(part, part->extract_context, part->parsedata.workbuf.c, part->parsedata.workbuf.len TSRMLS_CC);
part->parsedata.workbuf.len = 0;
}
}
PHP_MAILPARSE_API int php_mimepart_decoder_feed(php_mimepart *part, const char *buf, size_t bufsize TSRMLS_DC)
{
if (buf && bufsize) {
int i;
if (part->extract_filter) {
for (i = 0; i < bufsize; i++) {
if (mbfl_convert_filter_feed(buf[i], part->extract_filter MAILPARSE_MBSTRING_TSRMLS_CC) < 0) {
zend_error(E_WARNING, "%s() - filter conversion failed. Input message is probably incorrectly encoded\n",
get_active_function_name(TSRMLS_C));
return -1;
}
}
} else {
return part->extract_func(part, part->extract_context, buf, bufsize TSRMLS_CC);
}
}
return 0;
}
PHP_MAILPARSE_API void php_mimepart_remove_from_parent(php_mimepart *part TSRMLS_DC)
{
php_mimepart *parent = part->parent;
HashPosition pos;
php_mimepart **childpart;
if (parent == NULL)
return;
part->parent = NULL;
zend_hash_internal_pointer_reset_ex(&parent->children, &pos);
while(SUCCESS == zend_hash_get_current_data_ex(&parent->children, (void**)&childpart, &pos)) {
if (SUCCESS == zend_hash_get_current_data_ex(&parent->children, (void**)&childpart, &pos)) {
if (*childpart == part) {
ulong h;
zend_hash_get_current_key_ex(&parent->children, NULL, NULL, &h, 0, &pos);
zend_hash_index_del(&parent->children, h);
break;
}
}
zend_hash_move_forward_ex(&parent->children, &pos);
}
}
PHP_MAILPARSE_API void php_mimepart_add_child(php_mimepart *part, php_mimepart *child TSRMLS_DC)
{
}
syntax highlighted by Code2HTML, v. 0.9.1