Debugging SMTP email failures - GSuite - php

I am working on an eCommerce site that sends a number of emails to the customer when they complete their order using G Suite SMTP relay service. But a large number of these emails are failing. There does not seems to be any pattern to it either - sometimes all emails will send, some times just one or two, and sometimes none.
I am getting the following error: 421, "4.7.0", Try again later, closing connection.
Looking here: https://support.google.com/a/answer/3726730?hl=en doesn't really help me debug this or figure out why some emails fail.
I am using the phpmailer class (https://sourceforge.net/projects/phpmailer/)
The issue seems to occur when the first handshake fails:
function Hello($host="") {
$this->error = null; # so no confusion is caused
if(!$this->connected()) {
$this->error = array(
"error" => "Called Hello() without being connected");
return false;
}
# if a hostname for the HELO was not specified determine
# a suitable one to send
if(empty($host)) {
# we need to determine some sort of appopiate default
# to send to the server
$host = "localhost";
}
// Send extended hello first (RFC 2821)
//If this fails then the second attempt will always fail
if(!$this->SendHello("EHLO", $host))
{
//when this fails it generates the try again later error
if(!$this->SendHello("HELO", $host))
return false;
}
return true;
}
So what is the best approach for debugging this?

The error message is pretty explicit. You call a 3rd party web service, which returns an error code which says the server you are calling is at capacity, try later. Is this a free service which allows you to upgrade to a paid plan? Usually whee you see his kind of thing.

Related

PHP Twilio - help needed to catch authentication errors

I am new to the Twilio PHP API and am finding it difficult using the sample code and even finding the correct documentation on the Twilio site to help me implement SMS messaging into our web app.
After much trial and error, and reading through some postings on this site, I have gotten far enough to figure out how to send a simple SMS message and even use a try/catch structure to deal with bad recipient phone numbers.
What I cannot for the life of me figure out is how to catch authentication errors with a client's SID and AUTH token. I'd like to catch authentication errors for a client's credentials before I try even sending messages for them. Some clients may blast a message to 200 customers so I don't want to spin my wheels if their authenticaiton fails. Heres what I have been trying:
require_once K_DOCROOT.'/includes/twilio-php-main/src/Twilio/autoload.php';
// use Twilio\Rest\Client;
// Find your Account SID and Auth Token at twilio.com/console
// and set the environment variables. See http://twil.io/secure
$sid = "ACf6eXXXGGGDDDDDDDDDDDDDDDDDDDDDDD"; // intentionally incorrect
$token = "164d3a88f753e553691960fb4d405f82";
$dest = "+15106443333"; // recipient number
try {
$twilio = new Twilio\Rest\Client($sid, $token); // Had to change this to make it work without the USE above
// $message = $twilio->messages->create($dest, // to
// [
// "body" => "Testing 123",
// "from" => "+19998887777"
// ]
// );
//
// echo "Results: ".$message->sid;
}
catch (Exception $e) {
echo $e->getCode()." : ".$e->getMessage();
}
When I run this without the $twilio->messages->create() code, I get nothing... no errors, no responses whatsoever. If I uncomment the create() section where I try and send a message, I get the following:
20003 : [HTTP 401] Unable to create record: Authentication Error - invalid username
Which makes sense. But it is forcing me to catch the authentication error when sending a message and not before.
Maybe I am missing something, but is there some way to check for authentication errors, or get back some sort of status code letting me know a client's authentication was successful or not?
Also, if you have a link to the documentation that covers error handling for Client() calls, I would greatly appreciate it.

Detect Disposable Email with Garbage Domain

I am developing website using php/codeigniter.
I have downloaded a list of temporary email domains from github (https://gist.github.com/adamloving/4401361)
I integrated this to my website to filter and validate email address.But I noticed that some domains are garbage and cannot detect by the list provided.
Please image below.
Currently Im using this code to filter/validate emails:
public function is_temp_mail($mail='')
{
$this->db->select('domain');
$this->db->from('table_disposal_email_domains');
$domains=$this->db->get()->result();
foreach($domains as $domain)
{
list(,$mail_domain) = explode('#',$mail);
if(strcasecmp($mail_domain, $domain->domain) == 0){
return true;
}
}
return false;
}
How to block garbage domains.Please help.
One of the issue with disposable emails is that new domains are added daily. So, maintaining your own list isn't gonna be enough after a few days.
You can use the validator.pizza API, which is free and updated frequently.
Disclaimer: I made this API 😊
I wrote a simple API for determining the domains of temporary mails, all you need to determine the temporary mail is to send a GET request:
https://api.testmail.top/domain/check/data=example#mail.com&ip=8.8.8.8
with authorization header:
Authorization: Bearer XXXXXXXXXX.XXXXXXXXXX.XXXXXXXXXX
and in response you will receive a message like this if the mail turns out to be temporary:
{
"error": 0,
"result": false,
"message": "This domain is in Blacklist"
}
you will receive such an answer if the mail turns out to be trusted (something like gmail.com or yahoo.com):
{
"error": 0,
"result": true,
"message": "This domain is in Whitelist"
}
I have described error codes and more detailed instructions on this page
It would be good if you use a third party package to help you on blocking temporary email domains. You can use MailboxValidator API, which had 300 free API credits per month. You can use the free API key with MailboxValidator CodeIgniter Email Validation Package after sign up.
Disclaimer: I am working at MailboxValidator.

Zend Framework hostname Email Validator fails on yahoo.gr

I am using zend framework to send emails. I have an Hostname::ALLOW_DNS validator. It fails when trying to send email to yahoo.gr. I get this error:
An error occurred
An error occurred during execution; please try again later.
Additional information:
Zend\Mail\Exception\InvalidArgumentException
File:
/var/www/file/project/vendor/zendframework/zendframework/library/Zend/Mail/Address.php:41
Message:
'yahoo.gr ' is not a valid hostname for the email address
The email is smth#yahoo.gr. Any suggestions?
From your error, the only problem I can see is the whitespace 'yahoo.gr ' which will/can cause failure of validation.
Fix Suggestion 1:
You should start using trim() on your GET/POST "email" value.
Fix Suggestion 2:
Which is by the way in the documentation:
Validating only the local part
If you need Zend\Validator\EmailAddress to check only the local part of an email address, and want to disable validation of the hostname, you can set the domain option to FALSE. This forces Zend\Validator\EmailAddress not to validate the hostname part of the email address.
$validator = new Zend\Validator\EmailAddress();
$validator->setOptions(array('domain' => FALSE));
Fix Suggestion 3:
Which is by the way in the documentation as well:
Validating different types of hostnames
The hostname part of an email address is validated against Zend\Validator\Hostname. By default only DNS hostnames of the form domain.com are accepted, though if you wish you can accept IP addresses and Local hostnames too.
To do this you need to instantiate Zend\Validator\EmailAddress passing a parameter to indicate the type of hostnames you want to accept. More details are included in Zend\Validator\Hostname, though an example of how to accept both DNS and Local hostnames appears below:
$validator = new Zend\Validator\EmailAddress(
Zend\Validator\Hostname::ALLOW_DNS |
Zend\Validator\Hostname::ALLOW_LOCAL);
if ($validator->isValid($email)) {
// email appears to be valid
} else {
// email is invalid; print the reasons
foreach ($validator->getMessages() as $message) {
echo "$message\n";
}
}
Well, no more suggestions. I wish you good luck!
The End.

Getting Plesk Mailbox information with PHP?

I have set some mail quota's on email addresses that I manage through Plesk.
However, users are complaining that they are not being informed when their inbox is full or nearly full.
So my idea was to send an email to them when their inbox is about 90% full, so I was wondering if I can retrieve mail account information using PHP?
If not, is there another way of doing this? (I'm not too familiar with console commands)
In this answer I skip the Plesk API altogether, and I assume you either store the properties of the mailboxes in a database, hardcode it, or actually use the Plesk API to retrieve it.
Here is how I retrieve the space used by a mailbox:
function getSpaceUsedByMailBox($username,$password)
{
// open mailbox
$mailBox = imap_open('{localhost:110/pop3/novalidate-cert}INBOX',$username,$password);
// test if successful
$errors = imap_errors();
if ($errors === FALSE)
{
// get info
$info = imap_mailboxmsginfo($mailBox);
// give feedback
echo "Mailbox of $username contains ".$info->Nmsgs.
' messages and is '.$info->Size.' bytes big.';
// flush notices
imap_errors();
imap_alerts();
// close mailbox
imap_close($mailBox);
// return info
return $info;
}
// change this to proper error handling
echo 'ERROR: '.print_r($errors);
// return nothing
return NULL;
}
This is just to give you an idea. You have to adapt it to your coding style.

PHP Azure SDK Service Bus returns Malformed Response

I'm working on trace logger of sorts that pushes log message requests onto a Queue on a Service Bus, to later be picked off by a worker role which would insert them into the table store. While running on my machine, this works just fine (since I'm the only one using it), but once I put it up on a server to test, it produced the following error:
HTTP_Request2_MessageException: Malformed response: in D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php on line 1013
0 HTTP_Request2_Response->__construct('', true, Object(Net_URL2)) D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php:1013
1 HTTP_Request2_Adapter_Socket->readResponse() D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php:139
2 HTTP_Request2_Adapter_Socket->sendRequest(Object(HTTP_Request2)) D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2.php:939
3 HTTP_Request2->send() D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\Http\HttpClient.php:262
4 WindowsAzure\Common\Internal\Http\HttpClient->send(Array, Object(WindowsAzure\Common\Internal\Http\Url)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\RestProxy.php:141
5 WindowsAzure\Common\Internal\RestProxy->sendContext(Object(WindowsAzure\Common\Internal\Http\HttpCallContext)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\ServiceRestProxy.php:86
6 WindowsAzure\Common\Internal\ServiceRestProxy->sendContext(Object(WindowsAzure\Common\Internal\Http\HttpCallContext)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\ServiceBus\ServiceBusRestProxy.php:139
7 WindowsAzure\ServiceBus\ServiceBusRestProxy->sendMessage('<queuename>/mes…', Object(WindowsAzure\ServiceBus\Models\BrokeredMessage)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\ServiceBus\ServiceBusRestProxy.php:155
â‹®
I've seen previous posts that describe similar issues; Namely:
Windows Azure PHP Queue REST Proxy Limit (Stack Overflow)
Operations on HTTPS do not work correctly (GitHub)
That imply that this is a known issue regarding the PHP Azure Storage libraries, where there are a limited amount of HTTPS connections allowed. Before requirements were changed, I was accessing the table store directly, and ran into this same issue, and fixed it in the way the first link describes.
The problem is that the Service Bus endpoint in the connection string, unlike Table Store (etc.) connection string endpoints, MUST be 'HTTPS'. Trying to use it with 'HTTP' will return a 400 - Bad Request error.
I was wondering if anyone had any ideas on a potential workaround. Any advice would be greatly appreciated.
Thanks!
EDIT (After Gary Liu's Comment):
Here's the code I use to add items to the queue:
private function logToAzureSB($source, $msg, $severity, $machine)
{
// Gather all relevant information
$msgInfo = array(
"Severity" => $severity,
"Message" => $msg,
"Machine" => $machine,
"Source" => $source
);
// Encode it to a JSON string, and add it to a Brokered message.
$encoded = json_encode($msgInfo);
$message = new BrokeredMessage($encoded);
$message->setContentType("application/json");
// Attempt to push the message onto the Queue
try
{
$this->sbRestProxy->sendQueueMessage($this->azureQueueName, $message);
}
catch(ServiceException $e)
{
throw new \DatabaseException($e->getMessage, $e->getCode, $e->getPrevious);
}
}
Here, $this->sbRestProxy is a Service Bus REST Proxy, set up when the logging class initializes.
On the recieving end of things, here's the code on the Worker role side of this:
public override void Run()
{
// Initiates the message pump and callback is invoked for each message that is received, calling close on the client will stop the pump.
Client.OnMessage((receivedMessage) =>
{
try
{
// Pull the Message from the recieved object.
Stream stream = receivedMessage.GetBody<Stream>();
StreamReader reader = new StreamReader(stream);
string message = reader.ReadToEnd();
LoggingMessage mMsg = JsonConvert.DeserializeObject<LoggingMessage>(message);
// Create an entry with the information given.
LogEntry entry = new LogEntry(mMsg);
// Set the Logger to the appropriate table store, and insert the entry into the table.
Logger.InsertIntoLog(entry, mMsg.Service);
}
catch
{
// Handle any message processing specific exceptions here
}
});
CompletedEvent.WaitOne();
}
Where Logging Message is a simple object that basically contains the same fields as the Message Logged in PHP (Used for JSON Deserialization), LogEntry is a TableEntity which contains these fields as well, and Logger is an instance of a Table Store Logger, set up during the worker role's OnStart method.
This was a known issue with the Windows Azure PHP, which hasn't been looked at in a long time, nor has it been fixed. In the time between when I posted this and now, We ended up writing a separate API web service for logging, and had our PHP Code send JSON strings to it over cURL, which works well enough as a temporary work around. We're moving off of PHP now, so this wont be an issue for much longer anyways.

Categories