ZF-7874: Zend_Mail_Transport should not add charset parameter to multipart Content-Type header


When generating a multipart (text and html) message, Zend_Mail includes the following in the headers:

Content-Type: multipart/alternative; charset=iso-8859-1;

As far as I can tell, the charset parameter is not appropriate to a multipart/alternative Content-Type declaration.

from RFC 2045, p. 10:

For example, the "charset" parameter is applicable to any subtype of "text", while the "boundary" parameter is required for any subtype of the "multipart" media type.

At… I found this:

The charset parameter isn't valid in the headers of ANY multipart type. Neither the multipart/related specification nor any example in any specification suggests that it should be allowed. It only applies to text and to text-related formats such as application/xml. The body of a multipart/related document is not text so it cannot have a charset. It is a collection of parts which are required to consist of valid 7-bit ASCII headers combined with data in whatever encoding is specified in the content-type and content-encoding headers for the relevant part.

The inclusion of the charset parameter causes at least one webmail client (UebiMiau 2.7.10) to choke on the message.

modifying the _getHeaders() fucntion in Zend/Mail/Transport/Abstract.php to change this:

          $this->_headers['Content-Type'] = array(
                $type . '; charset=' . $this->_mail->getCharset() . ';'
                . $this->EOL
                . " " . 'boundary="' . $boundary . '"'

to this

           $this->_headers['Content-Type'] = array(
                $type . ';'
                . $this->EOL
                . " " . 'boundary="' . $boundary . '"'
            $this->boundary = $boundary;

Solved the problem with the web client

Also, if a message generated by the existing code is run through message lint at, it warns: "WARNING: Unexpected parameter 'charset' in header 'Content-Type'"


This fix seems to work for me:

protected function _getHeaders($boundary) { if (null !== $boundary) { // Build multipart mail $type = $this->_mail->getType(); if (!$type) { if ($this->_mail->hasAttachments) { $type = Zend_Mime::MULTIPART_MIXED; } elseif ($this->_mail->getBodyText() && $this->_mail->getBodyHtml()) { $type = Zend_Mime::MULTIPART_ALTERNATIVE; } else { $type = Zend_Mime::MULTIPART_MIXED; } }

        $addCharset = !in_array($type, array( 

        $this->_headers['Content-Type'] = array(
            $type . ( $addCharset ? ( '; charset=' . $this->_mail->getCharset() ) : "" ) . ';'
            . $this->EOL
            . " " . 'boundary="' . $boundary . '"'
        $this->boundary = $boundary;

    $this->_headers['MIME-Version'] = array('1.0');

    return $this->_headers;

Mr. Baltz's proposed fix assumes that there is some Multipart type for which charset is a valid header parameter. The reference I quoted from the IBM site is categorical that this is not so. Is there some authority to the contrary?

Zend_Mime only contemplates three multipart types: MULTIPART_ALTERNATIVE, MULTIPART_MIXED and MULTIPART_RELATED. Zend_Mail only permits those three types (see Zend_Mail::setType). Thus, this fix would permit a charset header parameter for the MULTIPART_RELATED type only. That type is defined in Nothing in that definition suggests that charset is a valid header parameter for that type.

It seems odd that Zend_Mime::MULTIPART_MIXED is mentioned twice in this part of the _getHeaders function (this is presumably why Mr. Baltz includes Zend_Mime::MULTIPART_MIXED twice in the array his code adds):

if (!$type) { if ($this->_mail->hasAttachments) { $type = Zend_Mime::MULTIPART_MIXED; } elseif ($this->_mail->getBodyText() && $this->_mail->getBodyHtml()) { $type = Zend_Mime::MULTIPART_ALTERNATIVE; } else { $type = Zend_Mime::MULTIPART_MIXED; } }

I wonder if the code I have just quoted should have been:

if (!$type) { if ($this->_mail->hasAttachments) { $type = Zend_Mime::MULTIPART_MIXED; } elseif ($this->_mail->getBodyText() && $this->_mail->getBodyHtml()) { $type = Zend_Mime::MULTIPART_ALTERNATIVE; } else { $type = Zend_Mime::MULTIPART_RELATED; } }

In any event, if charset is not a valid parameter in the header of any multipart type (as opposed to the header of a part), then its seems better (and simpler) just to eliminate it from the _getHeaders() function altogether.

Thank you for the suggestions, Oliver Baltz and O Gray . I'm working now, and I'll be happy if you give me several weeks, Thanks ;-)

Solved in SVN r18759 of trunk, r18760 of 1.9 branch.

But 2 Zend_Mime::MULTIPART_MIXEDs seem to be correct.

I think 2nd Zend_Mime::MULTIPART_MIXED is default value if script can not set the type automatically.