PHPMailer sending double emails - php

I am working on theapplication that fetches clients emails from DB, stores them in a array, loop through the array and send email to each client individually.
The problem occurred when PHPMailer is sending a double email to clients and also in and in one of emails "to" field contains both recepient address and in second email there is also a second clients emails as well.
I consider this as a vulnerability.
Here is my code:
$array = [ 0 => 'email#gmail.com' , 1 => 'email2#gmail.com' ]; //Example
foreach ($array as $key => $value) {
$mail->addAddress($value);
if (!$mail->send())
{
throw new Exception($mail->ErrorInfo);
} else
{
$mail->addAddress(NULL); //Attempt to unset
header('Location: ../public/email.php'); //Redirect to start page
}
}
So to sum it up: When this is run, it sends email to 'email#gmail.com' and 'email2#gmail.com'.
First email will get one copy of email.
Second email will get two copies of the same email, first with showing itself as recipient, and second one with itself recipient + other clients email.
I've tested this with only 2 fetched clients, with even more I guess there will be even more repetitions.
Thank you guys!

There are two problems here:
You are not removing the address from the mail, so when you add a second one, the first one is still there and both will see the other address.
You are using a header redirect in your loop without terminating your script. This can cause code after the redirect to run, but there is no guarantee for how much and how long.
The first problem you can solve by clearing the recipients at the end of the loop:
$mail->ClearAllRecipients();
As for the second problem, you should not redirect anywhere inside the loop and when you redirect after all messages have been sent, you should exit your scipt using exit; so that nothing gets executed after that.

I think you're re-using $value. Try using unset($value); see if it helps. You can also look here
Warning
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset(). Otherwise you will experience the following behavior:

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.

Validate an array of values, output an error and stop script if one value fails?

I have an array of emails. I would like to do some validation on each one to see if it is more or less the proper format. This script is validating emails for the administration side of a website. This particular piece of the administration side is for sending newsletters to particular groups, each containing an array of emails.
I am adding the functionality to add a group with administrator specified recipients. This would become useful if the administrator was getting rid of a particular group of newsletter recipients and wanted to add them to another group before destroying the original group.
I have come up with a way to throw an error if any of the items in the array do not match the validation, however, it seems like there should be a better way to do this in PHP. I have not been able to find an alternative method.
$email_array_data["count"] = count($email_array);
foreach($email_array as $email) {
if (email_validation_function($email) {
$email_array_data["passed_validation"]++;
} else {
$email_array_data["failed_validation"][] = $email;
}
}
if ($email_array_data["count"] == $email_array_data["passed_validation"]) {
Send The Emails
} else {
Echo The Emails That Failed Validation
}
This script works pretty well, but it seems like there would be a better way to do this that checking that every email met the requirements, then comparing the number of emails that passed/failed validation and the count of the emails array.
Is there a better method?
First, you should check out filter_var(). It's a great function for validating tons of data, especially emails (see here).
There are many ways to handle errors. Based on what I can see from your script, you are only considering the array valid if all emails are valid. You could throw an exception.
$is_valid = filter_var($email, FILTER_VALIDATE_EMAIL);
if( ! $email )
throw new Exception('Invalid email address supplied');
If you go this approach, you could catch the exception using a try {} catch {} Just another approach I guess.

Send multiple mail at a time but other reciepient should not display i.e. other recipient in bcc

I want to send mail to multiple user at a time but one user should not see other users addresses i.e. make them in bcc but mail should be sent only once.I have used PHPMailer for that.
$i = 1;
$emailCount = count($newEmail);
foreach($newEmail as $emailAddress)
{
if($emailCount != $i)
{
$phpmail->AddAddress($emailAddress);
}
$i++;
}
The code you are looking for is:
$phpmail->AddBCC($emailAddress);
Place this in your loop to add all of the addresses you would like to send to as BCCs. I believe that you do not need to specify an address using AddAddress, rather you can just add many BCCs and send the email that way. See this other thread for more info.
See this forum thread for information on including the correct files.
As MrGingerbear pointed out, AddBCC is all you need to do.
Each person will only receive one message and they will not be able to see other recipients.
Here is what I have:
$recipients = array(
'recipient1#domain.com' => 'Alex Baker',
'recipient2#domain.com' => 'Charles Dickens',
);
foreach($recipients as $email => $name)
{
$mail->AddBCC($email, $name);
}
EDIT: Clarifying that AddAddress() is not required, simply used most of the time because you usually include an address in the To: field. However, in your scenario, it would be disadvantageous to do so because then all users would see the address of each user.
EDIT 2: It is not possible, because of the way email is designed, to send only one message, but have the To: field look different to each recipient and also not see the email addresses of the other recipients.

How to get the sender's email address from Zend_Mail_Message?

I'm using the Zend_Mail_Message class to display emails inside of my PHP app, and am able to output the subject, content, date and even the NAME of the sender (using $message->from) but I can't figure out how to get the email address of the person who sent the message. The documentation is no help and googling finds a million results for how to send messages with Zend, but nothing about getting the address that sent the message.
EDIT:
This is how I ended up doing it. After some more digging, I found the sender's email in a field called 'return-path'. Unfortunately, this field has a dash in the name (WTF??) so to access it, I had to do this:
$return_path = 'return-path';
$message->reply_to = $zendMessage->$return_path;
Using the return-path caused some problems with some emails, specifically messages from no-reply accounts (mail-noreply#gmail.com, member#linkedin.com etc). These addresses would end up looking something like this:
m-9xpfkzulthmad8z9lls0s6ehupvordjdcor30geppm12kbvyropj1zs5#bounce.linkedin.com
...which obviously doesn't work for display in a 'from' field on the front-end. Email clients like Apple Mail and Gmail show mail-noreply#gmail.com or member#linkedin.com, so that's what I was going for too.
Anyways, after some more research, I discovered that the 'from' field in a Zend Mail Message object always looks something like this:
"user account name" <user#email.com>
The part in < > is what I was after, but simply doing $from = $zend_message->from only gave me the user's account name (hence my original question). After some more playing around, this is how I finally got it working:
$from = $zendMessage->from;
$start = strpos($from, '<');
$email = substr($from, $start, -1);
$result = str_replace('<', '', $email);
Hopefully this will save someone some frustration. If anyone knows of a simpler way of doing this, please let me know.
This works well..
$senderMailAddress = null;
foreach ( $message->getHeader('from')->getAddressList() as $address ) {
if ( $senderMailAddress === null) {
$senderMailAddress = $address->getEmail();
}
}
The main problem here is that many email programs, relay agents and virus scanner along the way do funny stuff to an actually simple and well defined email standard.
Zend_Mail_Message extends to Zend_Mail_Part which has a method called getHeaders(). This will have all the data from an email stored in the head versus the body which is accessed with getContent() and the actual email message.
With this method you'll get an array of all the key/value pairs in the header and while developing you should be able to determine which header field you will actually want. Once you know that you can then get the actual field with getHeader('field_name') or with its actual name directly.
However, if you have to many different email senders you may want to stick with the complete header array though and evaluate multiple fields for the best result like if there's an "reply-to" address. Again there are many uncertainties because the standard isn't always obeyed.

Categories