Friday, March 6, 2020

Verify Email Deliverability in PHP

Assume you have a list of subscribers you send news letters to every week. But what if half of the subscribers have changed their email accounts. In this post I am going to demonstrate how to verify if an email is fake or real.

Verify Email Deliverability in PHP
Successful email delivery is important to keep a good reputation and bounce rate in control. It ensures every email is delivered to a real user. We are going to take an email retrieve the hostname from email. Next we search for MX Records against that hostname. If we find MX Records we loop through every record and send commands to SMTP server.

  1. HELO: This is the first command which tells the SMTP server a new conversation is about to start.
  2. MAIL FROM: We tell SMTP server the senders email, the source email address which is going to start conversation.
  3. RCPT TO: This command tells the SMPT server the target or receiver email address of conversation.
  4. QUIT: Tells the SMTP server to close conversation.

Lets get started we create index.html file which contains a form to receive email we then send an ajax request to validate-email.php which will open a connection to SMTP server, validate the email and send back a json response.

index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Check Email Deliverability in PHP Demo</title>
        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
        <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
        <script type="text/javascript" src="js/javascript.js"></script>
        <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
        <link rel="stylesheet" href="css/style.css" />
    </head>
    <body>
        <div class="main-container">
            <div class="section">
                <label>Enter an email to validate</label>
                <form class="email-form">
                    <div class="flex">
                        <div class="loader">
                            <i class="fa fa-spinner fa-spin"></i>
                        </div>
                        <input type="email" class="form-control email-input" value="" required="required" placeholder="Enter an email to validate" />
                        <button type="submit">Validate</button>
                    </div>
                </form>
                <div class="content-wrapper" id="content-wrapper"></div>
            </div>
        </div>
    </body>
</html>

validate-email.php

<?php
//=== Initial response as a fallback
$res["success"] = 0;
$res["status"] = "UNKNOWN_ERROR";

if($_POST){
    //=== Check if email was submitted
    if( !isset($_POST["email"]) ){
        $res["status"] = "MISSING_EMAIL";
    }

    $email = $_POST["email"];

    //=== Check if email format
    $valid_format = filter_var($email, FILTER_VALIDATE_EMAIL);

    //=== If email format is correct proceed to validation check
    if($valid_format){

        //=== Get hostname from email
        $hostname = substr($email, strpos($email, "@") + 1);
        $mxhosts = $mxweights = [];

        //=== Get MX-Records of hostname
        $mxr = getmxrr($hostname, $mxhosts, $mxweights);
        $mxr_list = array_combine($mxweights, $mxhosts);

        //=== If MX-Records were found proceed to socket connection
        if( !empty($mxr_list) ){
            //=== Sort MX-Records by priority
            krsort($mxr_list, SORT_NUMERIC);

            $res["status"] = "INVALID_EMAIL";

            //=== Loop through all MX-Records
            foreach($mxr_list as $mxweight => $mxhost){
                $connection = @fsockopen($mxhost, 25, $errno, $errstr, 5);

                //=== If connection was successful send commands to email server
                if($connection){

                    //=== Send Helo command to initiate conversation
                    fwrite($connection, "HELO $mxhost\r\n");
                    $response = fgets($connection, 1024) . "\r\n";

                    //=== Send Mail From command to specify senders email
                    fwrite($connection, "MAIL FROM: <[email protected]> \r\n");
                    $response = fgets($connection, 1024) . "\r\n";

                    //=== Send RCPT TO command to specify receiver's email on server
                    fwrite($connection, "RCPT TO: <$email> \r\n");
                    $response = fgets($connection, 1024) . "\r\n";

                    //=== Send QUIT command to close conversation
                    fwrite($connection, "QUIT \r\n");
                    $response = fgets($connection, 1024);

                    fclose($connection);

                    //=== If last reponse returned 250 its a valid email
                    if( substr($response, 0, 3) == 250 ){
                        $res["success"] = 1;
                        $res["status"] = "EMAIL_VALID";
                        $res["mx_hosts"] = $mxr_list;

                        break;
                    }
                }
            }
        }else{
            //=== No MX-Records found
            $res["status"] = "EMAIL_SERVER_NOT_FOUND";
        }
    }else{
        //=== Invalid email format
        $res["status"] = "INVALID_FORMAT";
    }
}
echo json_encode($res, JSON_PRETTY_PRINT);
?>

javascript.js

$(document).ready(function(){
    $(".email-form").on("submit",function(e){
        e.preventDefault();
        var email = $(".email-input").val();
        if(email != ""){
            $(".loader").show();
            $.ajax({
                url: "validate-email.php",
                type: "POST",
                data:{
                   email: email,
                },
                success: function(res){
                   $(".loader").hide();
                   $(".content-wrapper").html( '<pre>' + res + '</pre>' );
                }
            });
        }
    });
});

style.css

*{
    box-sizing: border-box;
}
html,body{
    margin: 0px;
    padding: 0px;
}
body{
    background: #f0f0f0;
    font: normal normal 14px Open Sans,Verdana, Arial;
}
.main-container{
    max-width: 1024px;
    margin: 0px auto;
}
.flex{
    display: flex;
    position: relative;
}
.loader{
    position: absolute;
    font-size: 25px;
    background: rgba(150,150,150,0.5);
    width: 100%;
    height: 100%;
    z-index: 5;
    padding: 0px 10px;
    display: none;
    color: #006699;
    text-align: center;
}
input.form-control{
    border: 1px solid #ddd;
    padding: 10px;
    color: #444;
    font-size: 15px;
    width: 100%;
}
.email-form button {
    background: #3c8dbc;
    color: #fff;
    border: none;
    padding: 11px 15px;
    cursor: pointer;
}
.content-wrapper pre{
    background-color: #fff;
    border: 1px solid #ddd;
    padding: 15px;
}