Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multipart-Message Boundaries are empty #27

Closed
DasTobbel opened this issue Oct 13, 2020 · 6 comments · Fixed by #28
Closed

Multipart-Message Boundaries are empty #27

DasTobbel opened this issue Oct 13, 2020 · 6 comments · Fixed by #28
Labels
bug Something isn't working validated

Comments

@DasTobbel
Copy link
Contributor

DasTobbel commented Oct 13, 2020

Describe the bug
Got an Email, where a parsed header contains a malformed header, so that somehow the getBoundary() method is called twice and returns the correct boundary in the first run, but a empty one in the second
src/Structure.php:99:

    public function getBoundary(){
        $tmp = $this->header->find("/boundary\=\"(.*)\"/");
        \Log::debug(print_r(["getBoundary" => $tmp], true));
        return tmp;
    }
[...]
    public function find_parts(){
        if($this->type === IMAP::MESSAGE_TYPE_MULTIPART) {
            if (($boundary = $this->getBoundary()) === null)  {
                throw new MessageContentFetchingException("no content found", 0);
            }
          
            $boundaries = [
                $boundary
            ];
            \Log::debug(print_r(["boundaries" => $boundaries], true));
            if (preg_match("/boundary\=\"(.*)\"/", $this->raw, $match) == 1) {
                if(is_array($match[1])){
                    foreach($match[1] as $matched){
                        $boundaries[] = $matched;
                    }
                }else{
                    if(!empty($match[1])) {
                        $boundaries[] = $match[1];
                    }
                }
            }
            \Log::debug(print_r(["boundaries #2" => $boundaries], true));
            $raw_parts = explode( $boundaries[0], str_replace($boundaries, $boundaries[0], $this->raw) );
            $parts = [];
[...]
Log:
[2020-10-13 13:51:30] production.DEBUG: Array( [getBoundary()] => _009_VI1PR05MB57270B55FFCF7840254D7E6F95040VI1PR05MB5727eurp_ )
[2020-10-13 13:51:30] production.DEBUG: Array( [boundaries] => Array( [0] => _009_VI1PR05MB57270B55FFCF7840254D7E6F95040VI1PR05MB5727eurp_ ))
[2020-10-13 13:51:30] production.DEBUG: Array( [boundaries #2] => Array ([0] => _009_VI1PR05MB57270B55FFCF7840254D7E6F95040VI1PR05MB5727eurp_ , [1] => _000_VI1PR05MB57270B55FFCF7840254D7E6F95040VI1PR05MB5727eurp_ ))
[2020-10-13 13:51:30] production.DEBUG: Array( [getBoundary()] => null ) 
[2020-10-13 13:51:30] production.DEBUG: Array( [boundaries] => Array([0] => null ))
[2020-10-13 13:51:30] production.DEBUG: Array( [boundaries #2] => Array([0] => null ))

To Reproduce
Steps to reproduce the behavior:
Create an email where a the Header have a definition like this

Content-Language: de-DE

Content-Type: multipart/related;

	boundary="_009_VI1PR05MB57270B55FFCF7840254D7E6F95040VI1PR05MB5727eurp_";

	type="multipart/alternative"

MIME-Version: 1.0

Try to recive the said mail
Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop / Server (please complete the following information):

  • OS: ubuntu 20
  • PHP: 7.4
  • Version 2.1.11

Additional context
Another smaller Issue was that some boundary params were set without any qoute (") markers, could be fixed with an alternation on the regex = boundary\=\"?(.*)\"?

@DasTobbel DasTobbel changed the title Multipar-Message Boundaries are empty Multipart-Message Boundaries are empty Oct 13, 2020
@Webklex Webklex added bug Something isn't working validating labels Oct 13, 2020
@DasTobbel
Copy link
Contributor Author

So i worked some time on this, i think i kinda got a fix, pull request incoming - but after my fix i get the mail as an attachment - still investigating

@DasTobbel
Copy link
Contributor Author

Need to take a look into the Attachement Handling - i don't know why the text mail body gets handles as an attachment on my side (could be just me, need to validate)

@Webklex Webklex reopened this Oct 13, 2020
@Webklex
Copy link
Owner

Webklex commented Oct 13, 2020

Sry didn't mean to close it..

@Webklex
Copy link
Owner

Webklex commented Oct 13, 2020

You probably mean \Webklex\PHPIMAP\Message::fetchPart() @ line 461:

$this->fetchAttachment($part);

If I remember correctly it's there because many plain text attachments don't have the required disposition and therefor get interpreted as part of the text body. But I see the problem - do you have any suggestion for a better handling?

@DasTobbel
Copy link
Contributor Author

DasTobbel commented Oct 15, 2020

My best guess:
Maybe look for a filename -> If it has a filename its an attachment - if not, its a text body.
Not sure if this is any good tho...
Message.php

    private function fetchPart(Part $part) {
        if ($part->type == IMAP::MESSAGE_TYPE_TEXT && ($part->ifdisposition == 0 || (empty($part->disposition) || !in_array(strtolower($part->disposition), ['attachment', 'inline'])) ) ) {
            if (strtolower($part->subtype) == "plain" || strtolower($part->subtype) == "csv") {
                [...]
                if (isset($part->name) and !is_empty($part->name))
                    $this->fetchAttachment($part);
                } elseif (isset($part->filename) and !is_empty($part->filename))
                    $this->fetchAttachment($part);
                } else {
                    $this->bodies['text'] = $content;
                }
            } elseif {
            [..]

@DasTobbel
Copy link
Contributor Author

Hi @Webklex,
had a email where the the Part::parseSubtype() had an array as response of $this->header->get("content-type"); if that's the case it will fetch that part as an Attachment.

To Reproduce
Steps to reproduce the behavior:
Create an email where a part has a definition of the content-type like this

--limiter
Content-Type: text/plain; 
    charset=utf-8;
Content-transfer-encoding: quoted-printable

that is my text
--limiter--

somewhat similar to #24

suggestion 1

    private function parseSubtype(){
        $content_type = $this->header->get("content-type");
        // If its an array the mime needs to be in the first array, because it's in the same line as "content-type"
        $content_type = (is_array($content_type)) ? $content_type[0] : $content_type;
        if (($pos = strpos($content_type, "/")) !== false){
            $this->subtype = substr($content_type, $pos + 1);
        }
    }

or
suggestion 2 (somewhat fail proof but unnecessary i think)

    private function parseSubtype(){
        $content_type = $this->header->get("content-type");
        if (is_array($content_type)) {
            foreach ($content_type as $part){
                if ((strpos($part, "/")) !== false){
                    $content_type = $part;
                    break;
                }
            }
        }
        if (($pos = strpos($content_type, "/")) !== false){
            $this->subtype = substr($content_type, $pos + 1);
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working validated
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants