I'm extracting emails from a database where they're stored as strings. I need to parse these emails to extract their attachments. I guess there must already be some library to do this easily but I can't find any.
PHP has a MailParse extension that is a lot faster then using the PEAR alternative which is native PHP.
Here is a library that wraps this extension:
http://code.google.com/p/php-mime-mail-parser/
Example:
// require mime parser library
require_once('MimeMailParser.class.php');
// instantiate the mime parser
$Parser = new MimeMailParser();
// set the email text for parsing
$Parser->setText($text);
// get attachments
$attachments = $Parser->getAttachments();
PEAR::Mail::mimeDecode should do what you're looking for
This could be done using the Zend_Mail component of the Zend Framework
Maybe this example, which can also be found in the documentation helps:
// get the first none multipart part
$part = $message;
while ($part->isMultipart()) {
$part = $message->getPart(1);
}
echo 'Type of this part is ' . strtok($part->contentType, ';') . "\n";
echo "Content:\n";
echo $part->getContent();
I don't know however how you can tell Zend Mail to read from strings, maybe there's some work required to do this, but then you'd have a full-fletched library that does what you want and some more(like reading the subject, etc.).
Edit:
I just had a second look at it and realized that all you have to do is write an own storage implementation(subclass Zend_Mail_Storage_Abstract) which shouldn't be so hard to do.
I think that's the cleanest solution you'll get, albeit a little effort is required to make it work.
If you're looking for a more quick'n'dirty kind of solution someone else might be able to help you.
Hope that helps.
PhpMimeParser - parse multipart mime message(attachments, inline images, base64, quoted-printable) https://github.com/breakermind/PhpMimeParser You can cut mime messages from files, string.
// Load .eml mime message from file
$str = file_get_contents('mime-mixed-related-alternative.eml');
// Format output
echo "<pre>";
// Create object MimeParser
$m = new PhpMimeParser($str);
// Show Emails
print_r($m->mTo);
print_r($m->mFrom);
print_r($m->mBcc);
print_r($m->mCc);
// Show Message
echo $m->mSubject;
echo $m->mHtml;
echo $m->mText;
print_r($m->mInlineList);
// Show Files
print_r($m->mFiles);
E-mail attachments are MIME encoded and added to the message body using headers. The PEAR MIME decode package will do what you need:
http://pear.php.net/package/Mail_mimeDecode
There's a better library out there:
https://github.com/php-mime-mail-parser/php-mime-mail-parser
It is installable via Composer.
Since this has the same name as the one on Google Code that was linked to elsewhere in this SO post, I think it's the successor to it, but I can't tell. The authorship info is harder to find on Google Code, so I cannot confirm it's the same author.
Some sample code (from the project README):
// Include the library first
require_once __DIR__.'/vendor/autoload.php';
$path = 'path/to/mail.txt';
$Parser = new PhpMimeMailParser\Parser();
$Parser->setStream(fopen($path, "r"));
// Loop through all the Attachments
if (count($attachments) > 0) {
foreach ($attachments as $attachment) {
echo 'Filename : '.$attachment->getFilename().'<br />'; // logo.jpg
echo 'Filesize : '.filesize($attach_dir.$attachment->getFilename()).'<br />'; // 1000
echo 'Filetype : '.$attachment->getContentType().'<br />'; // image/jpeg
echo 'MIME part string : '.$attachment->getMimePartStr().'<br />'; // (the whole MIME part of the attachment)
}
}
Related
It just so happens we have two files that are the exact same size that we some times try to send together. We can send anything else we want with these files but as soon as we include both of them it only sends the first one. I'll include my phpmailer code just to be safe but its worked thus far.
$mail = new PHPMailer(true);
$mail->IsSendMail();
$mail->SetFrom($from_addy, $from_name);
foreach(explode(',',$to) as $address1){
foreach(explode(';',$address1) as $address2){
if($address2 != ''){
$mail->AddAddress($address2);
}
}
}
$mail->WordWrap = 70;
$mail->IsHTML(true);
$mail->Subject = $subject;
$mail->Body = $css.$message;
$mail->AltBody = nl2br($message);
$mail->MsgHTML($css.$message);
if(is_array($attachments)){
foreach($attachments as $attachment){
$file = file_get_contents($attachment['tmp_name']);
$mail->AddStringAttachment($file,$attachment['name']);
// I have put checks here and both attachments do make it this far.
}
}
You're not checking the return value from addStringAttachment so you don't know if it's working or not. PHPMailer doesn't throw exceptions for everything, and while you've requested them, you're not catching them anyway. Why read the file and use addStringAttachment - why not read the attachment directly from the file with addAttachment? String attachments are more useful when you need to attach the results of a remote API call, PDF generation etc. Like this:
if (!$mail->addAttachment($attachment['tmp_name'], $attachment['name'])) {
echo "Failed to attach ".$attachment['tmp_name'];
}
Why are you using isSendmail()? It's unlikely you need that.
You've not presented any evidence that it's got anything to do with the file size - it's not as if attachments are indexed by their size value or anything.
You're setting AltBody before calling msgHTML(), which overwrites AltBody. Calling nl2br() on AltBody contents is pointless because AltBody will usually be presented as plain text, so <br> tags will show up.
It looks like your $attachment array may be sourced from $_FILES, in which case it looks you are handling file uploads unsafely. read the PHP docs on that, and look at the "send file upload" example provided with PHPMailer.
Overall, it looks like you've based your code on a very old PHPMailer example, so make sure you're using the latest version, and look at the examples provided with it.
I've come across a bit of a problem and was wondering if anyone could point me in the right direction, I'm writing a email parser that parses emails and extracts particular info.
It parses plain text emails(the emails are in a set format) using regex, this works fine.
If an email has an attachment which is of type 3 && encoding = 3 && disposition = attachment then save the attachment to local disk.
My problem is step 2:
I foreach through each email structure if it has more than 2 parts I attempt to decode the attachment and save it to disk, but this is failing.
If I try to simply use file_put_contents I get a 0 KB File, I have given full permissions to the wamp server user, So I do not think it's file permissions and I have verified I can write by using mkdir.
file_put_contents(APPPATH . "attachments/". $partofpart->dparameters[0]->value, $partofpart); //$partofpart is part[2] of the full email structure
So I attempted to decode the part and save the decoded part instead using file_put_contents
$attachment = base64_decode($partofpart);
var_dump($attachment);
//then save the attachment with attachment name
file_put_contents(APPPATH . "attachments/". $partofpart->dparameters[0]->value, $attachment);
but this returns an error saying base64_decode requires an string not an object, how do I then convert my email-part-attachment to a string for use by base64_decode?
Even just writing that out I feel I'm missing something obvious and shouldn't require the extra steps.
Am I right in thinking the part IS the attachment? All the parameters lead me to believe so, Type, Subtype, size etc are all correct for the attachment.
Link to the entire scraper model
Thanks for reading and any help.
Below is a vardump of parts that meet the type/encoding/disposition checks
I have worked out the problem, I was not fetching the actual pdf back just its structure
So I wrote a function to retrieve the body of the particular email then used imap_base64 to encode then finally file_put_contents.
function getAttachment($msg_index, $part)
{
$mailbody = imap_fetchbody($this->conn,$msg_index,$part);
return $mailbody;
}
$attachment = imap_base64($this->email_model->getAttachment($email['index'], "2"));
//mkdir(APPPATH . "attachmentzs/");
//then save the attachment with attachment name
file_put_contents(APPPATH . "attachments/". $partofpart->dparameters[0]->value, $attachment);
I already tried several solutions, the closest (for me) should look like this:
$file = $pdf->Output('', 'E');
$message->attach(Swift_Attachment::newInstance($file, 'name.pdf', 'application/pdf'));
$pdf is an instance of TCPDF and $message is an instance of Swift_Message.
Using above the email is being sent ok, file is attached but when I try to open it I get the error message that file is corrupted or badly encoded.
My question is: how to send pdf generated by TCPDF as Swiftmailer attachment without saving the file to server and deleting it after sending the email. Here is the link to the TCPDF output method documentation, maybe somebody can see something I have missed.
I am using something like this and it is working. For the PDF content I am using one of the simplest examples on the PDF library.
[...]
$pdf_as_string = $pdf->Output('', 'S'); // $pdf is a TCPDF instance
[...]
$transport = Swift_MailTransport::newInstance(); // using php mail function
$message->setTo(array(
"client#customdomain.com" => "Main Email",
"client#publicdomain.com" => "Secondary Email"
));
$message->setSubject("This email is sent using Swift Mailer");
$message->setBody("You're our best client ever.");
$message->setFrom("developers#mydomain.com", "Developers United");
$attachment = Swift_Attachment::newInstance($pdf_as_string, 'my-file.pdf', 'application/pdf');
$message->attach($attachment);
[...]
Maybe this answer comes a little late since I am using swiftmailer v4_3_0 and TCPDF v6_0_002. But just in case is worth to someone.
I have had no problems attaching TCPDFs on the fly.
I call a function which eventually returns the PDF using the Output type 'S':
return $pdf->Output('TE_Invoice.pdf', 'S');
I attach the file using:
$message->attach(Swift_Attachment::newInstance()
->setFilename('TE_Invoice.pdf')
->setContentType('application/pdf')
->setBody($val['file']));
Where $val['file'] is the returned value from above.
I am using TCPDF Version: 5.9.134 and Swift Mailer Version: 4.1.3
You can use outputmode 'E' to get base64String.
$base64PdfString = $pdf->Output('', 'E');
Beware: Maybe you have to cut the first 5-6 lines, because of
Content-Type: application/pdf; name=""
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename=""
Base64StringStartsHere....
cut
$base64PdfArray = explode("\r\n", $base64PdfString);
$base64 = '';
for($i = 5; $i < count($base64PdfArray); $i++) {
$base64 .= $base64PdfArray[$i];
}
Now you have the Email as base64String.
Before sending you have to decode it.
$mail->attach(new \Swift_Attachment(base64_decode($base64), 'Pdf.pdf', 'application/pdf'));
Did you try this?
$file = $pdf->Output('', 'S');
I'm doing this with another mail backend in PHP, and this does work. I guess the mail backend takes care of encoding the attachment, so there is no need to encode it as base64 manually.
What is the easiest way to attach a PDF to an email via DOMPDF?
The end of my script I am using (part of it) is below:
$dompdf = new DOMPDF();
$dompdf->load_html($html);
$dompdf->render();
//below to save the pdf file - not needed if emailing pdf
file_put_contents('/home/ststrave/public_html/pdf/STS_Brochure.pdf', $dompdf->output());
//below to open pdf in browser - required
$dompdf->stream("STS_Brochure_".rand(10,1000).".pdf", array("Attachment" => false));
jexit();
Just for clarification - this is being used in Joomla.
Appreciate the simplest/quickest way using standard PHP mail function.
Cheers ;-)
Ok. You already accepted an answer, but for anyone else coming here, I think there is an easier way, but it's also not PHP's standard mail function, which really isn't going to work. If you can get the pear packages Mail and Mail_mime, it's really easy to send emails with attachments. You can also directly attach the DomPDF output without creating a file, like so:
$dompdf = new DOMPDF();
$dompdf->load_html($html);
$dompdf->set_paper("letter", "portrait" );
$dompdf->render();
$output = $dompdf->output();
$mm = new Mail_mime("\n");
$mm->setTxtBody($body);
$mm->addAttachment($output,'application/pdf','output.pdf', false);
$body = $mm->get();
$headers = $mm->headers(array('From'=>$from,'Subject'=>$subject));
$mail =& Mail::factory('mail');
if($mail->send($to,$headers,$body)){
echo "Your message has been sent.";
}
Here is the solution I was looking for when I came here:
Instead of:
$dompdf->stream();
do this:
$fileatt = $dompdf->output();
And than send mail using PHPMailer and attach the pdf to mail like so:
$filename = 'MyDocument.pdf';
$encoding = 'base64';
$type = 'application/pdf';
$mail->AddStringAttachment($fileatt,$filename,$encoding,$type);
In this way you don't have to deal with saving the file on the server.
PHP's mail function has no "standard" file attachment method. It's an extremely barebones interface to the SMTP system that forces you to do ALL the work of attaching a file yourself.
I strongly suggest using PHPMailer or Swiftmailer to do the email for you - it reduces the heavy grunt work of generating your own MIME email and inserting the attachment (many many lines of code) does to maybe 5 lines total.
Note that neither of them will handle a streamed PDF from DOMPDF. You'll have to save the PDF to a temporary file and attach that,
Raw Data Attachments
The attachData method may be used to attach a raw string of bytes as an attachment. For example, you might use this method if you have generated a PDF in memory and want to attach it to the email without writing it to disk. The attachData method accepts the raw data bytes as its first argument, the name of the file as its second argument, and an array of options as its third argument:
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attachData($this->pdf, 'name.pdf', [
'mime' => 'application/pdf',
]);
}
Say, I have an application/xhtml+xml content and a gif image. How can I serve (not render) these two in a single http get request (using PHP)? It should be a multipart/related content. After reading this RFC, I tried something; but it did not work. (No, I am not trying to send emails with attachment)
Thanks in advance.
EDIT: Finally I succeeded doing that. I think, it will be useful if I write how I did that. I have added this as an answer. Please look below.
I'm afraid you can't for the purposes you want.
If you want to do this to serve web pages, as far as I can tell, the browsers won't work with such MIME responses for rendering pages.
If you want an example of how this messages works, send yourself an email with an attachment and on your email client (not an webmail) go to the "View source" option on the email body.
You'll see your message, the attachment and possibly other parts on the same message using the MIME Multipart encoding.
OTOH, if you want it to send email, there are libraries, like PHPMailer, that will do all the encoding for you.
If that's what you want, check this example at their website.
Edit:
You could use PHPMailer to build the message, then you just use the result, instead of actually sending the email.
Try something like this:
** This is untested code, just for a starting point **
<?php
require_once('../class.phpmailer.php');
$mail = new PHPMailer(true); // the true param means it will throw exceptions on errors, which we need to catch
try {
$mail->MsgHTML(file_get_contents('contents.html'));
$mail->AddAttachment('images/phpmailer.gif'); // attachment
$mail->AddAttachment('images/phpmailer_mini.gif'); // attachment
$mime_message = $mail->CreateBody();
echo $mime_message;
} catch (phpmailerException $e) {
echo $e->errorMessage(); //Pretty error messages from PHPMailer
} catch (Exception $e) {
echo $e->getMessage(); //Boring error messages from anything else!
}
?>
Here is it [worked for me] (No, I did not used PHPMailer; may be it is useful, but I could not make it working):
// Two contents : application/xhtml+xml and image/gif
// Here we go
<?php
$boundary = "ghorar#deem";
$im_len = filesize("path/to/abc.gif")
header("Content-Type:multipart/related; boundary=\"$boundary\"; type=\"application/xhtml+xml\"");
$xml_cnt = <<<EOD
<media xmlns="http://www.sth.com/sth.xsd">
<objectURI>cid:112509abc#syz.com</objectURI>
<size>$im_len</size>
<type>image/gif</type>
<name>abcxyz</name>
<description>blah blah</description>
</media>
EOD;
$xml_len = strlen($xml_cnt);
$im = imagecreatefromgif("path/to/abc.gif");
$to_send = "This is a multi-part message example in MIME format
--$boundary
Content-Type: application/xhtml+xml;
Content-ID: <e4509xml#asd.com>
Content-Length: $xml_len
$xml_cnt
--$boundary
Content-Type: image/gif;
Content-ID: <112509abc#syz.com>
Content-Length: $im_Len
Content-Transfer-Encoding: binary
";
echo $to_send;
imagegif($im);
echo "\r\n--$boundary--";
imagedestroy($im);
?>
You can load the xml from file also. But, note that if your xml refers the image (like I did here) you need to refer that with its Content-ID. Also, note the '<' '>' characters in the Content-ID field of the image (after the boundary) and the 'cid:' notation in the place where it is referred. It worked for me. Thanks to Carlos Lima for spending time for me.
I've been trying to achieve the same thing.
It appears that MSIE is the only browser to currently support multipart/related natively (Opera may have some support - but I;ve not played around with it) however when loading such files, it seems to totally ignore the HTTP headers regarding mime type and other stuff (such as caching information, disposition etc). Indeed MSIE will only open the file if the URL has a .mht file extension (i.e. its breaking most of the rules about HTTP).
Also, MSIE will happily load anything which contains an embedded multipart/related file (provided it is encoded as plain text) without rendering the data that the attachment is contained within. It looks like the functionality for handling these files is a quick hack to allow viewing of the files
If you reconfigure your webserver to parse .mht urls using php (or use mod_rewrite to remap the URL) then you'll probably find that it may work (although I wouldn't hold out a lot of hope of it working with a query string appended). But expect problems if you are generating dynamic content (it won't expire from the cache / refresh when you expect).
To cut a long story short - its not going to work.