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
Related
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 .
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...
I am using imap_open function in PHP to download emails and insert them into a mysql database
Here is my code to get the headers and body message etc:
$emails = imap_search($inbox,'ALL');
//if emails are returned, cycle through each...
if($emails)
{
//begin output var
$output = '';
//put the newest emails on top
rsort($emails);
//for every email...
foreach($emails as $email_number)
{
//get information specific to this email
$header=imap_headerinfo($inbox,$email_number);
$structure = imap_fetchstructure($inbox,$email_number);
$from = $header->from[0]->mailbox . "#" . $header->from[0]->host;
$toaddress=$header->to[0]->mailbox."#".$header->to[0]->host;
$replyto=$header->reply_to[0]->mailbox."#".$header->reply_to[0]->host;
$datetime=date("Y-m-d H:i:s",$header->udate);
$subject=$header->subject;
$message = quoted_printable_decode(imap_fetchbody($inbox,$email_number,1.1));
if($message == '')
{
$message = quoted_printable_decode(imap_fetchbody($inbox,$email_number,1));
}
}
}
but it doesnt seem to get the body of all emails. For example, when it receives Read Receipts the body is just blank and the same with some other emails people send.
sometimes, the email body looks like:
PGh0bWw+DQo8aGVhZD4NCjxtZXRhIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0i dGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04Ij4NCjwvaGVhZD4NCjxib2R5IHN0eWxlPSJ3b3JkLXdy YXA6IGJyZWFrLXdvcmQ7IC13ZWJraXQtbmJzcC1tb2RlOiBzcGFjZTsgLXdlYmtpdC1saW5lLWJy ZWFrOiBhZnRlci13aGl0ZS1zcGFjZTsgY29sb3I6IHJnYigwLCAwLCAwKTsgZm9udC1zaXplOiAx NHB4OyBmb250LWZhbWlseTogQ2FsaWJyaSwgc2Fucy1zZXJpZjsiPg0KPGRpdj4NCjxkaXY+DQo8 ZGl2PnJlcGx5PC9kaXY+DQo8ZGl2Pg0KPHAgc3R5bGU9ImZvbnQtZmFtaWx5OiBDYWxpYnJpOyBt YXJnaW46IDBweCAwcHggMTJweDsiPjxiPktpbmQgUmVnYXJkcyw8YnI+DQo8YnI+DQpDaGFybGll IEZvcmQgfCZuYnNwOzwvYj48c3BhbiBzdHlsZT0iY29sb3I6IHJnYigyNTIsIDc5LCA4KTsiPjxi PlRlY2huaWNhbCBNYW5hZ2VyJm5ic3A7PC9iPjwvc3Bhbj48Yj58Jm5ic3A7SW50ZWdyYSBEaWdp dGFsPC9iPjxmb250IGNvbG9yPSIjNTk1OTU ... continued
How can i convert the whole message body to be plain text
Here's what I use, in general. $email refers to one of the objects from the return of eg imap_fetch_overview:
$structure = imap_fetchstructure($email->msgno);
$body = imap_fetchbody($email->msgno, '1');
if (3 === $structure->encoding) {
$body = imap_base64($body);
} else if (4 === $structure->encoding) {
$body = imap_qprint($body);
}
Note there are 6 possible encodings (ranging from 0 to 5), and I'm only handling 2 of them (3 and 4) -- you might want to handle all of them.
Also note I'm also getting only the 1st part (in imap_fetchbody) -- you might want to loop over the pieces to get them as needed.
Update
One other thing I noticed about your code. You're doing imap_fetchbody($inbox,$email_number,1.1). That third argument should be a string, not a number. Do this instead:
imap_fetchbody($inbox, $email_number, '1.1')
The code given handles only simple text messages having at most one sub-part and no encoding. This is basically the simplest kind of email there is. The world used to be that simple, sadly no more!
To handle more email, your code must be expanded to handle:
Multi-parts
Encodings
Multi-part is the concept that a single email message (a bunch of data) can be divided into multiple, logically-separate pieces. In the simplest case, there is only one part: the text of the message. In the next simplest case, there is message text with a single attachment. The next simplest case is message text plus multiple attachments. Then it starts to get hard, when the text of the message refers inline or embeds the attachments (think of an HTML message with an image -- that image could be an attachment that's linked with "local" CSS or embedded as eg base64 data url).
Encoding is the idea that email needs to accommodate the lowest common denominator of SMTP servers on the Internet. From 1971 to the early 1990s, most email messages were plain text using 7-bit US ASCII character set -- and SMTP mailers in the middle relied on this 7-bit framework. As the need for character sets became more apparent, simultaneously with the need to send binary data (eg images), 8-bit SMTP mailers cropped up as did various methods to shoe-horn 8-bit clean data into 7-bits. These include quoted-printable and base64. While 7-bit is virtually dead, we still have all the hoops of this history to jump through.
Rather than re-invent the wheel, there is a good piece of code on PHP.net that handles multi-part encoded messages. See the comment by david at hundsness dot com. You would use that code like this:
$mailbox = imap_open($service, $username, $password) or die('Cannot open mailbox');
// for all messages
$emails = imap_fetch_overview($mailbox, '1:1'/* . imap_check($mbox)->Nmsgs*/);
foreach ($emails as $email) {
// get the info
getmsg($mailbox, $email->msgno);
// now you have info from this message in these global vars:
// $charset,$htmlmsg,$plainmsg,$attachments
echo $plainmsg; // for example
}
imap_close($mailbox);
(Side note: his code has three parse errors, where he does ". =" to mean ".=". Fix those and you're good to go.)
Also, if you're looking for a good blog on doing this "from the ground up", check out this: http://www.electrictoolbox.com/php-imap-message-parts/
I've been trying out the PHPMailer sample script as given in its README file. Now, when I run that script:
<?php
require("phpmailer.inc.php");
$mail = new phpmailer;
//$mail->IsSMTP(); // set mailer to use SMTP
$mail->From = "from#email.com";
$mail->FromName = "Mailer";
//$mail->Host = "smtp1.site.com;smtp2.site.com"; // specify main and backup server
$mail->AddAddress("someone#someplace.com");
//$mail->AddAddress("ellen#site.com"); // name is optional
$mail->AddReplyTo("info#site.com", "Information");
$mail->WordWrap = 50; // set word wrap
//$mail->AddAttachment("c:\\temp\\js-bak.sql"); // add attachments
//$mail->AddAttachment("c:/temp/11-10-00.zip");
$mail->IsHTML(true); // set email format to HTML
$mail->Subject = "Here is the subject";
$mail->Body = "This is the message body";
$mail->Send(); // send message
?>
I get the following warning :
Warning: mb_send_mail() [function.mb-send-mail]: Unsupported charset
""iso-8859-1";" - will be regarded as ascii in
/export/opt/samplefiles/phpmailer.inc.php on line 162
and the message that is received contains the header as well:
Mime-Version: 1.0 This is the message body
Please help
That first warning hints at mb_send_mail() being used over the ordinary mail() function. Which is unlikely to be prepared for in the PHPMailer package.
It's the setting mbstring.func_overload you have to watch out for. It shouldn't have bit zero set. (For example change 5 to 4 -- or from seven to six,-- or for example from 3 to 2.)
It has to be set in the php.ini or .user.ini. Function overloading can't be fixed at runtime.
If that's not adaptable then a workaround might be mb_internal_encoding("UTF-8"); to eschew the error message, after applying mb_encode_mimeheader() on supplied values. Likely you have to adapt PHPMailer to omit the quotes around charaset parameters however.
I just realized that I've actually been using an older version of PHPMailer (version 0.89 or something). I've gotten myself a newer 5.2 and it appears resolved. Thanks anyway :P
I'm trying to generate EML files from PHP. Is there any library that will allow me to easily create them? I could find some ActiveX component on the internet but would rather use something more portable.
I ended up building the MIME message myself using this kind of template, where each field is replaced by a TEMPLATE_<name> variable:
From: TEMPLATE_FROM_ADDRESS
MIME-Version: 1.0
To: TEMPLATE_TO_ADDRESS
Subject: TEMPLATE_SUBJECT
Content-Type: multipart/mixed; boundary="080107000800000609090108"
This is a message with multiple parts in MIME format.
--080107000800000609090108
Content-Type: text/plain
TEMPLATE_BODY
--080107000800000609090108
Content-Type: application/octet-stream;name="TEMPLATE_ATTACH_FILENAME"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;filename="TEMPLATE_ATTACH_FILENAME"
TEMPLATE_ATTACH_CONTENT
--080107000800000609090108
Then creating the final message is quite simple using str_replace:
$content = file_get_contents("Template.eml");
$content = str_replace("TEMPLATE_FROM_ADDRESS", $fromEmail, $content);
$content = str_replace("TEMPLATE_TO_ADDRESS", $toEmail, $content);
// etc. for each template parameter
// Also don't forget to base64_encode the attachment content;
$content = str_replace("TEMPLATE_ATTACH_CONTENT", base64_encode($attachContent), $content);
Additional info about file attachment in this post: Attachment name and file extension not working in email *.eml
Edit (2018): Since this answer was written it seems it's been copied and pasted a bit everywhere, the template in particular. To avoid any conflict with other MIME data, you should make sure that the boundary "080107000800000609090108" is unique - it's a string of random characters no longer than 70 characters.
I think you don't need a library. It's just plain text (e.g. http://bitdaddys.com/example1.eml)
Date: Sat, 12 Aug 2006 14:25:25 -0400
From: John Doe <jdoes#someserver.com>
Subject: BitDaddys Software
To: sales#bitdaddys.com
Dear BitDaddys Corp.,
We have added your software to our approved list.
Thank you for your efforts.
Sincerely,
John Doe
Some Server Company
You can just output text with headers and save it using fwrite. For attachments use base64_encode() as stated here
Use imap_savebody (part of the imap library http://us1.php.net/manual/en/function.imap-savebody.php) with a null $part_number. It creates a beautiful .eml file with one line of code with the entire message (null $part_number = all parts... not documented but works).
the other two solutions depend on the format of the email (only one attachment and no html section in the first solution, and only text email in the second).
imap_savebody creates a perfect .eml file no matter what the format of the incoming email is (as long as it's RFC-complaint of course).