Bounce Code required

Swift Mailer is a fantastic library for sending email with php. Discuss this library or ask any questions about it here.

Moderators: Chris Corbyn, General Moderators

balim
Forum Newbie
Posts: 9
Joined: Mon Mar 19, 2007 8:40 am
Location: NRW, Germany

Bounce Code required

Post by balim »

Hi

I send a newsletter to 10k+ recipients.
To handle this, I wrote a function that uses the socket php functions. So I put Line by Line to the other Mailserver:

Code: Select all

fputs($connection, "HELO $mydomain\r\n");
I've a cron script that calls the function in a loop.

Now my Problem:
The transmission is very slow, so I could be >24h to send out all newsletter.
I've look around and found the swift mailer.
This looks pretty nice, so I lovely want to use it for my application.

But is it possible to return the bounce-code (e.g. 540) if a failure occurs?
At the moment, I do it so:

Code: Select all

fputs($connection, "HELO $mydomain\r\n");
$res=fgets($connection,1024);
if(substr($res,0,3) != "250"){
    $ret = substr($res,0,3);
    fputs($connection, "QUIT\r\n");
    fclose ($connection);
    return $ret;
}
I important for my statistic modul to get these code. I've to find out if it is a hard or soft bounce, to set the recipients to inactive if so.


I hope you understand what I mean :D

Greetings and thanks,
Thomas
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

You'd have to write a plugin. Fairly simple really, but the plugin API differs slightly between PHP4 and 5.

PHP5:

Code: Select all

class ResponseCodeDumper implements Swift_Events_ResponseListener
{
    public function responseReceived(Swift_Events_ResponseEvent $e)
    {
        echo $e->getCode(); //The response code
    }
}
PHP4:

Code: Select all

class ResponseCodeDumper extends Swift_Events_Listener
{
    function responseReceived(&$e)
    {
        echo $e->getCode();
    }
}
Fiddle with that to wrap it up however you want :)

Use like so:

Code: Select all

$swift = new Swift( ... );
$swift->attachPlugin(new ResponseCodeDumper());
You might want to read "Writing Plugins" from here: http://www.swiftmailer.org/wikidocs/
balim
Forum Newbie
Posts: 9
Joined: Mon Mar 19, 2007 8:40 am
Location: NRW, Germany

Post by balim »

Thanks for the fast answer.

I'm going to read a little bit in the wiki :D
balim
Forum Newbie
Posts: 9
Joined: Mon Mar 19, 2007 8:40 am
Location: NRW, Germany

Post by balim »

After my holiday I could continuing on the project. :)

I had included your response plugin, but if I send a mail to a non existing email address, I get the response codes: 250,250,354,250 (which means, everything is ok). That's not correct!
If the mail address doesn't exists, the mailserver should send a failure code (511) back. I've tested it with big mail provider (e.g. google, gmx) and with non existing domains (foo@thisdomaindoesntexists.com).

Is there a failue at the response plugin or in the way I use it?
I hope you could help me with my problem.

Code (php4):

Code: Select all

...

include CONF_INCDIR."swiftmailer/Swift.php";
require_once CONF_INCDIR."swiftmailer/Swift/Connection/SMTP.php";

class ResponseCodeDumper extends Swift_Events_Listener
{
    function responseReceived(&$e)
    {
        if (substr($e->getCode(), 0, 1) != 2 && $e->getCode() != 354) {
            echo "error: ".$e->getCode()."<br>";
        } else {
            echo "code: ".$e->getCode()."<br>";
        }
    }
}

$conn =& new Swift_Connection_SMTP("my_mailserver", 25);
$swift =& new Swift($conn);
$swift->attachPlugin( new ResponseCodeDumper(), "bounce-response");

$query = "SELECT * FROM newsletter_query WHERE mailflag = '' LIMIT 0,100"; // Read the personalized newsletter from Database
$db->query($query);
while ($db->next_record()){
    $message =& new Swift_Message($db->f("betreff"));
    $newsletter_uid = $db->f("newsletter_uid");
    if ("" != $db->f("htmltext")) {
        $message->attach(new Swift_Message_Part(stripslashes($db->f("htmltext")), "text/html"));
    } else {
    }
    $message->attach(new Swift_Message_Part(stripslashes($db->f("plaintext"))));

    $to =& new Swift_Address("asdf@nonexistingdomain.com", "test");
    $from =& new Swift_Address("foo@bar.com", "test");
    var_dump($swift->Send($message, $to, $from));
}

...
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

That's not Swift's fault if the mail server says 250. The mail server you send through cannot check the address other than the domain, and some do not even check that. If the server is authorative for that domain (i.e. it's the master MX) then yes, maybe it would throw a 5XX error but Swift can only tell you what your mail server says.

If you want to debug and see exactly what your mail server is doing then try this:

Code: Select all

$swift = new Swift($domain, null, SWIFT_ENABLE_LOGGING);

//send messages etc etc then at the end:

echo "<pre>" . $swift->log->dump() . "</pre>";
If 250 comes back then your server is doing that, most probably because it's an outgoing relay server with a smarthost (i.e. it's passing the mail to another server to handle the request first).
balim
Forum Newbie
Posts: 9
Joined: Mon Mar 19, 2007 8:40 am
Location: NRW, Germany

Post by balim »

thanks

Sadly, i could send email to every mail address, if exists or not, and get a successful response (250).
But I've an idea what this could be.

In my own class I made a mxlookup for every maildomain an go through every mx record until the mail is sent succesfully or all mx records are failed. There I got some 5xx and 4xx failure codes during the transmission.

With swift I specify only the SMTP Host from the provider and there I get ony a 250 response for a successfully transmission.

If this is the answer, is it possible to setting up swift to check the maildomains?
Or is there another solution to get correct response codes and a fast transmission?

And of course I can't configure the mailserver from my provider. :lol:
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

balim wrote:thanks

Sadly, i could send email to every mail address, if exists or not, and get a successful response (250).
But I've an idea what this could be.

In my own class I made a mxlookup for every maildomain an go through every mx record until the mail is sent succesfully or all mx records are failed. There I got some 5xx and 4xx failure codes during the transmission.

With swift I specify only the SMTP Host from the provider and there I get ony a 250 response for a successfully transmission.

If this is the answer, is it possible to setting up swift to check the maildomains?
Or is there another solution to get correct response codes and a fast transmission?

And of course I can't configure the mailserver from my provider. :lol:
I could write a MX verifier plugin which prevents the recipient being used if no MX record is found. I started toying with this idea a while back but got distracted. This is actually relatively trivial although it will require PHP to not be in safe_mode unless I can find out how to use sockets to query a DNS server (I tried finding the RFCs for this a while back but was overwhelmed by it).
balim
Forum Newbie
Posts: 9
Joined: Mon Mar 19, 2007 8:40 am
Location: NRW, Germany

Post by balim »

To query a DNS server, I might help you.

Here (http://www.php.net/getmxrr) in the user comments, a user postet a mxlookup class which I'm using.

Code: Select all

<?php

class mxlookup
{
      var $dns_socket = NULL;
      var $QNAME = "";
      var $dns_packet= NULL;
      var $ANCOUNT = 0;
      var $cIx = 0;
      var $dns_repl_domain;
      var $arrMX = array();

      function mxlookup($domain, $dns="192.168.2.1")
      {
         $this->QNAME($domain);
         $this->pack_dns_packet();
         $dns_socket = fsockopen("udp://$dns", 53);

         fwrite($dns_socket,$this->dns_packet,strlen($this->dns_packet));
         $this->dns_reply  = fread($dns_socket,1);
         $bytes = stream_get_meta_data($dns_socket);
         $this->dns_reply .= fread($dns_socket,$bytes['unread_bytes']);
         fclose($dns_socket);
         $this->cIx=6;
         $this->ANCOUNT   = $this->gord(2);
         $this->cIx+=4;
         $this->parse_data($this->dns_repl_domain);
         $this->cIx+=7;

         for($ic=1;$ic<=$this->ANCOUNT;$ic++)
         {
           $QTYPE = ord($this->gdi($this->cIx));
           if($QTYPE!==15){print("[MX Record not returned]"); die();}
           $this->cIx+=9;
           $mxPref = ord($this->gdi($this->cIx));
           $this->parse_data($curmx);
           $this->arrMX[] = array("MX_Pref" => $mxPref, "MX" => $curmx);
           $this->cIx+=3;
         }
      }

      function parse_data(&$retval)
      {
        $arName = array();
        $byte = ord($this->gdi($this->cIx));
        while($byte!==0)
        {
          if($byte==192) //compressed
          {
            $tmpIx = $this->cIx;
            $this->cIx = ord($this->gdi($cIx));
            $tmpName = $retval;
            $this->parse_data($tmpName);
            $retval=$retval.".".$tmpName;
            $this->cIx = $tmpIx+1;
            return;
          }
          $retval="";
          $bCount = $byte;
          for($b=0;$b<$bCount;$b++)
          {
            $retval .= $this->gdi($this->cIx);
          }
          $arName[]=$retval;
         $byte = ord($this->gdi($this->cIx));
       }
       $retval=join(".",$arName);
     }

     function gdi(&$cIx,$bytes=1)
     {
       $this->cIx++;
       return(substr($this->dns_reply, $this->cIx-1, $bytes));
     }

      function QNAME($domain)
      {
        $dot_pos = 0; $temp = "";
        while($dot_pos=strpos($domain,"."))
        {
          $temp   = substr($domain,0,$dot_pos);
          $domain = substr($domain,$dot_pos+1);
          $this->QNAME .= chr(strlen($temp)).$temp;
        }
        $this->QNAME .= chr(strlen($domain)).$domain.chr(0);
      }

      function gord($ln=1)
      {
        $reply="";
        for($i=0;$i<$ln;$i++){
         $reply.=ord(substr($this->dns_reply,$this->cIx,1));
         $this->cIx++;
         }

        return $reply;
      }

      function pack_dns_packet()
      {
        $this->dns_packet = chr(0).chr(1).
                            chr(1).chr(0).
                            chr(0).chr(1).
                            chr(0).chr(0).
                            chr(0).chr(0).
                            chr(0).chr(0).
                            $this->QNAME.
                            chr(0).chr(15).
                            chr(0).chr(1);
      }

}

?>

Code: Select all

<?php

/* Exampe of use: */
$mx = new mxlookup("php.net");

print $mx->ANCOUNT." MX Records\n";
print "Records returned for ".$mx->dns_repl_domain.":\n<pre>";
print_r($mx->arrMX);

?>

Code: Select all

Return:

02 MX Records Records returned for php.net:

Array
(
    [0] => Array
        (
            [MX_Pref] => 15
            [MX] => smtp.osuosl.org
        )

    [1] => Array
        (
            [MX_Pref] => 5
            [MX] => osu1.php.net
        )

)
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Thank you :) I'll disect the hell out of that until I understand it. I knew it wasn't as simple as issuing plain-text commands.

I should be able to make a plugin anyway :)
ollip1
Forum Newbie
Posts: 6
Joined: Sun Oct 08, 2006 3:11 am

return-path

Post by ollip1 »

Hi!

I use return-path like this

Code: Select all

$message = new Swift_Message($subject);
$message->setReturnPath($brokenaddr);
This makes the following to the header of the email

Code: Select all

Return-path: <brokenemail.address@domain.com>
I read brokenemail.address and see to whom it was sent and tag that address broken. Maybe little bit complicated but anyhow all broken emails are caught. I quess checking the address against before sending gives easier access to addresses that are not there.

Only problem at the moment is that I cannot deliver to all valid addresses. E.g. to my own gmail address may give once a while error:

Code: Select all

unrouteable mail domain "gmail.com"
I think the problem lies in the mail server, not in swift in my case.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Messages like that come back from the SMTP server rather than Swift yes. In fact most of the error messages contain some text from the server to make it easier to identify issues :)

Checking domains is not enough to verify an email will be delivered. The user may not exist so you still need a bounce-detect address as ollip1 says :)
balim
Forum Newbie
Posts: 9
Joined: Mon Mar 19, 2007 8:40 am
Location: NRW, Germany

Post by balim »

Hmm
At the moment I handle this without a bounce-detect address and it works (slow, but it works :wink: ).
The only problem is the speed :)

I check the domain for MX records, connect to an existing record and start the transfer.
There I get all failure codes. Also the "user doesn't exists" code.
Is there no chance to speed up this process with swift?

If no, I have to roll up my code and create such an address. *sigh*
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

balim wrote:Hmm
I check the domain for MX records, connect to an existing record and start the transfer.
There I get all failure codes. Also the "user doesn't exists" code.
Is there no chance to speed up this process with swift?
You could write custom smtp transport for swift, say Swift_SMTP_Direct, which would do what you described on its own.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Weirdan wrote:
balim wrote:Hmm
I check the domain for MX records, connect to an existing record and start the transfer.
There I get all failure codes. Also the "user doesn't exists" code.
Is there no chance to speed up this process with swift?
You could write custom smtp transport for swift, say Swift_SMTP_Direct, which would do what you described on its own.
This is true. The plugin system isn't the only way to extend Swift. Pretty much everything is abstract. The connections can be anything provided they return SMTP responses. If you snoop around the source of any of the ones other than sendmail or smtp you'll get some inspiration. I actually considered writing a connector which delivers mail direct to the MX of the address. This is almost verging on becoming a MTA in itself.

Speeding up the time it takes to get a bounce response is not really possible. Not reliably anyway; a lot of server will say goody goody to your request then send you a bounce notification later.
balim
Forum Newbie
Posts: 9
Joined: Mon Mar 19, 2007 8:40 am
Location: NRW, Germany

Post by balim »

d11wtq wrote:Not reliably anyway; a lot of server will say goody goody to your request then send you a bounce notification later.
Oh, that's good information. I don't know this until now. Then a bouce mailbox is the only way to get an accurate result, isn't it?
Post Reply