imap icon indicating copy to clipboard operation
imap copied to clipboard

getFilename() returns NULL for long attachment filenames

Open pporlan opened this issue 3 years ago • 1 comments

Q A
ddeboer/imap version 1.12.2 / 1.13.1
PHP version 7.4.28 / 8.1.3
IMAP provider imap.ionos.es

Summary

Long filenames splitted across multiple lines in the message are not retrieved correctly by the function $attachment->getFilename()

$message->getRawMessage() output: ... --=_6cc5e393f9284e5e531157803beddb81 Content-Transfer-Encoding: base64 Content-Type: text/csv; name*0*=utf-8''BTP%20Cerrados%20%C3%9Altimo%20Mes%20Tickets-20220311-0603; name*1*=03.csv Content-Disposition: attachment; filename*0*=utf-8''BTP%20Cerrados%20%C3%9Altimo%20Mes%20Tickets-20220311-; filename*1*=060303.csv; size=247245

77u/Ik7Dum1lcm8gZGUgVGlja2V0IjsiRmVjaGEgZGUgY3JlYWNpw7NuIjtBc3VudG87RGU7IkRl ...

Current behavior

$attachment->getFilename() returns NULL

Expected behavior

$attachment->getFilename() should return "BTP Cerrados Último Mes Tickets-20220311-060303.csv"

pporlan avatar Mar 11 '22 09:03 pporlan

I don't use this library, but was searching for some libraries that could handle quite some different scenario's. One of those scenario's would be the same like this issue.

When fetching the structure from an email holding an attachment 'with no name', the part holding the attachment looks like this:

...
->disposition = "ATTACHMENT",
->ifdparameters = 1,
->dparameters = array(
    0 => {stdClass}( attribute = "FILENAME*0", value = "a very long filename limited by 60chars"),
    1 => {stdClass}( attribute = "FILENAME*1", value = "remaining chars.txt")
)

I solved getting the filename of the attachment in my own code like this:

/** @var stdClass $part a part returned by `imap_fetchstructure()`
$filename = '';
        $filenameParts = array();
        if($part->ifdparameters) {
            foreach($part->dparameters as $i => $object) {
                if(strtolower($object->attribute) === 'filename') {
                    $filename = $object->value;
                }elseif(strtolower($object->attribute) === 'filename*'.$i){
                    $filenameParts[] = $object->value;
                }
            }
        }

        if(!$filename && $part->ifparameters) {
            foreach($part->parameters as $object) {
                if(strtolower($object->attribute) == 'name') {
                    $filename = $object->value;
                }
            }
        }

        if(!$filename && $filenameParts){
            $filename = implode('', $filenameParts);
        }

       return iconv_mime_decode($filename , ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');

Retrieving the filename should handle at least 3 cases:

  1. filename is in a parameter named 'FILENAME'
  2. filename is split in parameters named 'FILENAME*N' where N is the index of the parameter
  3. filename is in a parameter named 'NAME'

But look when looking at Ddeboer\Imap\Message\Parameters::$attachmentCustomKeys, there also seems to be a case where attributes are named exactly 'FILENAME*' or 'NAME*' ???

Are there any official docs on reading the filename from the structure? I failed to find anything other then cases 1. and 3 from non-official programming examples.

LeoZandvliet avatar Jul 28 '25 15:07 LeoZandvliet