When using SMTP together with Html Mail and Mail MIME, where images are being attached to the mail, SMTP module (or phpmailer) breaks the multipart body.

This:

--=_4a1fe9185cfdea2149fc3fd159e3c249
Content-Type: multipart/related;
 boundary="=_97b77c3365369dff3a1c295b9e0b3a42"

--=_97b77c3365369dff3a1c295b9e0b3a42
Content-Transfer-Encoding: 8bit
Content-Type: text/html; charset=UTF-8

is being changed into this:

--b1_4ea5acbeff994bdc4023b2997fc82f8e
Content-Type: text/html; charset = "UTF-8"
Content-Transfer-Encoding: 8Bit


Content-Type: multipart/related;
 boundary="=_ace8ad402627c67f96f9fb6e0c03a4f7"

--=_ace8ad402627c67f96f9fb6e0c03a4f7
Content-Transfer-Encoding: 8bit
Content-Type: text/html; charset=UTF-8

which obviously breaks the html/multi part of the mail.

Comments

simon georges’s picture

simon georges’s picture

Version: 7.x-1.0-beta2 » 7.x-1.x-dev

Could you eventually test the (soon to be released) -dev version, as a few things have been fixed since then?

Robin Miles’s picture

I am seeing the same issue when using 7.x-1.x-dev

I believe the issue arises in the function _remove_headers() which does not take into account the possible presence of Content-Type: multi-part/related; and further boundary markers inserted by mimemail doing its job.

ArtActivator.com’s picture

Priority: Normal » Critical

Subscribing. Still not working. Trying to send a simplenews newsletter with inline images.(using mimemail) Without images all fine.
Can't switch to phpmailer project, beacuse it does not integrates with mailsystem module. But I have logging_alerts installed, and need watchdog messages to be sent with mail() function....

------------------------
www.ArtActivator.com - Order your success

adrianlanzi’s picture

subscribing

adrianlanzi’s picture

Issue summary: View changes

Added last row

pounard’s picture

Issue summary: View changes

I did some debugging, it's more than just that.

When using mimemail, the mimemail module (no matter how I hate it) generates valid and clean full MIME encoded mail. When the SMTP module goes into it, it will change some boundaries, but it doesn't really break the mail, it just changes a few headers and boundaries around, but problem lies at two places.

Somewhere in those lines lies a code smell:

        foreach ($body_parts as $body_part) {
          if (strpos($body_part, 'text/plain')) {
            $text_plain = TRUE;
          }
          if (strpos($body_part, 'text/html')) {
            $text_html = TRUE;
          }
        }

        foreach ($body_parts as $body_part) {
          // If test/plain within the body part, add it to either
          // $mailer->AltBody or $mailer->Body, depending on whether there is
          // also a text/html part ot not.

          // ugly multipart/related with mixed and alternative inside hack
          // But real solution would require a much cleaner code around
          if (strpos($body_part, 'multipart/related')) {
            $mailer->Body = $body;
            $mailer->ContentType = 'multipart/mixed';
          }
          elseif (strpos($body_part, 'multipart/alternative')) {
            // Get boundary ID from the Content-Type header.
            $boundary2 = $this->_get_substring($body_part, 'boundary', '"', '"');
            // Clean up the text.
            $body_part = trim($this->_remove_headers(trim($body_part)));
            // Split the body based on the boundary ID.
            $body_parts2 = $this->_boundary_split($body_part, $boundary2);

In there, SMTP will set up the Body attribute of PHPMailer, when you have a clean fully generated MIME content, if goes into those lines:

          if (strpos($body_part, 'multipart/related')) {
            $mailer->Body = $body;
            $mailer->ContentType = 'multipart/mixed';
          }

What happens here is that the elseif is skipped, and it nevers sets the AltBody. I'm not saying it's not the right thing to do, but it does give wrong content to the PHPMailer class. The main consequence of this if that the PhpMailer class will do something like this:

  private function SetMessageType() {
    if (count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
      $this->message_type = 'plain';
    }
    else {
      if (count($this->attachment) > 0) {
        $this->message_type = 'attachments';
      }
      if (strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
        $this->message_type = 'alt';
      }
      if (strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
        $this->message_type = 'alt_attachments';
      }
    }
  }

Because there is not AltBody (since we didn't gave him one) it will definitely ignore that we have a fully generated MIME message, and set the internal "plain" type. Then:

  public function GetMailMIME() {
    $result = '';
    switch ($this->message_type) {
      case 'plain':
        $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
        $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
        break;
      case 'attachments':
      case 'alt_attachments':
        if ($this->InlineImageExists()) {
          $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
        }
        else {
          $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
        }
        break;
      case 'alt':
        $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
        break;
    }

    if ($this->Mailer != 'mail') {
      $result .= $this->LE . $this->LE;
    }

    return $result;
  }

And BINGO! Boundary has been dropped into the mail headers (moreover it generates a Content-Type header that violates some RFC's because charset must not be set into the headers but each part must set its own). First boundary in the Content-Type not being there, parts are not parsable by the mail client and the user will read the raw MIME encoded mail in its client.

For the record, there is some kind of magic there, because I happened to experience this bug often enough to have an angry customer in front of me, but it happens only when the mimemail generated MIME content exceed a certain size (I was not able to determine). Some work, some other don't.

pounard’s picture

Actually we are using code from this patch: https://www.drupal.org/node/1044534#comment-7921973 which it's the cause of my actual problem.

bripatand’s picture

Is there a work around finally? I have the same problem on 2 different environments. The problem doesn't occur all the time and seems to be dependent on the Body content indeed. Similar problem with attachments.

wundo’s picture

Status: Active » Closed (won't fix)

Closing very old (dead) issues, if you think this is still relevant please re-open.

spelcheck’s picture

Status: Closed (won't fix) » Closed (duplicate)
Parent issue: » #1813164: Mime Mail -> SMTP with embedded images

Duplicate of https://www.drupal.org/project/smtp/issues/1813164. Patch available that Needs Review.

With the patch, HTML Mail will work SMTP so long as Mail Mime is not enabled. Though many are using Mail Mime to auto-embed inline images, this is now done through SMTP itself. Mime Mail has the Link images only option, which prevents it from auto-embedding inline images before sending it to SMTP. Either of these configurations should work:

  • HTML Mail (without Mail Mime) -> SMTP (patched from issue 1813164)
  • Mime Mail (with Link images only option enabled) -> SMTP (patched from issue 1813164)