Decode email body in php gmail api? - php

I'm trying to read the email body usign Gmail API
I was using IMAP but for performance reasons(reading emails takes so much time in IMAP) I have to move to Gmail API which is considerably faster.
The issue is where I'm trying to decode the Body, with IMAP is simple, just read the transfer encodings from imap_fetchstructure return and use the appropriate function to decode. (imap_qprint,imap_7bit, etc)
And for Gmail
$message = $service->users_messages->get($user, $msg->id, ["format"=>"full"]);
$payload = $message->getPayload();
$mime = $payload->getMimeType();
$body = $payload->getBody();
$headers = $payload->getHeaders();
$content = $body->getData();
$decoded = base64_decode($content);
The variable $contents is the body in base64 but If I decode includes strange characters like ��ѽ��� that didn't happened with IMAP.
The content is plain text UTF-8 no additional parts or attachment, just plain text. And it happens with HTML as well.
These are the relevants headers
[{"name":"MIME-Version","value":"1.0"},
{"name":"Content-Type","value":"text\/plain; charset=utf-8"},
{"name":"Content-Transfer-Encoding","value":"quoted-printable"}]
I think the issue is that the body is quoted-printable but even if I use imap_qprint or quoted_printable_decode over the decoded base64 these strange characters continue.

I was having the same problem...Felipe Morales got at the solution...but to be a bit more explicit:
Take the base64-url-encoded string from the API response, and run it through this function:
function gmailBodyDecode($data) {
$data = base64_decode(str_replace(array('-', '_'), array('+', '/'), $data));
//from php.net/manual/es/function.base64-decode.php#118244
$data = imap_qprint($data);
return($data);
}
Works like a champ...

Related

Remove 'E' output headers from base64 string in TCPDF

I'm using TCPDF using
$base64String = $pdf->Output('file.pdf', 'E');
So I can send the data via AJAX
The only problem is that it comes with header information in addition to the Base64 string
Content-Type: application/pdf;
name="FILE-31154d59f28c63efae86e4f3d6a00e13.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="FILE-31154d59f28c63efae86e4f3d6a00e13.pdf"
So if I take the string that is created to base64_decode() or use with phpMailer in my case it errors. Is it possible to remove the headers so I only have the base64 string?
(The error is that the pdf can't be read by any PDF reader when opened)
I thought I'd be able to find something that solves this but I haven't found anything!!
UPDATE
This is what I've put in place to solve the issue
$base64String = preg_replace('/Content-[\s\S]+?;/', '', $base64String);
$base64String = preg_replace('/name=[\s\S]+?pdf"/', '', $base64String);
$base64String = preg_replace('/filename=[\s\S]+?"/', '', $base64String);
However it's not very elegant! So if anyone has a better solution please post it below :)
TCPDF docs are huge but unusable – it's easier to read the source code directly. It has those extra headers because you're asking for them by using the E output mode, which is intended for generating email messages.
For sending the PDF data as a PHPMailer attachment, you want the straight binary PDF data as a string, as provided by the S output mode, which you can pass straight into addStringAttachment(), and PHPMailer will handle all the encoding for you. All you have to do is this:
$mail->addStringAttachment($pdf->Output('file.pdf', 'S'), 'file.pdf');
To convert the PDF binary into base64, for example to us it in a JSON string, simply pass it through base64_encode:
$base64String = base64_encode($pdf->Output('file.pdf', 'S'));

PHP imap: how to decode and convert Windows-1252 charset emails?

My PHP app processes incoming emails. The processing code usually works fine, but the app crashed recently with the below exception:
Unexpected encoding - UTF-8 or ASCII was expected (View: /home/customer/www/gonativeguide.com/gng2-core/vendor/laravel/framework/src/Illuminate/Mail/resources/views/html/panel.blade.php) {"exception":"[object] (Facade\\Ignition\\Exceptions\\ViewException(code: 0): Unexpected encoding - UTF-8 or ASCII was expected (View: /home/customer/www/gonativeguide.com/gng2-core/vendor/laravel/framework/src/Illuminate/Mail/resources/views/html/panel.blade.php) at /home/customer/www/gonativeguide.com/gng2-core/vendor/league/commonmark/src/Input/MarkdownInput.php:30)
It seems that there was an incoming email whose text was not properly decoded and this made the app crash later on.
I realized that the email had a Windows-1252 encoding:
Content-Type: text/html; charset="Windows-1252"
Content-Transfer-Encoding: quoted-printable
The email decoding code looks currently like this:
// DECODE DATA
$data = ($partno)?
imap_fetchbody($mbox,$mid,$partno): // multipart
imap_body($mbox,$mid); // simple
// Any part may be encoded, even plain text messages, so check everything.
if ($p->encoding==4)
$data = quoted_printable_decode($data);
elseif ($p->encoding==3)
$data = base64_decode($data);
I checked this page to understand what I need to change to decode emails with Windows-1252, but it not clear to me which value corresponds to Windows-1252 and how to decode and convert the data to UTF-8. I would highly appreciate any hints, preferably with suggested code on this.
Thanks,
W.
In your case, this line:
$data = quoted_printable_decode($data);
needs to be adapted like this:
$data = mb_convert_encoding(quoted_printable_decode($data), 'UTF-8', 'Windows-1252');
More generally, to cope with non-UTF-8 encodings, you may want to extract the charset of the body part:
from the body part structure, returned by imap_bodystruct(), or
from the body part MIME headers, returned by imap_fetchmime().

Sending encrypted mail from php with non-ASCII Content-type

I am trying the below code to send encrypted mail where the cleartext is text/html and charset is utf-8.
For what it's worth, I cannot manage to get the mail displayed correctly (in MS Outlook).
As you may notice, I (meanwhile) try to have the relevant Content-Type: header both prepended to the data file to be encrypted and present in the headers parameter of the call to openssl_pkcs7_encrypt (and tried various other combinations as well).
While the desired content-type applies to the message when it is sent unencrypted, it simply does not work with encrypted messages. The result is always as if the "inner" Content-Type had been text/plain;charset=ascii.
I experimented with prepending the header to the data file before encryption only because I think I found some suggestion similar here on SE for a similar situation. But apparently this only makes the header line appear as part of the decrypted message (in other words, the file submitted to openssl_pkcs7_encrypt should really only be the "pure" content (as I had orginally suspected).
But having it in the headers parameter does not help, either: In the headers present in the enncryped file, there is a header added with the correct "outer" S/MIME content type, which overrides my "inner" content type given.
Question: Where and how in the combination of openssl_pkcs7_encrypt() and mail() should I specify the "inner" Content-Type of my encrypted message?
$hdr_to = implode(',',$empfaenger);
$hdr_subject = '=?UTF-8?q?' . quoted_printable_encode($subject) . '?=';
$other_headers = array(
"From" => $hdr_from,
"Content-Type" => "text/html; charset=utf-8",
"X-Mailer" => "PHP/".phpversion()
);
$mailbody = wordwrap($_REQUEST['message']);
//write msg to disk
$msg_fn = tempnam("/tmp","MSG");
$enc_fn = tempnam("/tmp","ENC");
$fp = fopen($msg_fn, "w");
fwrite($fp, 'Content-Type: ' . $other_headers['Content-Type'] . CRLF . CRLF);
fwrite($fp, $mailbody);
fclose($fp);
// Encrypt message
$enc_ok = openssl_pkcs7_encrypt($msg_fn,$enc_fn,$pubkeys,$other_headers,PKCS7_TEXT,1);
//$enc_ok = false; // for debugging: simulate encryption failure
if (!$enc_ok) {
// Will try to send unencrypted instead
} else {
// Seperate headers and body for mail()
$data = str_replace("\n",CRLF,file_get_contents($enc_fn));
$parts = explode(CRLF.CRLF, $data, 2);
$mailbody = $parts[1];
$other_headers = $parts[0];
}
// Send mail
$mail_ok = mail($hdr_to, $hdr_subject, $mailbody, $other_headers);
The problem was the PKCS7_TEXT flag given as parameter to the encryption function.
My intended media type was text/html; charset=utf-8, i.e.,
of type text
of subtype html
with parameter charset=utf-8
and superficially, the type matches what the name PKCS7_TEXT suggests. However, this flag is described as:
Adds text/plain content type headers to encrypted/signed message. If decrypting or verifying, it strips those headers from the output - if the decrypted or verified message is not of MIME type text/plain then an error will occur.
To be precise, it seems that said header and a blank line (to separate it from the message body) are prepended, which turns any previously existing header lines (Content-Type: or other) into part of the body.
So the simple solution is to not use the PKCS7_TEXT flag (unless your message is really plain ASCII² text).
² It may also be okay for latin-1 text, but I won't count on it. After all the description quoted above suggests that an error will occur; then again, UTF-8 text gets decrypted successfully (i.e., without an explicit error), but exhibits the usual codepage related display artefacts .

parsing email attachments in php

I have a .forward.postix that is piping incoming emails to a shell account, and consequently to a PHP script that parses the emails - awesome.
As of now, and based on everything I've seen online, I'm using the explode('From: ', $email); approach to split everything down into the vars I need for my database.
Enter attachments! Using the same approach, I'm doing this to pull out a jpg attachment - but it appears not every email client formats the raw source the same way and it's causing some headaches!
$contentType1 = explode('Content-type: image/jpg;', $email); //start ContentType
$contentType2 = explode("--Boundary_", $contentType1[1]); //end ContentType
$jpg1 = explode("\n\n", $contentType2[0]); //double return starts base64 blob
$jpg2 = explode("\n\n", $jpg1[1]); //double return marks end of base64 blob
$image = base64_decode($jpg2[0]); //decode base64 blob into jpg raw
Here's my issue:
Not all mail clients - like GMail for example - list an attachment as 'Content-type: image/jpg'. GMail puts in 'Content-Type: IMAGE/JPEG' and sometimes sends it as 'Content-Type: image/jpeg'! Which don't match my explode...
My Question: Does someone know a better way of finding the bounds of a base64 blob? or maybe a case-insensitive way of exploding the 'Content-Type:' so I can then try against image/jpg or image-jpeg to see what matches?
You can try to split your string using preg_split(). Maybe something like this
$pattern = '#content-type: image/jpe?g#i';
$split_array = preg_split($pattern, $email);

PHP: Add attachments to emails "on the fly"?

I just got PHP's mail function to work properly in my test environment.
I have a PHP app that outputs a number of strings. It would be really nice to convert these strings to attachments (*.TXT -files) in an email, without first storing them on disk and having to read them back. Would this be possible in PHP?
Yes, this is possible. You just need to make your email message a multipart message with the following syntax:
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=random-boundary
This is the optional preamble of a multipart/mixed message.
--random-boundary
Content-Type: text/plain
This is the main message body.
--random-boundary
Content-Type: text/plain
Content-Disposition: attachment; filename=file.txt
This is the content of the attached file.
--random-boundary--
This is the optional epilogue of a multipart/mixed message.
Each part can then be described like any other message. But you should probably use a library that does this for you.
Now if you’re using PHP’s mail function, the first two line would be the header and the rest would be the contents of that mail message. The boundary should be a random boundary so that the possibility of having that string with -- in front of it being in the contents of one part is very unlikely.
Yes, you can use e.g. PEAR's Mail_Mine class for it.
bool addAttachment ( string $file , string $c_type = 'application/octet-stream' , string $name = '' , boolean $isfile = true , string $encoding = 'base64' ) is the method you want to use, with $file containing your strings and $isfile being false.
And you can Use Zend_Mail Classes for much easier code
the file name would be "smapleFilename" and its the last parameter in createAttachment function
but don't foget to setup your transport before that
sample :
$mail = new Zend_Mail();
$mail->setBodyText("body")
->createAttachment("your wanted text " , Zend_Mime::TYPE_TEXT,
Zend_Mime::DISPOSITION_ATTACHMENT , Zend_Mime::ENCODING_BASE64, "smapleFilename.txt");
$mail->setFrom('test#222222.com', 'Server');
$mail->addTo('test#hotmail.com');
$mail->setSubject("subject");
$mail->send();
in Zend framework project you would do like this :
resources.mail.transport.type = smtp
resources.mail.transport.host = "mail.111111.com"
resources.mail.transport.auth = login
resources.mail.transport.username = test#111111.com
resources.mail.transport.password = test
;resources.mail.transport.ssl = tls
resources.mail.transport.port = 2525
resources.mail.transport.register = true ; True by default

Categories