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

getBody on null #30

Closed
buckfuddey opened this issue Sep 18, 2018 · 16 comments
Closed

getBody on null #30

buckfuddey opened this issue Sep 18, 2018 · 16 comments

Comments

@buckfuddey
Copy link

buckfuddey commented Sep 18, 2018

I found great use of your package however the function getBodyPart() in the file Services/Messages/mail.php line 350 has the option to return null to a function which later calls getBody() on the returned value. And if you call getBody on null it throws a fatal error.

I found that only messages from icloud causes this problem since gmail's own library sets the body (plaintext) in the payload object rather than in a part.

As you can see in the images below, one email object does not have parts, it was recieved from an icloud account and the second from a gmail account, however i have tried with yahoo, hotmail and privately hosted mail servers aswell, and those work.

A suggestion is to do a check first to see if the payload has parts to begin with, and if not extract the data from the payload body itself.

withparts
withoutpart

@buckfuddey
Copy link
Author

buckfuddey commented Sep 20, 2018

After a few hours spent building my own solution to icloud mails I have found additional issues that you might want to look into considering this is a work in progress. If linebreaks are included in a mail sent from icloud they are split into one part per linebreak. So if I was to write the following.
Hi
My
Name
Is
Buck
That would translate into 5 separate parts that all need to be accounted for if one wants to extract the full email.

Update, the issues seems to stem from sending emails from a phone in combination with icloud, might use different ASCII for linebreak than a computer does.

@JonahKlimack
Copy link
Contributor

can I see the solution you built? I ran into this same problem...

@buckfuddey
Copy link
Author

buckfuddey commented Sep 25, 2018

`public function hasNoParts(){
if (empty($this->payload->getParts())) {
return true;
} else {
return false;
}
}

public function extractFromBody(){
if ($this->hasNoParts()) {
$type = $this->payload->getMimeType();
$body = $this->payload->getBody();
if ($type == 'text/html' || $type == 'text/plain') {
$this->bodyArr[$type] = $this->getDecodedBody($body->getData());
}
if ($body->getAttachmentId()) {
$this->attachmentData[] = array(
'id' => $body->getAttachmentId(),
'fileName' => $part->getFilename(),
'mimeType' => $type
);
}
} else {
$parts = $this->payload->getParts();
foreach ($parts as $part) {
if (empty($part->getParts())) {
$type = $part->getMimeType();
$body = $part->getBody();
if ($type == 'text/html' || $type == 'text/plain') {
if (isset($this->messageBodyArr[$type])) {
$this->messageBodyArr[$type] .= $this->getDecodedBody($body->getData());
} else {
$this->messageBodyArr[$type] = $this->getDecodedBody($body->getData());
}
}

				if ($body->getAttachmentId()) {
					$this->attachmentData[] = array(
						'id' => $body->getAttachmentId(),
						'fileName'  => $part->getFilename(),
						'mimeType' => $type
					);						
				}				
			} else {
				$subParts = $part->getParts();
				$this->traverseData($subParts);
			}
		}
	}		
}

public function traverseData($parts){
	foreach ($parts as $part) {			
		if (empty($part->getParts())) {
			$type = $part->getMimeType();
			$body = $part->getBody();
			if ($type == 'text/html' || $type == 'text/plain') {
				if (isset($this->messageBodyArr[$type])) {
					$this->messageBodyArr[$type] .= $this->getDecodedBody($body->getData());
				} else {
					$this->messageBodyArr[$type] = $this->getDecodedBody($body->getData());
				}						
			}

			if ($body->getAttachmentId()) {
				$this->attachmentData[] = array(
						'id' => $body->getAttachmentId(),
						'fileName'  => $part->getFilename(),
						'mimeType' => $type
				);

			}
		} else {
			$subParts = $part->getParts();
			$this->traverseData($subParts);
		}
	}
}

public function getDecodedBody( $content ) {
$content = str_replace( '_', '/', str_replace( '-', '+', $content ) );
return base64_decode( $content );
}
`
All the functions called that aren't present in the supplied code are google API functions.

@buckfuddey
Copy link
Author

New to github, don't know why the supplied code spazzes out half way...

@JonahKlimack
Copy link
Contributor

use 3 backtics and end with

@JonahKlimack
Copy link
Contributor

by the way, thank you very much, I really need to get this done tonight, and this was in the way

@buckfuddey
Copy link
Author

No worries, there's way to little info on the google API for PHP anyway.

@JonahKlimack
Copy link
Contributor

I mean, thanks. There's no errors, but they're all null

Am I using this right? Call extractfrombody? when looping through messages? Looking over it...

@buckfuddey
Copy link
Author

you need to have the payload as a class variable to be able to extract data on it. I set the payload from the message as such:
$message = $this->service->users_messages->get( 'me', $messageId );
$this->payload = $message->getPayload();

@JonahKlimack
Copy link
Contributor

in the constructor of mail.php?

@JonahKlimack
Copy link
Contributor

aha! got it! Thanks!

@buckfuddey
Copy link
Author

It's already set in the mail constructor of the dacastro4 mail.php class. As long as you instantiate the class it should work. However, I'm not sure it can be called statically like other methods. The class I made is not the same however, so you might run into issues.

@JonahKlimack
Copy link
Contributor

some small things, but I implemented it and pushed to production, you saved me man, I had a meeting tomorrow and I was going to tell them this part isn't working, thanks.

@buckfuddey
Copy link
Author

no worries, you'll do the same for someone else some day :)

dacastro4 pushed a commit that referenced this issue Oct 15, 2018
This is an optional method that I have found works great for extracting the body of a message. It does not change any of your existing code. You simply call $message->extractBodyFrom . This was written by buckfuddey.  See issue #30   It has worked reliably for me in production for 2 days now.   Admittedly not the style you have, but it works to patch up some of the holes that keep creating this error. I propose you add it for now until at which point later you can clean it up, if you wish.
@valentim04
Copy link

Hi, here is a simple but effective solution:

$gmail = LaravelGmail::message()->preload()->all();

    $messages = [];
    foreach ($gmail as $key) {
             
        if ($key->hasNoParts() == false ){
            $body = $key->getHtmlBody();
        } else {
            $body0 = $key->payload->getBody();
            $body = $key->getDecodedBody($body0->data);
        }

}

@nunomsh
Copy link

nunomsh commented Jan 15, 2019

This solution wont work if the body as "deeper parts", and you wont get attachments if the body is null.
One possible solution is to get the payload and parts

In your controller:

public function decodeBody($body) 
	{
		$rawData = $body;
		$sanitizedData = strtr($rawData,'-_', '+/');
		$decodedMessage = base64_decode($sanitizedData);
		if(!$decodedMessage){
			$decodedMessage = FALSE;
		}
		return $decodedMessage;
	}

In the function you want to get the emails. In my case I want to get all the emails and attachments and store then, but for this example just the general idea

$messages = LaravelGmail::message()->in('INBOX')->unread()->preload()->all()->take(50);

foreach ($messages as $val => $mlist) {

	$body = $messages[$val]->payload->getBody();
	$FOUND_BODY = $this->decodeBody($body['data']);
	$message_id = $mlist->id;
    
	if(!$FOUND_BODY) {
		$parts = $messages[$val]->payload->getParts();
		foreach ($parts  as $part) {
			if($part['body'] && $part['mimeType'] == 'text/html') {
				$FOUND_BODY = $this->decodeBody($part['body']->data);
				break;
			}
		}
	} if(!$FOUND_BODY) {
		foreach ($parts  as $part) {

			if($part['parts'] && !$FOUND_BODY) {
				foreach ($part['parts'] as $p) {
					if($p['mimeType'] === 'text/html' && $p['body']) {
						$FOUND_BODY = $this->decodeBody($p['body']->data);
						break;
					}
				}
			}
			if($FOUND_BODY) {
				break;
			}
		}
	}
  
	
	if ($messages[$val]->hasAttachments()==1) {

		$anexos = $messages[$val]->getAttachments();
		foreach ($anexos as $key => $anexo) {
			$file = $anexos[$key]->getData();
			$fileName= $anexo->filename;
			$anexo->saveAttachmentTo('anexos/', $messages[$val]->getId().'_'.$anexo->getFileName(), 'public');
		}
	}
	
	$assoc = Gmail::updateOrCreate(
		['google_id' =>  $messages[$val]->getId()],
		['name' => $messages[$val]->getFromName(), 
		'subject' => $messages[$val]->getSubject(),
		'body' => $FOUND_BODY,
		'email_date' => $messages[$val]->getDate(),
		'has_attach' =>$messages[$val]->hasAttachments()
	]);
	$assoc->save();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants