Zend Mail and encodings, content-transfer etc. - Unified? - email

Is there any class in the Zend Framework that allows me to easily read emails?
The Zend_Mail class does allow me to easy get headers, subject and the content body. But transferring everything to UTF-8 and human-readable format is still a pain.
Or am I doing something wrong? As far as I can tell, Zend Framework does not allow me to easily get UTF-8 strings that I can just use, I still have to do some post-processing. Right?

The key thing is that you need to iterate over the parts within the Message and find the text. Once you have it, then you can use quoted_printable_decode to get the text itself in a useful way.
This is some rough and ready code that reads IMAP email boxes with Zend_Mail:
<?php
$mail = new Zend_Mail_Storage_Imap(array(
'host' => EMAIL_ACCOUNT_HOST,
'user' => EMAIL_ACCOUNT_USERNAME,
'password' => EMAIL_ACCOUNT_PASSWORD,
));
echo (int)$mail->countMessages() . " messages found\n";
foreach ($mail as $message) {
$from = $message->getHeader('from');
$subject = trim($message->subject);
$to = trim($message->to);
$body = getBody($message);
// do something with message here
}
function getBody(Zend_Mail_Message $message)
{
// find body
$part = $message;
$isText = true;
while ($part->isMultipart()) {
$foundPart = false;
$iterator = new RecursiveIteratorIterator($message);
foreach ($iterator as $part) {
// this detection code is a bit rough and ready!
if (!$foundPart) {
if (strtok($part->contentType, ';') == 'text/html') {
$foundPart = $part;
$isText = false;
break;
} else if (strtok($part->contentType, ';') == 'text/plain') {
$foundPart = $part;
$isText = true;
break;
}
}
}
if($foundPart) {
$part = $foundPart;
break;
}
}
$body = quoted_printable_decode($part->getContent());
}

Related

Using php mail() to do simple checks

I am using php mail() (Via piped to program) and my goal is to simply get the email and scan the "from" header to filter it and then if it passes my "rules" or "checks" pass it along to the intended receiver. I have been able to use a sample code to get the mail and I can actually get my "test check" done. The problem I am having is that I cannot get the php mail() function to resend the mail as it was (plain or html). Every time i get my test mail, it comes with all the headers exposed and code. Not a nice and neat email. I also found out that I could encounter problems with this if there are attachments to the mail. I have seen alot of suggestions about going thru PHPMailer and I am willing to entertain that option. I just don't need this to get to complicated. here is the code I am using -
#!/usr/bin/php -q
<?php
$notify= 'myemail#mydomain.com'; // an email address required in case of errors
function mailRead($iKlimit = "")
{
if ($iKlimit == "") {
$iKlimit = 1024;
}
$sErrorSTDINFail = "Error - failed to read mail from STDIN!";
$fp = fopen("php://stdin", "r");
if (!$fp) {
echo $sErrorSTDINFail;
exit();
}
$sEmail = "";
if ($iKlimit == -1) {
while (!feof($fp)) {
$sEmail .= fread($fp, 1024);
}
} else {
while (!feof($fp) && $i_limit < $iKlimit) {
$sEmail .= fread($fp, 1024);
$i_limit++;
}
}
fclose($fp);
return $sEmail;
}
$email = mailRead();
$lines = explode("\n", $email);
$to = "";
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
if ($splittingheaders) {
$headers .= $lines[$i]."\n";
if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
$subject = $matches[1];
}
$tst = substr($subject, -3);
if ($tst == "win" | $tst == "biz" | $tst == "net"){
$subject = $subject . "BAD ADDRESS";
}
if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
$from = $matches[1];
}
if (preg_match("/^To: (.*)/", $lines[$i], $matches)) {
$to = $matches[1];
}
} else {
// not a header, but message
$message .= $lines[$i]."\n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$splittingheaders = false;
}
}
mail('noreply#mydomain.com', $subject, $message);
?>
I am interested in learning, I am code savvy. Somewhat new to email formatting, but very understanding of PHP. Any help is appreciated. Thanx.

Laravel - How to send email to all users

Im struggling to make a code that sends email to my subscribed users. I want to pass body to view according to users default language, can anyone help me ?
My code:
if($newsletter->save())
{
//get users to send to
$users = User::where('newsletter', '=', '1')->Where('activated', '=', '1')->get();
//Send to all users subscribed
foreach($users as $user)
{
//set info according to user default lang
if($user->default_lang == 'pt')
{
$body = $newsletter_pt;
$subject = Input::get('subject_PT');
}
elseif($user->default_lang == 'de')
{
$body = $newsletter_de;
Input::get('subject_DE');
}
$data = array(
'body' => $body,
'subject' => $subject
);
$from_name = Input::get('from_name');
$from_email = Input::get('from_email');
//QUEUE The Newsletters to send
Mail::queue('admin.newsletters.template1', $data, function($message) use ($user, $subject, $from_name, $from_email)
{
$message->from($from_email, $from_name);
$message->bcc($user->email, $user->name);
$message->subject($subject);
});
} //end foreach
return Redirect::To('admin/newsletters')->with('message', array("1" => "Newsletter enviada com sucesso !"));
}
Thanks a lot ;)
Ensure that the $data array has the information you expect to pass into the view before you call Mail::queue(). It should pass both a $body and $subject variable to your view.
You might also try using Mail::send() instead of Mail::queue() and see if it makes a difference (if you're not using a queueing system). Sometimes the queue() method can trip up when serializing Eloquent models, so if $newsletter_pt or $newsletter_de are Eloquent models they might not make it to the view intact.

How to read accents using Zend_Mail_Storage_Imap

I'm trying to read mails with Zend_Mail_Storage_Imap. Here is a part of my code :
$mail = new Zend_Mail_Storage_Imap(
array( 'host' =>'imap.gmail.com',
'ssl' =>true,
'port'=>993,
'folder'=>'inbox',
'user' => '***',
'password' => '***'
)
);
foreach ($mail as $k => $message) {
$msg = $mail->getMessage( $k );
echo $msg->getContent();
}
Everthing work's well, except the accent format. I have things like this :
int=C3=A9gration for intégration
S=C3=A0rl* for sàrl
Can somebody help me?
utf8_decode( quoted_printable_decode( $part->getContent() ))
Have a look into the "getText()" method here:
http://wiip.fr/content/zend-mail-storage-imap
The text is in french, but don't bother and only take what you need into this getText method. Basically you need to determine the encoding of the email & decode it.
You can also have a look there : Email decoding doesn't work in zend mail
here it is a possible solution if you use utf-8 internal encoding.
/**
* retrieves message content
* #param Zend_Mail_Message|Zend_Mail_Part $oMessage
* #return string returns message content as utf-8 string from message object
*/
public static function contentDecoder ($oMessage, $bVerbose = false) {
mb_internal_encoding("UTF-8");
echo ($bVerbose)?("\tultimateContentDecode {\n"):('');
if($oMessage->headerExists('content-type')) {
echo ($bVerbose)?("\t\t" . $oMessage->contentType . "\n"):('');
preg_match('/^([a-z\/]+)(?:;\s+format=[a-zA-Z0-9-]+)?(?:;\s+charset=([a-zA-Z0-9-]+))?/i',
str_replace('"', '', $oMessage->contentType), $matches);
list(,$sMimeType,$sEncoding) = $matches;
echo ($bVerbose)?("\t\tType of this part is {$sMimeType}, {$sEncoding}\n"):('');
} else {
$sContent = $oMessage->getContent();
$sMimeType = 'text/plain';
$sEncoding = mb_detect_encoding($sContent, self::$_sEncDetectOrder);
echo ($bVerbose)?("\t\tUnknow content type. text/plain; charset={$sEncoding}\n"):('');
}
$sEncoding = strtoupper($sEncoding);
if($oMessage->headerExists('content-transfer-encoding')) {
$sQuoting = $oMessage->contentTransferEncoding;
echo ($bVerbose)?("\t\tContent-Transfer-Encoding: " . $oMessage->contentTransferEncoding . "\n"):('');
} else {
$sQuoting = '';
}
$sContent = $oMessage->getContent();
switch ($sQuoting) {
case 'quoted-printable':
case '7bit':
$sContent = quoted_printable_decode($sContent);
break;
case 'base64':
$sContent = base64_decode($sContent);
break;
}
if ($sEncoding != 'UTF-8') {
echo ($bVerbose)?("\t\tConvert encoding: {$sEncoding} -> UTF-8 \n"):('');
$sContent = mb_convert_encoding($sContent, 'UTF-8', $sEncoding);
}
$sContent = trim(preg_replace(
array('/^\s+/m', '/\s+$/m', '/[ \t]+/'),
array('', '', ' '), $sContent));
echo ($bVerbose)?("--\n{$sContent}\n--"):('');
echo ($bVerbose)?("\t}\n"):('');
return $sContent;
}

Zend_Mail_Part: Get number of attachments

How can i find out that how many attachments a message has?
Is this method reliable?
$attachments = 0;
$msg = $mail->getMessage($msgno);
if($msg->isMultipart()){
$parts = $msg->countParts();
for($i=1; $i<=$parts; $i++){
$part = $msg->getPart($i);
try {
if(strpos($part->contentType,'text/html')===false && strpos($part->contentType,'text/plain')===false)
$attachments++;
} catch (Zend_Mail_Exception $e) {}
}
}
or this?
$matches = array();
$pattern = '`Content-Disposition: (?!inline)(.*)[\s]*filename=`';
$attachments = (string) preg_match_all($pattern, $storage->getRawContent($msgno), $matches);
It is possible to have attachments that are text/html or text/plain, so it may not be reliable in all cases. If there is an attachment that is an HTML file for example, you could have this situation.
You may be better off checking the content-disposition of each mime part instead:
$attachments = 0;
$msg = $mail->getMessage($msgno);
if($msg->isMultipart()){
foreach($msg->getParts() as $part) {
try {
if ($part->disposition == Zend_Mime::DISPOSITION_ATTACHMENT ||
$part->disposition == Zend_Mime::DISPOSITION_INLINE)
$attachments++;
} catch (Zend_Mail_Exception $e) {}
}
}

Zend Framework view render takes a lot of memory

I try to create emails for next sending with my cron php script.
And I use Zend_View for rendering email template.
I have 50k users but 3000 emails was created with 64MB memory limit and 7200 with 128MB.
Code of rendering emails
public function prepareEmailBody($template, $templates)
{
$view = new Zend_View(array('basePath' => './application/views'));
$template_file_name = $template . '.phtml';
foreach ($templates as $key => $value) {
$view->$key = $value;
}
$body = $view->render('mails/' . $template_file_name);
return $body
}
And use this method in
foreach ($users as $user) {
.....
$text = Mailer::getInstance()->prepareEmailBody($template, $vars);
.....
}
Please advice how optimize code.
You could try using one View object and the partial helper instead, this might improve things (or might make it slower).
public function getView()
{
if (!$this->_view) {
$this->_view = new Zend_View(array('basePath' => './application/views'));
}
return $this->_view;
}
public function prepareEmailBody($template, $templates)
{
$template_file_name = $template . '.phtml';
$body = $this->getView()->partial($template_file_name, $templates);
return $body
}