PHP IMAP decoding messages
Asked Answered
D

5

17

I have emails sent via base64 encoding and 8bit encoding. I was wondering how I could check the encoding of the message using imap_fetchstructure (been doing this for about two hours, so lost) and then decode it properly.

Gmail and Mailbox (app on iOS) are sending it as 8bit while Windows 8's Mail app is sending it as base64. Either way, I need to decode whether its 8bit or base64 by detecting what type of encoding it has used.

Using PHP 5.1.6 (yes, I should update, been busy).

I really have no code to show. This is all I have:

<?php
$hostname = '{********:993/imap/ssl}INBOX';
$username = '*********';
$password = '******';

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

$emails = imap_search($inbox,'ALL');

if($emails) {

    $output = '';

    rsort($emails);

    foreach($emails as $email_number) {

        $overview = imap_fetch_overview($inbox,$email_number,0);
        $message = imap_fetchbody($inbox,$email_number,2);
        $struct = imap_fetchstructure($inbox, $email_number);

        $output.= '<div class="toggle'.($overview[0]->seen ? 'read' : 'unread').'">';
        $output.= '<span class="subject">'.$overview[0]->subject.'</span> ';
        $output.= '<span class="from">'.$overview[0]->from.'</span>';
        $output.= '<span class="date">on '.$overview[0]->date.'</span>';
        $output.= '</div>';

        /* output the email body */
        $output.= '<div class="body">'.$message.'</div>';
    }

    echo $output;
} 

imap_close($inbox);
?>
Dariusdarjeeling answered 21/3, 2013 at 5:3 Comment(3)
Are the characters not coming properly for you of the email?Letty
@DeadMan Emails from Windows Mail (Metro app in Win8) are base64, while ones coming from other applications are 8bit.Dariusdarjeeling
The result of imap_fetchstructure() should have a property encoding. That not working for you?Laborsaving
B
50

imap_bodystruct() or imap_fetchstructure() should return this info to you. The following code should do exactly what you're looking for:

<?php
$hostname = '{********:993/imap/ssl}INBOX';
$username = '*********';
$password = '******';

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

$emails = imap_search($inbox,'ALL');

if($emails) {
    $output = '';
    rsort($emails);

    foreach($emails as $email_number) {
        $overview = imap_fetch_overview($inbox,$email_number,0);
        $structure = imap_fetchstructure($inbox, $email_number);

        if(isset($structure->parts) && is_array($structure->parts) && isset($structure->parts[1])) {
            $part = $structure->parts[1];
            $message = imap_fetchbody($inbox,$email_number,2);

            if($part->encoding == 3) {
                $message = imap_base64($message);
            } else if($part->encoding == 1) {
                $message = imap_8bit($message);
            } else {
                $message = imap_qprint($message);
            }
        }

        $output.= '<div class="toggle'.($overview[0]->seen ? 'read' : 'unread').'">';
        $output.= '<span class="from">From: '.utf8_decode(imap_utf8($overview[0]->from)).'</span>';
        $output.= '<span class="date">on '.utf8_decode(imap_utf8($overview[0]->date)).'</span>';
        $output.= '<br /><span class="subject">Subject('.$part->encoding.'): '.utf8_decode(imap_utf8($overview[0]->subject)).'</span> ';
        $output.= '</div>';

        $output.= '<div class="body">'.$message.'</div><hr />';
    }

    echo $output;
}

imap_close($inbox);
?>
Badtempered answered 28/3, 2013 at 17:8 Comment(4)
It has decoded most of the messages, except ones from Windows Mail on Windows 8. Hopefully nobody uses that. Thank you!Dariusdarjeeling
Why shunt 4 of possible 6 encodings into the else block handling of quoted_printables? php.net/manual/en/function.imap-fetchstructure.phpAutonomy
doesnt work when $structure->parts[1] is an attachmentAutonomy
why check $structure->parts[1]->encoding and not $structure->encoding like mentioned in answer below? I think that is more correct?Autonomy
J
11

You can look at this example.

Imap/Imap

Here's code snippet

switch ($encoding) {
    # 7BIT
    case 0:
        return $text;
    # 8BIT
    case 1:
        return quoted_printable_decode(imap_8bit($text));
    # BINARY
    case 2:
        return imap_binary($text);
    # BASE64
    case 3:
        return imap_base64($text);
    # QUOTED-PRINTABLE
    case 4:
        return quoted_printable_decode($text);
    # OTHER
    case 5:
        return $text;
    # UNKNOWN
    default:
        return $text;
}
Jubbah answered 3/3, 2015 at 13:30 Comment(4)
I had to use imap_qprint on ENC7BIT messages to be shown correctly. You can use PHP constants instead of numbers: case ENC7BIT:Mess
thanks for the answer @Jubbah , just I have a couple of questions, imap_8bit & quoted_printable_decode are some kind of their opposit, why we are using them in case #1Absorptivity
according to php documentations imap_binary would turn 8-bit string to base64 according to this page: php.net/manual/en/function.imap-binary.phpAbsorptivity
is it the 8-bit encoding or 7-bit encoding that php uses as the default encoding, I mean its a little point of confusion for meAbsorptivity
F
2

Returned Objects for imap_fetchstructure()

  1. encoding (Body transfer encoding)

Transfer encodings (may vary with used library)

0 7BIT 1 8BIT 2 BINARY 3 BASE64 4 QUOTED-PRINTABLE 5 OTHER

$s = imap_fetchstructure($mbox,$mid);
if ($s->encoding==3)
    $data = base64_decode($data);
Fattish answered 2/4, 2013 at 11:21 Comment(0)
H
0

I also faced this issue, and this is how I fixed

$structure = imap2_fetchstructure($connection, $messages);
    if (!isset($structure->parts) || !is_array($structure->parts) || !isset($structure->parts[0])) {
        $body = imap2_body($connection, $messages);
        return base64_decode($body);
    }

    $part = $structure->parts[0];
    $body = imap2_fetchbody($connection, $messages, '1');

    if ($part->encoding == 3) {
        return base64_decode($body);
    }
    if ($part->encoding == 1) {
        return imap2_8bit($body);
    }
    return imap2_qprint($body);
Homiletics answered 1/2, 2023 at 8:49 Comment(0)
J
-1
            // Best for reading attached file from e-mail as well as body of the mail
            $hostname = '{imap.gmail.com:993/imap/ssl}Inbox';
            $username = '[email protected]';
            $password = '****************';

            $inbox = imap_open($hostname,$username,$password) or die('Cannot connect to Gmail: ' . imap_last_error());  
            $emails   = imap_search($inbox, 'SUBJECT "'.$subject.'"');
            rsort($emails);
            foreach ($emails as $key => $value) {

                $overview = imap_fetch_overview($inbox,$value,0);
                $message_date = new DateTime($overview[0]->date);
                $date = $message_date->format('Ymd');
                $message = imap_fetchbody($inbox,$value,2);
                $structure = imap_fetchstructure($inbox, $value);
                $attachments = [];
                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($inbox, $value, $i+1);                           
                            if($structure->parts[$i]->encoding == 3) //3 = BASE64 encoding
                            { 
                                $attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
                            }
                            elseif($structure->parts[$i]->encoding == 4) //4 = QUOTED-PRINTABLE encoding
                            { 
                                $attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
                            }
                        }
                    }
                }
            }
            imap_close($inbox);//Never forget to close the connection
Jukebox answered 31/5, 2019 at 16:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.