Feedback form hangs due to PHPMailer - phpmailer

I have a feedback form, this script is filtering and validating data and writing data to the database. At the very bottom, through include, I connected a script with PHPMailer, which sends the text of the feedback form to my mail via gmail smtp.
If I comment out the script connections, then the form is submitted either immediately or after 1 second. With him, he can wait 2-3 seconds.
I submit form data via XMLHttpRequest, remotely. Upon successful submission, the form is reset to zero and the submit button becomes inactive, and via pop-up notifications I display the server's response. So it happened that I managed to press the send button 2-3 times until the script worked and, accordingly, several records were created and several letters were sent to the mail. Is this how it should be or have I configured PHPMailer wrong?
Let me know what data I need to attach. This is my PHPMailer script:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require $_SERVER['DOCUMENT_ROOT'] . '/form/PHPMailer/PHPMailer.php';
require $_SERVER['DOCUMENT_ROOT'] . '/form/PHPMailer/Exception.php';
require $_SERVER['DOCUMENT_ROOT'] . '/form/PHPMailer/SMTP.php';
$mail = new PHPMailer;
$mail->CharSet = 'UTF-8';
$mail->isSMTP();
$mail->SMTPDebug = 0; // 0 = off (for production use) - 1 = client messages - 2 = client and server messages
$mail->Host = gethostbyname("smtp.gmail.com");; // use $mail->Host = gethostbyname('smtp.gmail.com'); // if your network does not support SMTP over IPv6
$mail->Port = 587; // TLS only
$mail->SMTPSecure = 'tls'; // ssl is deprecated
$mail->SMTPAuth = true;
$mail->Username = 'mymail#gmail.com'; // email
$mail->Password = 'mypassword'; // password
$mail->setFrom($email, $name); // From email and name
$mail->addAddress('mymail#gmail.com', 'Admin'); // to email and name
$mail->Subject = $subject;
$mail->msgHTML("Message from: \n"."<h3>".$email."</h3>\n"."<h1>".$message."</h1>"); //$mail->msgHTML(file_get_contents('contents.html'), __DIR__); //Read an HTML message body from an external file, convert referenced images to embedded,
$mail->AltBody = 'HTML messaging not supported'; // If html emails is not supported by the receiver, show this body
// $mail->addAttachment('images/phpmailer_mini.png'); //Attach an image file
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
$mail->send();
// if(!$mail->send()){
// echo "Mailer Error: " . $mail->ErrorInfo;
// }else{
// echo "Message sent!";
// }
?>

You're doing some inadvisable things here.
$mail->Host = gethostbyname("smtp.gmail.com");
This sets Host to a literal IP address, and that in turn means that you will never have it match a TLS certificate name. As a result you're having to disable TLS verification, which is never a good thing. If you understand exactly why you're doing this, and what the consequences are, that's fine, but if not, you shouldn't be doing it.
There isn't any error checking in this script. I suggest starting again using the gmail example provided with PHPMailer which is much more careful.
You've discovered why sending to remote mail servers with SMTP during web form processing generally a bad idea: it's too slow, partly by design. The best way to work around this is to install a local mail server (postfix is good) and configure it as a relay to your gmail account – if you search for that you'll find plenty of examples. When that's done, you can submit messages to localhost, it will be more or less instant, and it will take care of queuing, throttling, bounces etc.

Related

AddStringAttachment giving unusual results

I am sending attachments (CSV) which I have been sending for years using mail() but decided to migrate to SMTP for better reliability.
Code 1 (CSV attachment)
$attachment = $this->CSVData; // "Date","Name","Dept" ... \n"2019-03-13","Dave" ...
$encoding = 'base64';
$contentType = 'text/csv';
$filename = $this->createFileName(); //Get FileDate and Name
$recipient = $delivery_email; // xxxxxxx#gmail.com
$subject = $this->emailHeader['subject'] . " CSV Data";
$message = 'Daily Data File';
$mail = new PHPMailer\PHPMailer\PHPMailer(true); // Passing `true` enables exceptions
try {
//Server settings
$mail->SMTPDebug = 0; // Enable verbose debug output
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = SMTP_HOST; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = SMTP_USER; // SMTP username
$mail->Password = SMTP_PASS; // SMTP password
$mail->SMTPSecure = SMTP_AUTH; // Enable TLS encryption, `ssl` also accepted
$mail->Port = SMTP_PORT; // TCP port to connect to
//Recipients
$mail->setFrom($this->fromEmail, $this->fromEmailName); // Add a FROM
$addresses = explode(',', $recipient);
foreach ($addresses as $address) {
$mail->AddAddress(trim($address)); // Add a recipient(s)
}
if ( !empty($this->emailCC) ) $mail->addCC($this->emailCC); // Add a CC
//13-03-2019: Add the attachment to the email
$mail->AddStringAttachment($attachment, $filename, $encoding, $contentType);
//Content
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = $subject;
$mail->Body = $message;
$mail->AltBody = 'This email is formatted in HTML';
$mail->send();
$this->fo->printStatus('Email successfully sent to: '. $recipient );
return true;
} catch (Exception $e) {
$this->fo->printStatus( basename(__FILE__) .' '. __LINE__ . ': Message could not be sent. Mailer Error: '. $mail->ErrorInfo );
return false;
}
The email gets delivered to me BUT ...
Problems:
When viewing in Gmail browser - I get message: "
Gmail virus scanners are temporarily unavailable – The attached files haven't been scanned for viruses. Download these files at your own risk."
When viewing in Gmail browser - I cant save/download the file? (clicking the download button does nothing)
When clicking attachment to view in browser, I now get error: "Whoops. There was a problem while previewing this document"
I try "Show Original" and it takes 30+ seconds for the email to load which just shows the base64 encoded data
I tried to open in inbox in Outlook and after 5 minutes of the emails not loading I gave up (thinking the emails are not encoded properly or something causing outlook to get stuck)
It looks like it is working (i.e. the file looks legit based on the gmail icon preview) but I cant do anything else with it and I don't know if it is a Gmail issue or File Issue.
Any advice?
Turns out that Gmail was having issues all yesterday afternoon with attachments. It was a Gmail issue - The timing is unbelievable
https://www.theguardian.com/technology/2019/mar/13/googles-gmail-and-drive-suffer-global-outages

PHPMailer Debug messages

I'm using PHPMailer and I would like to save in my DB some debug info.
The code below shows how to save debug info when using an SMTP server like below:
mail->SMTPDebug = SMTP::DEBUG_SERVER;
$mail->SMTPDebug = 2; //Alternative to above constant
$mail->isSMTP(); // tell the class to use SMTP
$mail->SMTPAuth = true; // enable SMTP authentication
$mail->Port = 25; // set the SMTP port
$mail->Host = "mail.yourhost.com"; // SMTP server
$mail->Username = "name#yourhost.com"; // SMTP account username
$mail->Password = "your password"; // SMTP account password
According to the PHPMailer documentation SMTPDebug has 4 levels.
SMTP::DEBUG_OFF (0): Disable debugging (you can also leave this out completely, 0 is the default).
SMTP::DEBUG_CLIENT (1): Output messages sent by the client.
SMTP::DEBUG_SERVER (2): as 1, plus responses received from the server (this is the most useful setting).
SMTP::DEBUG_CONNECTION (3): as 2, plus more information about the initial connection - this level can help diagnose STARTTLS failures.
SMTP::DEBUG_LOWLEVEL (4): as 3, plus even lower-level information, very verbose, don't use for debugging SMTP, only low-level problems.
When I use level 2 I get Client and Server responses logged to the screen.
Is there a way to have only Server responses logged? I do not want to save Clients request too as it may have some sensitive data (The email content sent itself).
Yes. You can inject a closure that captures the debug output and writes it to your DB. There is an example in the docs:
It might be something like this (including filtering out client messages):
$mail->Debugoutput = function($str, $level) use ($db) {
if (strpos($str, 'CLIENT -> SERVER') === false) {
mysqli_query($db, "INSERT INTO maildebug SET level = '".mysqli_real_escape_string($db, $level)."', message = '".mysqli_real_escape_string($db, $str)."'");
}
};
A somewhat tidier option would be to accumulate all the debug output in a temporary variable, and only write it to the DB if there is a problem.

SMTP connect() failed error after 70 successful sends

A number of people had shown similar question, using phpmailer with g-mail. This problem is slightly different and may need a different answer.
The phpmailer code is fairly standard (below). I have a loop that goes through some data in the table and builds customised messages for recipients, sending a message for each row in the table. When the message is sent, it successfully completes about 75 out of some 100 recipients, and for the last 25, it reports the SMTP connect() failed error. This doesn't happen every time, and occasionally, the script goes through the loop without errors.
<?php
$mail = new PHPMailer();
$mail->IsSMTP(); // telling the class to use SMTP
$mail->SMTPDebug = 0; // enables SMTP debug information (for testing)
$mail->SMTPsecure = "ssl";
$mail->SMTPAuth = true; // enable SMTP authentication
$mail->Host = "smtp.gmail.com"; // sets the SMTP server
$mail->Port = 587; // set the SMTP port for the GMAIL server
$mail->Username = "xx"; // SMTP account username
$mail->Password = "xx"; // SMTP account password
$mail->SetFrom("xx#gmail.com");
$mail->isHTML(true);
$mail->CharSet = "utf-8";
$mail->Subject = $msgsubject;
$mail->AltBody = "Invitation";
do {
$membername = $row_musicians['name'];
$membernumber = $row_musicians['number'];
$emailaddress = $row_musicians['email'];
$rhdate = $row_musicians['rhdate'];
$mail->clearAttachments();
$mail->Body = 'Dear '.$membername.',
<p>'.$maintext.'</p>
';
// some more text using custom variables from database
// closing part of the message
$mail->Body .= '<p>'.nl2br($closing, false).'</p>
</body>
</html>';
$mail->AddAddress($emailaddress, $membername); // sending each message
sleep(1); // wait one second between sends, to avoid spam filters
echo '<br />
Recipient: '.$membername.' ('.$emailaddress.') -- ';
if(!$mail->Send()) {
echo "Mailer Error: <div style='color:#009999'>" . $mail->ErrorInfo." </div>";
} else {
echo "Message sent!";
}
$mail->ClearAddresses();
} while ($row_musicians = mysql_fetch_assoc($musicians));
mysql_free_result($musicians);
?>
I recommend basing your code on the mailing list example provided with PHPMailer.
You're doing most things right - initialising PHPMailer first and setting properties that are common to all messages, but one key thing is missing:
$mail->SMTPKeepAlive = true;
Without this it means that it closes and reopens a connection for every message, and you're mostly likely running into connection rate limits.
you're also using the combination of Port= 587 and SMTPSecure = 'ssl' which will generally not work; switch to Port = 465.
I'd also recommend against the do/while loop - it will fail if your database query fails to find anything because it's bottom-tested and thus will always run once, even if there is no data. Use a while or foreach loop instead.
The mailing list example does all of these things already.
Final thing - if this is all your code, it looks like you're running an old version of PHPMailer, so get the latest.

List-Unsubscribe in header prevents email from being delivered to gmail

I am using phpmailer to send email.
When I add the list-unsubscribe the email gets delivered to all accounts, except gmail. It just gets dropped, it doesn't go into spam, it just never arrives at the gmail account. When I remove the list-unsubscribe, it successfully gets sent to the gmail account.
This is the list-unsubscribe that I am using:
List-Unsubscribe:<http://keepity.com>,<mailto:admin#keepity.com>
This is how its called in phpmailer:
$mail->AddCustomHeader("List-Unsubscribe:<http://keepity.com>,<mailto:admin#keepity.com>");
This is the full function that calls phpmailer. If I comment out the list-unsubscribe then the mail gets delivered to gmail account, otherwise it never arrives. Does anyone know why it would not be delivered?
static function phpmailer_sendmail($mail,$from,$fromAlias,$to,$replyTo,$replyToAlias,$subject,$html,$text) {
require_once (JPATH_COMPONENT.DS.'PHPMailer-master/class.phpmailer.php');
$mail = new PHPMailer(true); // by setting TRUE you enable exceptions
$mail->IsSMTP(true); // SMTP
$mail->SMTPAuth = true; // SMTP authentication
$mail->Mailer = "smtp";
$mail->Host= "xyz"; // Amazon SES
$mail->Port = 465; // SMTP Port
$mail->Username = "xyz"; // SMTP Username
$mail->Password = "xyz"; // SMTP Password
$mail->ClearAllRecipients();
$mail->ClearAddresses();
$mail->ClearCCs();
$mail->ClearBCCs();
$mail->ClearReplyTos();
$mail->ClearAttachments();
$mail->ClearCustomHeaders();
$mail->SetFrom($from, $fromAlias);
$mail->AddReplyTo($replyTo,$replyToAlias);
$mail->Subject = $subject;
$mail->IsHTML(true);
$mail->Body = $html;
$mail->AltBody = $text;
$address = $to;
$addressAlias = $to;
$mail->AddAddress($address, $addressAlias);
$mail->AddCustomHeader("List-Unsubscribe:<http://keepity.com>,<mailto:admin#keepity.com>");
$mail->Send();
}
the function addCustomHeader gets 2 arguments
and the unscribe value format should be
<email_to_unscribe#email.com>, <http://url_to_unscribe.com>
here is an example how it should be called :
$mail->addCustomHeader("List-Unsubscribe",'<admin#keepity.com>, <http://keepity.com/?email='.$address.'>');
I know this is old, but it's ranking well in Google for a search of "List-Unsubscribe" and the provided suggestion isn't quite correct.
PHPmailer addCustomHeader only takes one argument. The double quotes wrap the entire header like this.
$mail->AddCustomHeader("List-Unsubscribe: <mailto:info#example.com?subject=Unsubscribe>, <http://example.com/unsubscribe.php?mailid=1234>");
List-Unsubscribe takes 2 arguments, a mailto: and a URL that can be set up to automatically unsubscribe the email. Of course you can generate the mailid (or whatever you call the GET var) dynamically too.

Signing PHP emails on shared hosting

I am on a shared hosting plan, and when I send emails with PHP, email clients (like Gmail) will add a little via bit to my from field, with my host's domain in there.
So instead of my emails being just from my domain:
From: me#mydomain.com
It's from two domains:
From: me#mydomain.com via host13.myhost.com
Clearly, this is confusing to people receiving email and is poor branding. Since I'm on a shared hosting plan, I don't think I'm likely to have access to the configurations settings of PHP or whatever it uses to mail. Is it possible for me to digitally sign my PHP emails, or is this not possible on shared hosting?
Here is what I'm doing now:
$header = "From: me#mydomain.com";
mail("you#yourdomain.com", "subject", "body", $header);
You can try this, you need to download the PHP Mailer class from Here and your code will be like this:
<?php
include "PHP MAILER CLASS";
$mail = new PHPMailer(true); // the true param means it will throw exceptions on errors, which we need to catch
$mail->IsSMTP(); // telling the class to use SMTP
try {
//$mail->SMTPDebug = 1; // enables SMTP debug information (for testing)
$mail->SMTPAuth = true; // enable SMTP authentication
$mail->SMTPSecure = "ssl"; // sets the prefix to the servier
$mail->Host = "smtp.gmail.com"; // sets GMAIL as the SMTP server
$mail->Port = 465; // set the SMTP port for the GMAIL server
$mail->Username = "example#gmail.com"; // GMAIL username
$mail->Password = "password"; // GMAIL password
$mail->AddAddress("Reciever Email", "Reciever Name");
$mail->SetFrom('Sender Email', 'Sender Name');
$mail->Subject = "Subject";
$mail->AltBody = 'To view the message, please use an HTML compatible email viewer!'; // optional - MsgHTML will create an alternate automatically
$mail->MsgHTML("Message Body");
$mail->Send();
} catch (phpmailerException $e) {
$e->errorMessage(); //Pretty error messages from PHPMailer
} catch (Exception $e) {
$e->getMessage(); //Boring error messages from anything else!
}
?>
The default mail function is at the mercy of your server setup and rarely looks like regular mail to the reciever. You should use a library either SwitfMailer or pear MAIL that can send the mail through your own mail server via SMTP. You can use your normal email account or setup a new one for your web service.

Resources