Capture email smtp error with print_debugger without attachment dump - php

Scenario: I have a number of Codeigniter applications that I am improving the email delivery system to use a database queue. So my application logs all emails that need to be sent into the database. I run a cron job every minute to check this database queue and send out any pending emails (throttled to my server limits as needed).
It is all working fantastically and has dramatically improved our applications.
Part of the feature is it will record when emails are not sent, and will retry again later. As part of the process I update the database to include any error messages during the send attempts, for debugging later. I do this with $this->email->print_debugger().
Once again all working ok (normally).
The problem is if an attachment was included in the email, the attachment 'data' is dumped as part of the print_debugger() - so my database gets smashed with large text dumps of attachment data.
What I tried: So I went looking in the Email.php class of codeigniter, thinking I could extend the library to just not include the attachment data in the print_debugger() function - but it seems that the error that is returned from the function is actually from the SMTP server itself in another function. I cant see a way to stop the attachment data coming through on the error.
Question/Challenge: Can anyone see a way where I can capture an SMTP error, and the email headers - but not the attachment data? I'm happy to settle for not capturing any of the email body either (since I know what that is anyway in my database queue).

I believe that you can just extend the library. This is the current function:
public function print_debugger()
{
$msg = '';
if (count($this->_debug_msg) > 0)
{
foreach ($this->_debug_msg as $val)
{
$msg .= $val;
}
}
$msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
return $msg;
}
There are four main parts to what is returned:
_debug_msg - The debug messages
_header_str - The email's headers
_subject - The email's subject
_finalbody - The body of the message and the attachment data
Implementing a similar function, without _finalbody should give you the desired output but without the body and attachment data.
Here's an example solution, (with the class 'MY_Email' located here at: application/libraries/MY_Email.php):
class MY_Email extends CI_Email
{
public function my_print_debugger()
{
$msg = '';
if (count($this->_debug_msg) > 0)
{
foreach ($this->_debug_msg as $val)
{
$msg .= $val;
}
}
$msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".'</pre>';
return $msg;
}
}
Looking through the code for the class, I think that $_body may contain the body data without the attachment data, so it could be worth trying to append $this->_body, rather than $this->_finalbody to the function. I've haven't tested this, so I'm not certain.
Hopefully this helps!

Related

Sendgrid multiple emails with different contentType?

Im trying to send multiple emails with Sendgrid, and everything works fine, unless i change Content type meanwhile.
Is it possible to change contentType determined which email im sending to?
The error:
{"message":"If present, text/plain and text/html may only be provided once.","field":"content","help":null}]
Code:
foreach ($emailAddress as $key => $email) {
$addresses["$email"] = "$key";
$perzon = new Personalization();
$perzon->addTo(new To((trim($email))));
$mail->addPersonalization($perzon);
if ($email == "example#example.org" || $email == "iexample2#example2.org") {
$mail->addContent(
"text/plain",
str_replace(" ", "", $messagePlain)
);
$mail->setSubject($subject);
} else {
$mail->addContent(
"text/html",
nl2br($message)
);
$mail->setSubject($subject);
}
}
It looks like you're re-using the $mail object each time you loop. And addContent() adds extra content to the email (clue's in the name! :-)), it doesn't overwrite existing content. So by the time you're on the second email you could have added two lots of plaintext content (for example), and the mailing class can't cope with that because it doesn't know which version of the plaintext content you really want to send - an email cannot contain more than one version of the same kind of content (because if it did, again the receiving client wouldn't know which version to display either).
So wherever you are declaring the $mail object (it isn't shown in your question), you need to move that line inside your foreach loop and that should resolve the issue, because you'll have a fresh instance of the email object each time, instead of keep adding things to an existing one.
Also I'd expect you need to move the code which sends the email inside the loop too, otherwise it'll only send once (using whatever was the last settings generated by the loop).

How can I send an email to lots of users using a proper way in CodeIgniter 3

I need some advice because I am building a subscription module. And I have a list of so many emails. Let say 1052 emails. And I have a code like this:
$email_list = $this->getClientEmails(); //all email for now returns 1052 valid emails
$valid_email = array();
$invalid_email = array();
if(count($email_list) > 0) {
for($x = 0; $x < count($email_list); $x++) {
if(valid_email($email_list[$x]['email'])) {
$valid_email[] = $email_list[$x]['email'];
}
//get all invalid emails
/*else {
$invalid_email[] = array(
'id' => $email_list[$x]['id'],
'email' => $email_list[$x]['email']
);
}*/
}
}
$email_string = implode(',', $valid_email);
$this->email->set_mailtype("html");
$this->email->from($from, 'Sample Admin');
$this->email->to($email_string); //send an email to 1052 users
$this->email->cc('test#sampleemail.com.ph');
$this->email->subject($subj);
$this->email->message($content);
$send_mail = $this->email->send();
if($send_mail) {
fp('success');
} else {
fp('failed');
}
Is it fine if I send an email like this? Or should I make a loop to send my email to different users? Means I will not use my imploded string. I will do the sending once in every week. And also what if the sending of email suddenly stops in the middle what should I do? Do I need to resend it again? Or should I make a column in my table that will update if the email is sent or not?
Can you give me some advice about this? That's all thanks.
Okay because you have a mailing list the first thing that i would recommend is that you push the script to background. Use selinium or cron for the same that way page render won't get stuck.
Once done You can send emails either way, send to multiple people or one at a time, both of them are valid and won't cause any problem. The point you need to consider here is the SMTP connection that you maintain.
If you are sending them all individually, you don't want to close connection to SMTP server and reconnect every time to send the mail which would only cause the overhead.
I should say that from your case the most valid way to send email is make a queue on some database preferably redis and have a task handle them in background (cron job if you are on cpanel or selinium if you own the server)
Finally this is a part that you might wanna test out. Because you have a mailing list i am guessing you don't want people to see through your whole list so check the headers when you are sending mails to all at once and if you don't see email from other users , you are good to go else send to each of them separately.
Also one final thing, emails not being delivered is usually bounced which may reflect bad on your server so have a script that flags emails that are constantly rejected and stop sending mails to the same or your ip address might end with bad repo and mails might end up in spam.
Have you thought of using PHPMailer as a library on your CodeIgniter installation?
You could just do it like this:
if(count($email_list) > 0) {
for($x = 0; $x < count($email_list); $x++) {
if(valid_email($email_list[$x]['email'])) {
$mail->addAddress($email_list[$x]['email'], $x);
}
}
}
Please refer to this example on how to use PHPMailer.
I hope this helps, or at least that it gives you a different perspective on how can this be done.
Referring to:
Or should I make a column in my table that will update if the email is sent or not?
Yes, I think that if you want to control if an email has been sent you should use a 1 character field on your table as a "flag" to corroborate that the email has been sent to your users.

Can't use an array in Swift Mailer function

I'm using Swift Mailer to send emails to users. I've following script to send email to a number of users at a time.
$j=0;
while($row = mysql_fetch_assoc($email))
{
$message[$j]->addTo($row['email_invited']);
$mailer->send($message[$j]);
$j++;
}
I get the following error while doing this:
Fatal error: Cannot use object of type Swift_Message as array in
/home/public_html/example.com/people.php on line 21
If I try to send emails like this:
while($row = mysql_fetch_assoc($email))
{
$message->addTo($row['email_invited']);
}
$mailer->send($message);
It sends the email normally & every user gets it but the issue with that is every user can see what other users have got the email along with him. So basically in "To" field, every email is bound & thus every users gets to see all other email addresses.
What I want to achieve is every user should get a personalized email only even though it's sent in bulk. That is why I tried to use array & separate email but getting error in that.
Any idea how to achieve that?
How about doing. I think you need to create new instance of message (and maybe mailer too) in every loop.
while($row = mysql_fetch_assoc($email))
{
$mailer = Swift_Mailer::newInstance($transportName);
$message = Swift_Message::newInstance($subject);
$message->addTo($row['email_invited']);
$mailer->send($message);
}

How do we use more than one message in flashMessenger using zend framework

I am using zend. I have the following piece of code,
....
$cust = 'test#test.com';
$list.='Also Sent Mail to following members';
foreach($m_list as $value)
{
$mail_to_manu = new Zend_Mail('utf-8');
$mail_to_manu->clearFrom();
$mail_to_manu->setBodyHtml('Text')
->setFrom('noreply#test.com', 'test admin')
->addTo($value['email'])
->setSubject('Test');
$mail_to_manu->send();
$list.=$value['manufacturers_email'].'<br/>';
}
$this->_helper->flashMessenger->addMessage('Mail send to '. $cust. ' Successfully'.$list);
$this->_redirector->gotoUrl('/index');
.....
I got message with out any break.my message looks like,
Mail send to test#test.com Successfully Also Sent Mail to following members some1#example.com some2#example.com...
I need to my message will be like,
Mail send to test#test.com Successfully
Also Sent Mail to following members,
some1#example.com
some2#example.com
...
So i need some break one after another.Is it possible to do that in Flash messenger. If yes,Kindly Advice.
Are you using strip_tags or something similar in the view script? It could cause the <br /> tags to get stripped out.
It's also possible to add multiple messages by calling flashMessenger->addMessage() once for every address:
$cust = 'test#test.com';
$this->_helper->flashMessenger->addMessage('Mail send to '. $cust. ' Successfully');
if(count($m_list)>0 )
$this->_helper->flashMessenger->addMessage('Also Sent Mail to following members');
foreach($m_list as $value)
{
$mail_to_manu = new Zend_Mail('utf-8');
$mail_to_manu->clearFrom();
$mail_to_manu->setBodyHtml('Text')
->setFrom('noreply#test.com', 'test admin')
->addTo($value['email'])
->setSubject('Test');
$mail_to_manu->send();
$this->_helper->flashMessenger->addMessage($value['manufacturers_email']);
}
$this->_redirector->gotoUrl('/index');
Your question
The reason why it's all bunched up together is that you're echoing it all out in a loop without additional markup.
What about something like:
foreach ($this->messages as $message)
{
echo '<p class="message">' . $this->escape($message) . '</p>';
}
But, in fact, there is a much better way of handling the flashMessenger in views. As you know the Zend FlashMessenger is an action helper. But there is also a great view helper avaiable which helps you output your messages nicely. The best thing about it is, that you can pass an array('warning' => 'This is a warning') and the array key (warning) will be used as class for the <p> tag. You can find information about this helper on Carlton Gibson's blog and additional explanations in this SO question.
Your variable naming needs improvement
Write readable variable names like $customer instead of just $cust. Nobody ever said shortening variable names is a means of writing lean code ;). Shortening variable names is bad (code smell) because it make code less readable for others and for yourself in the future.
Use casing like this $mailToManufacturer instead of using underscores. It's a general agreement (standard) and therefore good for readability and understanding of code too.

parsing mailbox (mbox or mbx) php

I need to parse mbox or email files using php, that is i would pass a file .mbox or .eml that contains several emails and parse it into its constituents e.g from, to, bcc etc.
is there any library that does it, or any code on how to do this in php?
thansk
There is a PEAR class http://pear.php.net/package/Mail_Mbox for that.
Albeit it's not difficult to separate a .mbox file manually. The individual mails are simply separated by /^From\s/ (may never appear in the mail body) and a block of Headers:. And most mail applications also store a length field in there. But it's indeed easier to use a readymade script for handling all the variations.
The PEAR class above works for getting individual messages out of MBOX, but if you want to then also parse the message into its constituent elements like "From Address", "Attachments", etc, then I would recommend mime_parser.php
In fact mime_parser.php can handle extracting the messages from a MBOX also, so depending on your needs, you might not need the PEAR class.
Here is the PEAR module Mail_Mbox for parsing mbox data:
https://pear.php.net/manual/en/package.mail.mail-mbox.php
if you need something faster for small needs, like extract all emails you collected into gmail by grouping into label and exported with google takeout in order to import the list let's say to mailchimp...
<?php
// tested with google mail > account > privacy > data exporter (with label)
// https://takeout.google.com/settings/takeout
$raw = file_get_contents('emails.mbox');
preg_match_all('/^Reply-To:\s(.*)$/im', $raw, $matches);
// avoid duplicate
$emails = array_unique($matches[1]);
$filtered_out = '';
// CSV field example (tested with mailchimp)
$filtered_in = 'Email Address' . "\n";
foreach ($emails as $email) {
$email = strtolower($email);
// filter out invalid emails, rarely
// happens that exporters makes mistakes ;)
// for example xxxxxxxxx#gmail.comx.xxxxxxxxxx.org
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
$filtered_in .= $email . "\n";
} else {
$filtered_out .= $email . "\n";
}
}
header('Content-Type: text/plain');
// save to file
// file_put_contents('emails.csv', $filtered_in);
echo $filtered_in;
?>
hope this help!

Categories