How to copy email from Gmail to my server using PHP IMAP? - php

How can I copy email from Gmail to my server's /home/email directory after connecting to a Gmail mailbox using PHP's IMAP functionality?
I want to retrieve every email as a file in MIME format and I want to download the complete MIME file with PHP, not just the body or header of the email. Because of this, imap_fetchbody and imap_fetchheader can't do the job.
Also, it seems imap_mail_copy and imap_mail_move can't do the job because they are designed to copy / move email to mailboxes:
imap_mail_copy: Copy specified messages to a mailbox.
imap_mail_move: Move specified messages to a mailbox.

PHP will download the full MIME message from Gmail or any IMAP server using imap_fetchbody when the $section parameter is set to "". This will have imap_fetchbody retrieve the entire MIME message including headers and all body parts.
Short example
$mime = imap_fetchbody($stream, $email_id, "");
Long example
// Create IMAP Stream
$mailbox = array(
'mailbox' => '{imap.gmail.com:993/imap/ssl}INBOX',
'username' => 'my_gmail_username',
'password' => 'my_gmail_password'
);
$stream = imap_open($mailbox['mailbox'], $mailbox['username'], $mailbox['password'])
or die('Cannot connect to mailbox: ' . imap_last_error());
if (!$stream) {
echo "Cannot connect to mailbox\n";
} else {
// Get last week's messages
$emails = imap_search($stream, 'SINCE '. date('d-M-Y',strtotime("-1 week")));
if (!count($emails)){
echo "No emails found\n";
} else {
foreach($emails as $email_id) {
// Use "" for section to retrieve entire MIME message
$mime = imap_fetchbody($stream, $email_id, "");
file_put_contents("email_{$email_id}.eml", $mime);
}
}
// Close our imap stream.
imap_close($stream);
}

Related

PHP Direct SMTP Send + Attachment

I'm using xpertmailer to send email direct to the remote SMTP server following an MX lookup. This works really well and works on an old closed source NAS drive running PHP4 and on current PHP5 boxes.
<?php
define('DISPLAY_XPM4_ERRORS', true); // display XPM4 errors
require_once '/path-to/SMTP.php'; // path to 'SMTP.php' file from XPM4 package
$f = 'me#mydomain.net'; // from mail address
$t = 'client#destination.net'; // to mail address
// standard mail message RFC2822
$m = 'From: '.$f."\r\n".
'To: '.$t."\r\n".
'Subject: test'."\r\n".
'Content-Type: text/plain'."\r\n\r\n".
'Text message.';
$h = explode('#', $t); // get client hostname
$c = SMTP::MXconnect($h[1]); // connect to SMTP server (direct) from MX hosts list
$s = SMTP::Send($c, array($t), $m, $f); // send mail
// print result
if ($s) echo 'Sent !';
else print_r($_RESULT);
SMTP::Disconnect($c); // disconnect
?>
I'm now trying to add an attachment to it, but I've no idea how to get an attachment to be included and sent.
Anyone any ideas how I can do this ?
Thanks
Example:
$m = new MAIL;
// attach source
$a = $m->Attach('text message', 'text/plain');
$f = '/path/image.gif';
// attach file '$f', disposition 'inline' and give a name 'photo.gif' with ID value (this ID value can be used in embed HTML images)
$a = $m->Attach(file_get_contents($f), FUNC::mime_type($f), 'photo.gif', null, null, 'inline', MIME::unique());
echo $a ? 'attached' : 'error';

How to change a pipe-forwarded email's "From" and "To" section in PHP

I am trying to mask emails. Basically give an email to a client like "RandomName#MyDomain.com" and have it forward "MyRealEmail#MyDomain.com".
I am pipe forwarding the emails to a php script on my server, where I want to use the "To" and "From" to find the real recipient of the message and forward the message to them removing any identifiable information from the Sender (From) section.
I can parse almost all the data right now from the header, but my problem is with the body. The html body portion can vary so much from different origins. Outlook have a <html> and <body> section, while Gmail just has <div>s. Regardless, I get these strange "=" signs in my raw email too, in both text and html sections, like <=div>!
I just want to change the "From" and "To" and keep the rest of the email pretty much exactly as it is so it doesn't have anomalies in its text or html section.
How can I do this? Should I just parse the raw email and change the occurrences of the emails? how can I send it then? or should I remake the email using phpmailer or some other class? how can i get the body correct then?
My hosting provider doesn't have MailParse extension installed, since I have seen some solutions on the site using that extension, so I am having to do this using available extensions in PHP 5.5
UPDATE
I managed to figure out the = issue, it was quoted-printable, so now I am calling quoted_printable_decode() to resolve that issue. Still trying to figure the best way to forward the email after altering the header though.
After a lot of failed attempts, finally have a solution I can live with. The host server didn't want to allow MailParse because it was an issue on their shared hosting environment, so I went with Mail_mimeDecode and Mail_MIME PEAR extensions.
// Read the message from STDIN
$fd = fopen("php://stdin", "r");
$input = "";
while (!feof($fd)) {
$input .= fread($fd, 1024);
}
fclose($fd);
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$decoder = new Mail_mimeDecode($input);
$structure = $decoder->decode($params);
// get the header From and To email
$From = ExtractEmailAddress($structure->headers['from'])[0];
$To = ExtractEmailAddress($structure->headers['to'])[0];
$Subject = $structure->headers['subject'];
ExtractEmailAddress uses a solution from "In PHP, how do I extract multiple e-mail addresses from a block of text and put them into an array?"
For the Body I used the following to find the text and html portions:
$HTML = "";
$TEXT = "";
// extract email body details
foreach($structure as $K => $V){
if(is_array($V)){
foreach($V as $KK => $VV){
if(is_object($VV)){
$bodyHTML = false;
$bodyPLAIN = false;
foreach($VV as $KKK => $VVV){
if(!is_array($VVV)){
if($KKK === 'ctype_secondary'){
if($VVV === 'html') { $bodyHTML = true; }
if($VVV === 'plain') { $bodyPLAIN = true; }
}
if($KKK === 'body'){
if($bodyHTML){
$bodyHTML = false;
$HTML .= quoted_printable_decode($VVV);
}
if($bodyPLAIN){
$bodyPLAIN = false;
$TEXT .= quoted_printable_decode($VVV);
}
}
}
}
}
}
}
}
Finally, I had the parts I needed so I used Mail_MIME to get the message out. I do my database lookup logic here and find the real destination and masked From email address using the From and To I extracted from the header.
$mime = new Mail_mime(array('eol' => "\r\n"));
$mime->setTXTBody($TEXT);
$mime->setHTMLBody($HTML);
$mail = &Mail::factory('mail');
$hdrs = array(
'From' => $From,
'Subject' => $Subject
);
$mail->send($To, $mime->headers($hdrs), $mime->get());
I don't know if this will cover all cases of email bodies, but since my system is not using attachments I am ok for now.
Take not of quoted_printable_decode(), that how I fixed the issue with the = in the body.
The only issue is the delay in mail I am having now, but I'll deal with that

Cannot get Swiftmailer to work on a PHP site

I am new to PHP and Swiftmailer.
What I am trying to do is set up a PHP site on a webserver and use SwiftMailer to send e-mails.
The code I got does work on my local XAMPP server, but will produce the error message:
"Fatal error: Class 'Swift_Attachment' not found in /[address to my php file]"
when executed from the on-line webserver.
Here is my code:
<?php
// Get this directory, to include other files from
$dir = dirname(__FILE__);
// Get the contents of the pdf into a variable for later
ob_start();
require_once($dir.'/pdf_selbst_lir.php');
$pdf_html = ob_get_contents();
ob_end_clean();
// Load the dompdf files
require_once($dir.'/dompdf/dompdf_config.inc.php');
$dompdf = new DOMPDF(); // Create new instance of dompdf
$dompdf->load_html($pdf_html); // Load the html
$dompdf->render(); // Parse the html, convert to PDF
$pdf_content = $dompdf->output(); // Put contents of pdf into variable for later
// Get the content of the HTML email into a variable for later
ob_start();
require_once($dir.'/Templates/html.php');
$html_message = ob_get_contents();
ob_end_clean();
// Swiftmailer
require_once($dir.'/swiftmailer-5.0.1/lib/swift_required.php');
// Create the attachment with your data
$attachment = Swift_Attachment::newInstance($pdf_content, 'pdfname.pdf', 'application/pdf');
// Create the Transport
$transport = Swift_SmtpTransport::newInstance('mymailserver', 587, 'tls')
->setUsername('username')
->setPassword('password')
;
// Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);
// Create the message
$message = Swift_Message::newInstance()
->setFrom(array('senderaddress' => 'Sender Name'))
->setSubject('subject')
->addBcc('somebccaddress')
->setBody($html_message, 'text/html')
->attach($attachment);
try {
$message->addTo('somerecipient');
}
catch (Swift_RfcComplianceException $e)
{
// Address was invalid
echo "Email address not correct.";
}
// Send the message
if ($mailer->send($message))
$success = true;
else
$error = true;
?>
Note that when I comment out all the attachment-related stuff, the error message switches to
"Fatal error: Class 'Swift_SmtpTransport' not found in /[address to my php file]" and points to the "$transport" line.
So it appears the overall SwiftMailer is not working, and this is not attachment-related.
Help would be greatly appreciated!
I don'see ->setTo( your_mail_dest) and ->send()
try with a simple send like this
$message = Swift_Message::newInstance()
->setFrom(array('senderaddress' => 'Sender Name'))
->setTo( **** your_mail_dest ****)
->setSubject('subject')
->addBcc('somebccaddress')
->setBody($html_message, 'text/html')
->attach($attachment)
->send();
Case closed.
It turned out that the code was working fine after all. All I needed to do was upgrade Swiftmailer (now running on 5.4.2-DEV). Sorry for the hassle and thanks scaisEdge for helping!

Retrieve mail via PHP / IMAP - Header is changed after fetching the mail

i have a big Problem. I'll fetch all mails of a mailbox with php_imap, connecting to pop3 service of an microsoft exchange server.
The Adress of the Mailbox: sample#sample.com
This Mailbox has many aliases: e.G. sample_test#sample.com, sample_test2#sample.com
My problem: When someone writes a mail to sample_test#sample.com the mail is deliverd to the mailbox. So far thats no Problem. When viewing the Header-Source in Microsoft Outlook it displays
To: Sample "sample_test#sample.com"
Still, no problem!
But ...
after receiving this via my php-script and writing the imap_header and imap_body to the file e.G. mail1.eml viewing the source gives me
To: Sample "sample#sample.com"
Here i wish to get
To: Sample "sample_test#sample.com"
My Code:
Heres the Snippet for Understanding how i save this to eml file ...
$mailbox = '{'.$imapHost.':'.$imapPort.'}INBOX';
$imapStream = imap_open($mailbox, $imapUser, $imapPassword, 0, 1, array('DISABLE_AUTHENTICATOR' => 'GSSAPI'));
$hdr = imap_check($imapStream);
$overview = imap_fetch_overview($imapStream, "1:$mailCount");
$size = count($overview);
for($i=$count-1;$i>=0;$i--) {
-->$headers = imap_fetchheader($imapStream, $i, FT_PREFETCHTEXT);
-->$body = imap_body($imapStream, $i);
-->file_put_contents($fullPath . '/' . $saveemlfile, $headers . "\n" . $body);
}
imap_close($imapStream);
Thanks for your Help!

php imap check if email has attachment

I'm trying to build a small webmail app. When I read all the emails in inbox I want to show for each mail if it has attachments. This works, but the problem is that it takes to long to do that, about 0.5 secs for 1Mb email attach. Multiply that with all emails in inbox that have big attach files :|
My question is: How to check if an email has attach withouth loading the whole email ? Is that possible ?
Bellow is the code I'm using now:
function existAttachment($part)
{
if (isset($part->parts))
{
foreach ($part->parts as $partOfPart)
{
$this->existAttachment($partOfPart);
}
}
else
{
if (isset($part->disposition))
{
if ($part->disposition == 'attachment')
{
echo '<p>' . $part->dparameters[0]->value . '</p>';
// here you can create a link to the file whose name is $part->dparameters[0]->value to download it
return true;
}
}
}
return false;
}
function hasAttachments($msgno)
{
$struct = imap_fetchstructure($this->_connection,$msgno,FT_UID);
$existAttachments = $this->existAttachment($struct);
return $existAttachments;
}
To check whether the email has attachment, use $structure->parts[0]->parts.
$inbox = imap_open($mailserver,$username, $password, null, 1, ['DISABLE_AUTHENTICATOR' => 'PLAIN']) or die(var_dump(imap_errors()));
$unreadEmails = imap_search($inbox, 'UNSEEN');
$email_number = $unreadEmails[0];
$structure = imap_fetchstructure($inbox, $email_number);
if(isset($structure->parts[0]->parts))
{
// has attachment
}else{
// no attachment
}
imap_fetchstructure does fetch the whole email content in order to analyze it. Sadly there is no other way to check for attachment.
Maybe you can use the message size info from imap_headerinfo to get a prediction if the message will have attachments.
Another way is to fetch the emails in an regular interval in the background and store them with their content and UID for later lookup in a database. You need to do that later anyway when you want so search for specific messages. (You do not want to scan the while imap account when searching for "dinner")

Categories