Search code examples
phpemailmime

email sql attachment issue


I know there are a lot of questions similar to mine and yes, I have had a look through some of them and none of them seemed to answer my question.

Basically I have created a script for a client which creates a dump of their database and emails it to them, I'll post the code below, now the database information is generated via the script and no physical sql file is created on the server, instead I am adding it all to a variable and then using the variable to attach the database information to the email headers.

Also please note I have used the code for email attachments before and it works fine, but in that instance the file did physically exist so I'm beginning to think that is the problem.

<?php

    //Script configuration section.
    $D_hostname = 'localhost'; # Database hostname.
    $D_username = 'username'; # Database username.
    $D_password = 'password'; # Database password.
    $D_database = 'database'; # Database name.

    $E_from = '{BLOG BACKUP} <backup@mywebsite.com>'; # From header.
    $E_subject = '[' . date('d-m-Y') .  ']: This weeks database backup.'; # Email subject.
    $E_content = 'This weeks database backup has been created, please find it attached to this email. Remember to save a copy and retain this email too!<br />'; #Email content.
    $E_filename = date('d-m-Y') .  '_blog_backup.sql'; #Attachment filename.
    $E_filetype = 'application/octet-stream'; # Attachment type.
    $E_recipients = 'email@mywebsite.com, email@myclient.com'; #Email recipients.

    //Connect to the database.
    $connection = mysql_connect($D_hostname, $D_username, $D_password);

    //Select the database
    mysql_select_db($D_database, $connection);

    //Set up an empty array.
    $tables = array();

    //Get a list of all the databases tables.
    $Q_show_tables = mysql_query('SHOW TABLES');

    //Add each table to the array.
    while($R_show_tables = mysql_fetch_row($Q_show_tables)){
        $tables[] = $R_show_tables[0];
    }

    //Get all rows for each table and create a mySQL query.
    foreach($tables as $table) {

        //Get all rows from the table.
        $Q_get_rows = mysql_query('SELECT * FROM '.$table);
        $C_get_rows = mysql_num_fields($Q_get_rows);

        //Add a drop clause for the table
        $data_row .= 'DROP TABLE ' . $table . ';';

        //Get create table info.
        $Q_create_table = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));

        //Add the create table clause to the query.
        $data_row.= "\n\n" . $Q_create_table[1] . ";\n\n";

        //Now gather all of the rows.
        for ($int_01 = 0; $int_01 < $C_get_rows; $int_01++) {
            while($R_get_rows = mysql_fetch_row($Q_get_rows)) {

                //Add the insert clause.
                $data_row .= 'INSERT INTO ' . $table . ' VALUES (';

                //For each field write out a row.
                for($int_02 = 0; $int_02 < $C_get_rows; $int_02++) {
                    $R_get_rows[$int_02] = addslashes($R_get_rows[$int_02]);
                    $R_get_rows[$int_02] = ereg_replace("\n","\\n",$R_get_rows[$int_02]);
                    if (isset($R_get_rows[$int_02])) {
                        $data_row .= '"'.$R_get_rows[$int_02].'"' ;
                    } else {
                        $data_row .= '""';
                    }
                    if ($int_02 < ($num_fields-1)) { 
                        $data_row .= ','; 
                    }
                }
            $data_row .= ");\n";
            }
        }
        $data_row .="\n\n\n";
    }

    //Split and encode the data.
    $data_split = chunk_split(base64_encode($data_row));

    //Create a unique boundary
    $new_boundary = md5(time());

    //This is your basic header information such as who the email is from and the date it was sent.
    $mail_header .= 'From: ' . $E_from . "\r\n";
    $mail_header .= 'Date: ' . date('r') . "\r\n";

    //This part of the header first defines to the email client that it is a multipart message and adds the emails content/body.
    $mail_header .= 'Content-Type: multipart/mixed; boundary="' . $new_boundary . '"' . "\r\n\r\n";
    $mail_header .= 'MIME-Version: 1.0' . "\r\n";
    $mail_header .= 'This is a multi-part message in MIME format' . "\r\n";
    $mail_header .= '--' . $new_boundary . "\r\n";
    $mail_header .= 'Content-Type:text/html; charset="iso-8859-1"' . "\r\n";
    $mail_header .= 'Content-Transfer-Encoding: 7bit' . "\r\n\r\n";
    $mail_header .= $E_content . "\r\n\r\n";

    //This part of the header is for the attachment and includes the contents of the file, the files name and other information.
    $mail_header .= '--' . $new_boundary . "\r\n";
    $mail_header .= 'Content-Type: ' . $E_filetype . '; name="' . $E_filename . '"' . "\r\n";
    $mail_header .= 'Content-Transfer-Encoding: base64' . "\r\n";
    $mail_header .= 'Content-Disposition: attachment; filename="' . $E_filename . '"' . "\r\n\r\n";
    $mail_header .= $data_split . "\r\n\r\n";

    //This is needed to stop any rendering errors by email clients and signifies the end of the emails header.
    $mail_header .= '--' . $new_boundary  . '--' . "\r\n";

    //This mails out all of the above.
    mail($E_recipients, $E_subject, '', $mail_header);

?>

The problem: When the email is sent the attachment and the message content do not show at all in horde mail client or hotmail, yet it works fine in gmail. I'm guessing it's a problem with the mail header but I am unable to see any errors.

Can anyone see any issues with the email headers? if so can you advise me on how to fix the problem?

As always and help is greatly appreciated.


Solution

  • Holy moly!

    Creating a correctly formatted email message with mime objects is not a trivial task. I've hacked the metamail code but wouldn't dream of writing my own PHP code to handle it. Where I've not got a native implementation of a mime capable mail client I can call from PHP (metamail, mutt), I'd use one of the freely available libs to handle it (phpmailer, swiftmailer being obvious candidates).

    Another thing I'd tend to avoid would be creating a representation of a database backup in memory in a PHP script.

    Some other things I wouldn't do are:

    • using addslashes to escape DB input
    • tampering with the nature of causality
    • avoid using modular programming where there are concerns to be seperated
    • transfering potentially very large files via email (most MTAs will limit the size of emails)

    I'd recommend starting again - setup a cron job to extract the backup to a local file then write a PHP to gate access to the file - and email the user a link to where they can download the backup.