Search code examples
phpemailimapwebmail

Fetching email using PHP imap messes up inline embedded images


I made a webmail php class to fetch my domain's webmail. Everything runs perfectly except that when the email has embedded images (like signatures), the images are fetched with CID in their src and also are treated as attachments.

<img src="cid:auto_cid_2093934881" />

So i end up with a broken source image (in its original place in the email body) and an attachment of the same image that actually works. But i don't need the image to be an attachment, i just want the images to be shown in their places.

Can anyone help me fix this class so it can fetch the images correctly?

Here's my code to get a single email:

<?php
    $uid=mysql_real_escape_string($_GET['email_id']);
    ini_set("max_execution_time",360);

    /* connect to server */
    $hostname = '{***.com:143/notls}INBOX';
    $username = 'fadi@***.com';
    $password = '*******';

    /* try to connect */
    $inbox = imap_open($hostname,$username,$password) or die('Cannot connect to domain:' . imap_last_error());

    $overview = imap_fetch_overview($inbox,$uid,0);
    $header = imap_headerinfo($inbox, $uid);
    $fromaddr = $header->from[0]->mailbox . "@" . $header->from[0]->host;

    $webmail = new Webmail();
    $message = $webmail->getEmailBody($uid,$inbox);

    $attachments = $webmail->extract_attachments($inbox,$uid);
    $attach_hrefs = '';
    if(count($attachments)!=0){

        $target_dir = "emails/".$uid;
        if (!is_dir($target_dir)) {
          // dir doesn't exist, make it
          mkdir($target_dir);
        }

        foreach($attachments as $at){

            if($at[is_attachment]==1){

                file_put_contents($target_dir.'/'.$at[filename], $at[attachment]);
                $attach_hrefs .='<a target="new" href="'.$target_dir.'/'.$at[filename].'">'.$at[filename].'</a> ';

            }
        }

    }
?>

Here's the webmail class:

<?php
class Webmail{

function getEmailBody($uid, $imap)
{
    $body = $this->get_part($imap, $uid, "TEXT/HTML");
    // if HTML body is empty, try getting text body
    if ($body == "") {
        $body = $this->get_part($imap, $uid, "TEXT/PLAIN");
    }
    return $body;
}

function get_part($imap, $uid, $mimetype, $structure = false, $partNumber = false)
{
    if (!$structure) {
        $structure = imap_fetchstructure($imap, $uid, FT_UID);
    }
    if ($structure) {
        if ($mimetype == $this->get_mime_type($structure)) {
            if (!$partNumber) {
                $partNumber = 1;
            }
            $text = imap_fetchbody($imap, $uid, $partNumber, FT_UID);
            switch ($structure->encoding) {
                case 3:
                    return imap_base64($text);
                case 4:
                    return imap_qprint($text);
                default:
                    return $text;
            }
        }

        // multipart
        if ($structure->type == 1) {
            foreach ($structure->parts as $index => $subStruct) {
                $prefix = "";
                if ($partNumber) {
                    $prefix = $partNumber . ".";
                }
                $data = $this->get_part($imap, $uid, $mimetype, $subStruct, $prefix . ($index + 1));
                if ($data) {
                    return $data;
                }
            }
        }
    }
    return false;
}

function get_mime_type($structure)
{
    $primaryMimetype = ["TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER"];

    if ($structure->subtype) {
        return $primaryMimetype[(int)$structure->type] . "/" . $structure->subtype;
    }
    return "TEXT/PLAIN";
}
function extract_attachments($connection, $message_number) {

    $attachments = array();
    $structure = imap_fetchstructure($connection, $message_number);

    if(isset($structure->parts) && count($structure->parts)) {

        for($i = 0; $i < count($structure->parts); $i++) {

            $attachments[$i] = array(
                'is_attachment' => false,
                'filename' => '',
                'name' => '',
                'attachment' => ''
            );

            if($structure->parts[$i]->ifdparameters) {
                foreach($structure->parts[$i]->dparameters as $object) {
                    if(strtolower($object->attribute) == 'filename') {
                        $attachments[$i]['is_attachment'] = true;
                        $attachments[$i]['filename'] = $object->value;
                    }
                }
            }

            if($structure->parts[$i]->ifparameters) {
                foreach($structure->parts[$i]->parameters as $object) {
                    if(strtolower($object->attribute) == 'name') {
                        $attachments[$i]['is_attachment'] = true;
                        $attachments[$i]['name'] = $object->value;
                    }
                }
            }

            if($attachments[$i]['is_attachment']) {
                $attachments[$i]['attachment'] = imap_fetchbody($connection, $message_number, $i+1);
                if($structure->parts[$i]->encoding == 3) { // 3 = BASE64
                    $attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
                }
                elseif($structure->parts[$i]->encoding == 4) { // 4 = QUOTED-PRINTABLE
                    $attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
                }
            }

        }

    }

    return $attachments;

  }
}
?>

Thanks in advance.


Solution

  • Try bellow class for fetch mail body with inline image
    Note See get_mail_body($body_type = 'html') method in class to that store mail body inline image to local server you need to change it as you directory layout.

    I hope this help you.

        $email_message = new Email_message();
        $mailbody = $email_message->get_mail_body();
    

    ;

    class Email_message {
    
        public $connection;
        public $messageNumber;    
        public $bodyHTML = '';
        public $bodyPlain = '';
        public $attachments;
        public $getAttachments = true;
    
        public function __construct($config_data = array()) {
    
            $this->connection = $config_data['connection'];
            $this->messageNumber = isset($config_data['message_no'])?$config_data['message_no']:1;
        }
    
        public function fetch() {
    
            $structure = @imap_fetchstructure($this->connection, $this->messageNumber);
            if(!$structure) {
                return false;
            }
            else {
                if(isset($structure->parts)){
                    $this->recurse($structure->parts);
                }
                return true;
            }
    
        }
    
        public function recurse($messageParts, $prefix = '', $index = 1, $fullPrefix = true) {
    
            foreach($messageParts as $part) {
    
                $partNumber = $prefix . $index;
    
                if($part->type == 0) {
                    if($part->subtype == 'PLAIN') {
                        $this->bodyPlain .= $this->getPart($partNumber, $part->encoding);
                    }
                    else {
                        $this->bodyHTML .= $this->getPart($partNumber, $part->encoding);
                    }
                }
                elseif($part->type == 2) {
                    $msg = new Email_message(array('connection' =>$this->connection,'message_no'=>$this->messageNumber));
                    $msg->getAttachments = $this->getAttachments;
                    if(isset($part->parts)){
                        $msg->recurse($part->parts, $partNumber.'.', 0, false);
                    }
                    $this->attachments[] = array(
                        'type' => $part->type,
                        'subtype' => $part->subtype,
                        'filename' => '',
                        'data' => $msg,
                        'inline' => false,
                    );
                }
                elseif(isset($part->parts)) {
                    if($fullPrefix) {
                        $this->recurse($part->parts, $prefix.$index.'.');
                    } else {
                        $this->recurse($part->parts, $prefix);
                    }
                }
                elseif($part->type > 2) {
                    if(isset($part->id)) {
                        $id = str_replace(array('<', '>'), '', $part->id);
                        $this->attachments[$id] = array(
                            'type' => $part->type,
                            'subtype' => $part->subtype,
                            'filename' => $this->getFilenameFromPart($part),
                            'data' => $this->getAttachments ? $this->getPart($partNumber, $part->encoding) : '',
                            'inline' => true,
                        );
                    } else {
                        $this->attachments[] = array(
                            'type' => $part->type,
                            'subtype' => $part->subtype,
                            'filename' => $this->getFilenameFromPart($part),
                            'data' => $this->getAttachments ? $this->getPart($partNumber, $part->encoding) : '',
                            'inline' => false,
                        );
                    }
                }
    
                $index++;
    
            }
    
        }
    
        function getPart($partNumber, $encoding) {
    
            $data = imap_fetchbody($this->connection, $this->messageNumber, $partNumber);
            switch($encoding) {
                case 0: return $data; // 7BIT
                case 1: return $data; // 8BIT
                case 2: return $data; // BINARY
                case 3: return base64_decode($data); // BASE64
                case 4: return quoted_printable_decode($data); // QUOTED_PRINTABLE
                case 5: return $data; // OTHER
            }
    
    
        }
    
        function getFilenameFromPart($part) {
    
            $filename = '';
    
            if($part->ifdparameters) {
                foreach($part->dparameters as $object) {
                    if(strtolower($object->attribute) == 'filename') {
                        $filename = $object->value;
                    }
                }
            }
    
            if(!$filename && $part->ifparameters) {
                foreach($part->parameters as $object) {
                    if(strtolower($object->attribute) == 'name') {
                        $filename = $object->value;
                    }
                }
            }
    
            return $filename;
    
        }
    
        function get_mail_body($body_type = 'html')
        {
            $mail_body = '';
            if($body_type == 'html'){
                $this->fetch();
                preg_match_all('/src="cid:(.*)"/Uims', $this->bodyHTML, $matches);
                if(count($matches)) {
                    $search = array();
                    $replace = array();
                    foreach($matches[1] as $match) {
                        $unique_filename = time().".".strtolower($this->attachments[$match]['subtype']);
                        file_put_contents("./uploads/$unique_filename", $this->attachments[$match]['data']);
                        $search[] = "src=\"cid:$match\"";
                        $replace[] = "src='".base_url()."/uploads/$unique_filename'";
                    }
                    $this->bodyHTML = str_replace($search, $replace, $this->bodyHTML);
                    $mail_body = $this->bodyHTML;
                }
            }else{
                    $mail_body = $this->bodyPlain;
            }
            return $mail_body;
        }
    
    }