3

enter image description here

I got a script which sends out an automated email when a function runs. I want to be able to send the HTML email along with a PDF attachment. I know I need to encode the file in to Base64 however I am just getting base64 code attached to the bottom of my email. I assume it's something to do with the mime stuff. Anyone see the issue?

    $to = 'example@example.com';

    $subject = 'test!';

    $file = file_get_contents("files/CAPS-Standing-Order.pdf");
    $encoded_file = chunk_split(base64_encode($file));

    // message
    $boundary = md5("sanwebe");

    $message = 'Hello';

    // To send HTML mail, the Content-type header must be set
    $headers  = 'MIME-Version: 1.0' . "\r\n";
    $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";

    // Additional headers
    $headers .= 'From: CAPS Consortium <contact@capsconsortium.com>' . "\r\n";

    $message .= "--$boundary\r\n";
    $message .="Content-Type: pdf; name=\"CAPS-Standing-Order.pdf\"\r\n";
    $message .="Content-Disposition: attachment; filename=\"CAPS-Standing-Order.pdf\"\r\n";
    $message .="Content-Transfer-Encoding: base64\r\n";
    $message .="X-Attachment-Id: ".rand(1000,99999)."\r\n\r\n";
    $message .= $encoded_file; 

    // Mail it
    mail($to, $subject, $message, $headers);
Ryan
  • 161
  • 2
  • 14
  • 2
    simple: don't build your own mime emails. use a proper mail package like swiftmailer or phpmailer. all of that header/encode/split business will be reduced to a single `->addAttachment('somefile.pdf')`, and you can get on with more important things, like getting more coffee. – Marc B Mar 10 '16 at 14:45
  • 1
    `$headers .= "Content-type: multipart/mixed;boundary=\"".$boundary."\"";` – James Lalor Mar 10 '16 at 14:49

1 Answers1

4
<?php

$file = file_get_contents("files/CAPS-Standing-Order.pdf");
$encoded_file = chunk_split(base64_encode($file));

$attachments[] = array(
    'name' => 'CAPS-Standing-Order.pdf', // Set File Name
    'data' => $encoded_file, // File Data
    'type' => 'application/pdf', // Type
    'encoding' => 'base64' // Content-Transfer-Encoding
);

$this->sendMail("example@example.com", "Hello", "test!", $attachments); 
// Send the actual mail and include the attachments

The function I made to send cleaner mail

<?php
function sendMail($email = "", $text = "", $subject = "", $attachments = array()) {
    if(!$email || !$text) {
        return false;
    }

    $headers   = array();
    $headers[] = "To: {$email}";
    $headers[] = "From: CAPS Consortium <contact@capsconsortium.com>";
    $headers[] = "Reply-To: CAPS Consortium <contact@capsconsortium.com>";
    $headers[] = "Subject: {$subject}";
    $headers[] = "X-Mailer: PHP/".phpversion();

    $headers[] = "MIME-Version: 1.0";

    if(!empty($attachments)) {
        $boundary = md5(time());
        $headers[] = "Content-type: multipart/mixed;boundary=\"".$boundary."\"";
        // Have attachment, different content type and boundary required.
    } else {
        $headers[] = "Content-type: text/html; charset=UTF-8";
    }

    $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <title>CAPS Consortium</title>
            <style>table { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; }</style>
        </head>
        <body style="font-family: arial;" width="100%">
            [text]
        </body>
    </html>';

    $generated = date('jS M Y H:i:s');
    $subject = ($subject ? $subject : 'Default Subject');
    $message = $html;

    $message = str_replace("[text]", $text, $message);

    if(!empty($attachments)) {
        $output   = array();
        $output[] = "--".$boundary;
        $output[] = "Content-type: text/html; charset=\"utf-8\"";
        $output[] = "Content-Transfer-Encoding: 8bit";
        $output[] = "";
        $output[] = $message;
        $output[] = "";
        foreach($attachments as $attachment) {
            $output[] = "--".$boundary;
            $output[] = "Content-Type: ".$attachment['type']."; name=\"".$attachment['name']."\";";
            if(isset($attachment['encoding'])) {
                $output[] = "Content-Transfer-Encoding: " . $attachment['encoding'];
            }
            $output[] = "Content-Disposition: attachment;";
            $output[] = "";
            $output[] = $attachment['data'];
            $output[] = "";
        }
        return mail($email, $subject, implode("\r\n", $output), implode("\r\n", $headers));
    } else {
        return mail($email, $subject, $message, implode("\r\n", $headers));
    }

}

Hopefully this helps. Shouldn't require too much explanation as it's pretty much what you have, just cleaner and easier to maintain.

James Lalor
  • 1,236
  • 9
  • 22
  • Scrap that was getting a cached result. The above script will put the email HTML in the attachment of a .txt file and the attachment header and Base64 as the email body. However moving the output of the body above the attachment puts the html and attachment the right way however it will just display the text. – Ryan Mar 10 '16 at 15:33
  • http://prntscr.com/adish6 = example. http://pastebin.com/kJN8Jcxm = current state of the function. – Ryan Mar 10 '16 at 15:34
  • 1
    Try type of application/pdf instead of pdf in attachment array? – James Lalor Mar 10 '16 at 15:56
  • FYI if you dont pass the attachments into the function it works fine. I assume the issue lies within `if(!empty($attachments)) {}` – Ryan Mar 10 '16 at 16:07
  • 1
    Aye, let me try sending a PDF, un momento. – James Lalor Mar 10 '16 at 16:09
  • Let me paste my full script for you including the full HTML markup. – Ryan Mar 10 '16 at 16:23
  • 1
    Change attachment to what I have, and add the lines I added `if(isset($attachment['encoding'])) { $output[] = "Content-Transfer-Encoding: " . $attachment['encoding']; }` inside too. – James Lalor Mar 10 '16 at 16:26
  • 1
    http://imgur.com/PbbGAE1 Using your Email HTML, what I get through my local server. – James Lalor Mar 10 '16 at 16:41
  • what the f**k... Like I said before it looks solid I just can't understand why it doesn't work. Must be some settings. – Ryan Mar 10 '16 at 16:46
  • 1
    Hi I tried the same code on a fresh insall of CentOS and it worked fine. Must be server settings. I can confirm this is the fix. Thanks for all your help. – Ryan Mar 11 '16 at 10:18
  • 1
    To be fair, I just updated this a little, I believe by switching this part: http://pastebin.com/jZcWRH0v might fix it in future issues. – James Lalor Mar 11 '16 at 12:33