Fix #7796 - media outreach crm - needs distributed email sending (due to spam blocks)
authorAlan <alan@roojs.com>
Wed, 11 Oct 2023 02:33:14 +0000 (10:33 +0800)
committerAlan <alan@roojs.com>
Wed, 11 Oct 2023 02:33:14 +0000 (10:33 +0800)
DataObjects/Core_domain.php
DataObjects/Core_notify.php
DataObjects/Core_notify_blacklist.php
DataObjects/Core_notify_server.php
DataObjects/pman.links.ini
Notify.php
NotifySend.php

index 89b4587..8c6bc83 100644 (file)
@@ -15,5 +15,22 @@ class Pman_Core_DataObjects_Core_domain extends DB_DataObject
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
-    
+    function loadOrCreate($dom)
+    {
+        // should we validate domain?
+        static $cache = array();
+        if (isset($cache[$dom])) {
+            return $cache[$dom];
+        }
+        
+        $cd = DB_DataObject::Factory($dom);
+        if ($cd->get('domain', $dom)) {
+            $cache[$dom] = $cd;
+            return $cd;
+        }
+        $cd->domain = $dom;
+        $cd->insert();
+        $cache[$dom] = $cd;
+        return $cd;
+    }
 }
index 8c0e628..0998632 100644 (file)
@@ -344,5 +344,24 @@ class Pman_Core_DataObjects_Core_notify extends DB_DataObject
         
         return true;
     }
+    // after called do not rely on content as it includes NOW()
+    function flagDone($event,$msgid)
+    {
+        $ww = clone($this);
+        if(strtotime($this->act_when) > strtotime("NOW")){
+            $this->act_when = $this->sqlValue('NOW()');
+        }
+        $this->sent = $this->sent == '0000-00-00 00:00:00' ? $this->sqlValue('NOW()') :$this->sent; // do not update if sent.....
+        $this->msgid = '';
+        $this->event_id = $event->id;
+        $this->update($ww);
+    }
+    
+    function flagLater($when)
+    {
+        $ww = clone($this);
+        $this->act_when = $when;
+        $this->update($ww);
+    }
     
 }
index 6aa5b8e..408f542 100644 (file)
@@ -17,6 +17,32 @@ class Pman_Core_DataObjects_Core_notify_blacklist extends DB_DataObject
     public $added_dt;
     
     
+    function messageIsBlacklisted($err)
+    {
+        $match = array(
+            '5.7.0 DT:SPM'. // 163.com
+            'on our block list ',  // live.com
+            'spameatingmonkey.net', // spameatingmonkey.net (users)
+            'sender is listed on the block', // korian?
+            'proofpoint.com', // another spam detecotr
+            'cloud-security.net', // another spam protector..
+        
+        );
+        foreach($match as $str) {
+            if (strpos($err, $str) !== false) {
+                return true;
+            }
+        }
+        return false;
+    }
     
+    // delete blacklists older than 1 week (and try again)
+    function prune()
+    {
+        $this->query("
+            DELETE FROM {$this->tableName()} where added_dt < NOW()  - INTERVAL 1 WEEK
+        ");
+            
+    }
     
 }
\ No newline at end of file
index f678e6c..0e95ea1 100644 (file)
@@ -17,7 +17,226 @@ class Pman_Core_DataObjects_Core_notify_server extends DB_DataObject
     public $is_active;
     public $last_send;
     
+    // most services should call this first..
     
+    function getCurrent($notify)
+    {
+        static $current = false;;
+        
+        if ($current !== false) {
+            return $current;
+        }
+        
+        $ns = DB_DataObject::factory('core_notify_server');
+        $ns->poolname = $notify->poolname;
+        $ns->is_active = 1;
+        $ns->hostname = gethostname();
+        if (!$ns->count()) {
+            $notify->jerr("Server not found for this server " .  gethostname() . " in core_notify_server" );
+        }
+        $ns->find(true);
+        $current = $ns;
+        return $ns;
+    }
     
     
+    function isFirstServer()
+    {
+        $servers = $this->availableServers();
+        if (empty($servers)) {
+            return false;
+        }
+        // only run this on the first server...
+        return $this->id == $servers[0]->id;
+    }
+    
+    
+    // called on current server.
+    function assignQueues($notify)
+    {
+         
+        
+        $servers = $this->availableServers();
+        $ids = array();
+        foreach($servers as $s) {
+            $ids[] = $s->id;
+        }
+        
+        
+        if (empty($ids)) {
+            $notify->jerr("no configured servers in core_notify_server for poolname = {$notify->poolname}");
+            
+        }
+         
+        // only run this on the first server...
+        if ($this->id != $ids[0]) {
+            return; 
+        }
+        foreach($ids as $rn) {
+            $up[$rn]  = array();
+        }
+        
+        $num_servers = count($ids);
+        
+        if ($num_servers == 1) {
+            $p = DB_DataObject::factory($notify->table);
+            $p->query("
+                UPDATE
+                    {$notify->table}
+                SET
+                    server_id = {$ids[0]}
+                WHERE
+                    sent < '2000-01-01'
+                    and
+                    event_id = 0
+                    and
+                    act_start < NOW() +  INTERVAL 3 HOUR 
+                    and
+                    server_id != {$ids[0]}
+            ");
+            return;
+        }
+        
+        // ((@row_number := CASE WHEN @row_number IS NULL THEN 0 ELSE @row_number END  +1) % {$num_servers})
+        
+        
+        
+        $p = DB_DataObject::factory($notify->table);
+        $p->whereAdd("
+                sent < '2000-01-01'
+                and
+                event_id = 0
+                and
+                act_start < NOW() +  INTERVAL 3 HOUR 
+                and
+                server_id NOT IN (" . implode(",", $ids) . ")
+        ");
+        if ($p->count() < 1) {
+            return;
+        }
+        
+        $p->selectAdd();
+        $p->selectAdd("id, ((@row_number := CASE WHEN @row_number IS NULL THEN 0 ELSE @row_number END  +1) % {$num_servers})  as rn");
+        $kv = $p->fetchAll('id,rn');
+        foreach($kv as $id => $r) {
+            $up[ $ids[$r] ][] = $id;
+        }
+        foreach($up as $sid => $nids) {
+            $p = DB_DataObject::factory($notify->table);
+            $p->query("
+                UPDATE
+                    {$this->table}
+                SET
+                    server_id = $sid
+                WHERE
+                    id IN (". implode(",', $nids"). ')'
+            );
+        }
+         
+        DB_DataObject::factory("core_notify_blacklist")->prune();
+        
+    }
+        // called on current server.
+
+    function availableServers()
+    {
+        $ns = DB_DataObject::factory('core_notify_server');
+        $ns->poolname = $this->poolname;
+        $ns->is_active = 1;
+        $ns->orderBy('id ASC');
+        return  $ns->fetchAll();
+        
+    }
+    
+    function updateNotifyToNextServer( $cn , $when = false, $allow_same = false)
+    {
+        // fixme - this should take into account blacklisted - and return false if no more servers are available
+        $email = empty($cn->to_email) ? ($cn->person() ? $cn->person()->email : $cn->to_email) : $cn->to_email;
+
+        $w = DB_DataObject::factory($cn->tableName());
+        $w->get($cn->id);
+        
+        $servers = $this->availableServerIds();
+        $start = 0;
+        foreach($servers as $i => $s) {
+            if ($s->id == $this->id) {
+                $start = $i;
+            }
+        }
+        
+        $offset = ($start + 1)  % count($ids);
+        $good = false;
+        while ($offset  != $start) {
+            $s = $servers[$offset];
+            if (!$s->isBlacklisted($email)) {
+                $good = $s;
+                break;
+            }
+        }
+        if ($good == false && $allow_same) {
+            $good = $this;
+        }
+        
+        if ($good == false) {
+            return false;
+        }
+        
+        
+        // next server..
+        $pp = clone($w);
+        $w->server_id = $good->id;   
+        $w->act_when = $when === false ? $w->sqlValue('NOW() + INTERVAL 1 MINUTE') : $when;
+        $w->update($pp);
+        return true;
+    }
+    
+    
+    function isBlacklisted($email)
+    {
+        // return current server id..
+        static $cache = array();
+        
+        // get the domain..
+        $ea = explode('@',$email);
+        $dom = strtolower(array_pop($ea));
+        if (isset($cache[$dom])) {
+            return $cache[$dom];
+        }
+        
+        $cd = DB_DataObject::factory('core_domain')->loadOrCreate($dom);
+        
+        $bl = DB_DataObject::factory('core_notify_blacklist');
+        $bl->server_id = $this->id;
+        $bl->domain_id = $cd->id;
+        if ($bl->count()) {
+            $cache[$dom] = true;
+            return true;
+        }
+        
+        return ralse; 
+    }
+    function initHelo()
+    {
+        
+        $ff->Mail['helo'] = $this->helo;
+        
+    }
+    function checkSmtpResponse($errmsg, $core_domain)
+    {
+        $bl = DB_DataObject::factory('core_notify_blacklist');
+        $bl->server_id = $this->id;
+        $bl->domain_id = $core_domain->id;
+        if ($bl->count()) {
+            return;
+        }
+        // is it a blacklist message
+        if (!$bl->messageIsBlacklisted($errmsg)) {
+            return;
+        }
+        $bl->added_dt = $bl->sqlValue("NOW()");
+        $bl->insert();
+        
+        
+    }
+    
 }
\ No newline at end of file
index ec531e1..d2e550d 100644 (file)
@@ -65,12 +65,21 @@ watch_id = core_watch:id
 trigger_person_id = core_person:id
 trigger_event_id = Events:id
 domain_id = core_domain:id
+server_id = core_notify_server:id
+
 
 [core_notify_recur]
 person_id = core_person:id
 last_event_id = Events:id
 method_id = core_enum:id
 
+
+[core_notify_blacklist]
+server_id = core_notify_server:id
+domain_id = core_domain:id
+
+
+
 [core_email]
 owner_id = core_person:id
 bcc_group_id = core_group:id
index 0e2d6a2..705cb6b 100644 (file)
@@ -124,11 +124,15 @@ class Pman_Core_Notify extends Pman
     var $evtype = ''; // any notification...
                     // this script should only handle EMAIL notifications..
     
-    var $server_id;
+    var $server;  // core_notify_server
     
+    var $poolname = 'core';
     
     var $opts; 
     var $force = false;
+    
+    var $clear_interval = '1 WEEK'; // how long to clear the old queue of items.
+    
     function getAuth()
     {
         $ff = HTML_FlexyFramework::get();
@@ -181,22 +185,23 @@ class Pman_Core_Notify extends Pman
         
         $this->generateNotifications();
         
-        $this->assignQueues();
-        
-        //DB_DataObject::debugLevel(1);
+         //DB_DataObject::debugLevel(1);
         $w = DB_DataObject::factory($this->table);
         $total = 0;
         
         
         
         $ff = HTML_FlexyFramework::get();
-        if (!empty($ff->Core_Notify['servers']) && empty($ff->Core_Notify['servers-non-pool'][gethostname()])) {
-            
-            if (!isset($ff->Core_Notify['servers'][gethostname()])) {
-                $this->jerr("Core_Notify['servers']['" . gethostname() ."'] is not set");
-            }
-            $w->server_id = array_search(gethostname(),array_keys($ff->Core_Notify['servers']));
-        }
+        
+        
+        $this->server = DB_DataObject::Factory('core_notify_server')->getCurrent($this);
+        
+        $this->server->assignQueues($this);
+        
+        
+        $this->clearOld();
+        
+        
         if (!empty($this->evtype)) {
             $w->evtype = $this->evtype;
         }
@@ -286,10 +291,13 @@ class Pman_Core_Notify extends Pman
             // not sure what happesn if person email and to_email is empty!!?
             $email = empty($p->to_email) ? ($p->person() ? $p->person()->email : $p->to_email) : $p->to_email;
             
-            $black = $this->isBlacklisted($email);
+            $black = $this->server->isBlacklisted($email);
             if ($black !== false) {
-                $this->logecho("DOMAIN blacklisted - {$email} - moving to another pool");
-                $this->updateServer($p, $black);
+                
+                if (false === $this->server->updateNotifyToNextServer($p)) {
+                    $p->updateState("????");
+                }
+                
                 continue;
             }
              
@@ -299,8 +307,7 @@ class Pman_Core_Notify extends Pman
                 // push it to a 'domain specific queue'
                 $this->logecho("REQUEING - maxed out that domain - {$email}");
                 $this->pushQueueDomain($p, $email);
-                  
-                
+                   
                 //sleep(3);
                 continue;
             }
@@ -311,11 +318,13 @@ class Pman_Core_Notify extends Pman
             
             
         }
-         $this->logecho("REQUEUING all emails that maxed out:" . count($this->next_queue));
+        $this->logecho("REQUEUING all emails that maxed out:" . count($this->next_queue));
         if (!empty($this->next_queue)) {
              
             foreach($this->next_queue as $p) {
-                $this->updateServer($p);
+                if (false === $this->server->updateNotifyToNextServer($p)) {
+                    $p->updateState("????");
+                }
             }
         }
         
@@ -335,55 +344,10 @@ class Pman_Core_Notify extends Pman
     }
     
     
-    function isBlacklisted($email)
-    {
-        // return current server id..
-        $ff = HTML_FlexyFramework::get();
-        //$this->logecho("CHECK BLACKLISTED - {$email}");
-        if (empty($ff->Core_Notify['servers'])) {
-            return false;
-        }
-      
-        if (!isset($ff->Core_Notify['servers'][gethostname()]['blacklisted'])) {
-            return false;
-        }
-       
-        // get the domain..
-        $ea = explode('@',$email);
-        $dom = strtolower(array_pop($ea));
-        
-        //$this->logecho("CHECK BLACKLISTED DOM - {$dom}");
-        if (!in_array($dom, $ff->Core_Notify['servers'][gethostname()]['blacklisted'] )) {
-            return false;
-        }
-        //$this->logecho("RETURN BLACKLISTED TRUE");
-        return array_search(gethostname(),array_keys($ff->Core_Notify['servers']));
-    }
+   
     
     // this sequentially distributes requeued emails.. - to other servers. (can exclude current one if we have that flagged.)
-    function updateServer($ww, $exclude = -1)
-    {
-        $w = DB_DataObject::factory($ww->tableName());
-        $w->get($ww->id);
-        
-        $ff = HTML_FlexyFramework::get();
-        static $num = 0;
-        if (empty($ff->Core_Notify['servers'])) {
-            return;
-        }
-        $num = ($num+1) % count(array_keys($ff->Core_Notify['servers']));
-        if ($exclude == $num ) {
-            $num = ($num+1) % count(array_keys($ff->Core_Notify['servers']));
-        }
-        // next server..
-        $pp = clone($w);
-        $w->server_id = $num;
-                    
-        $w->act_when = $w->sqlValue('NOW() + INTERVAL 1 MINUTE');
-        $w->update($pp);
-        
-         
-    }
+     
   
     
     function generateNotifications()
@@ -420,65 +384,7 @@ class Pman_Core_Notify extends Pman
     
     }
     
-    function assignQueues()
-    {
-        $ff = HTML_FlexyFramework::get();
-        
-        if (empty($ff->Core_Notify['servers'])) {
-            return;
-        }
-        
-        if (isset($ff->Core_Notify['servers-non-pool'][gethostname()])) {
-            return; 
-        }
-        
-        if (!isset($ff->Core_Notify['servers'][gethostname()])) {
-            
-            $this->jerr("Core_Notify['servers']['" . gethostname() ."'] is not set");
-        }
-        // only run this on the main server...
-        if (array_search(gethostname(),array_keys($ff->Core_Notify['servers'])) > 0) {
-            return;
-        }
-        
-        $num_servers = count(array_keys($ff->Core_Notify['servers']));
-        $p = DB_DataObject::factory($this->table);
-        $p->whereAdd("
-                sent < '2000-01-01'
-                and
-                event_id = 0
-                and
-                act_start < NOW() +  INTERVAL 3 HOUR 
-                and
-                server_id < 0"
-            
-        );
-        if ($p->count() < 1) {
-            return;
-        }
-         $p = DB_DataObject::factory($this->table);
-        // 6 seconds on this machne...
-        $p->query("
-            UPDATE
-                {$this->table}
-            SET
-                server_id = ((@row_number := CASE WHEN @row_number IS NULL THEN 0 ELSE @row_number END  +1) % {$num_servers})
-            WHERE
-                sent < '2000-01-01'
-                and
-                event_id = 0
-                and
-                act_start < NOW() +  INTERVAL 3 HOUR 
-                and
-                server_id < 0
-            ORDER BY
-                id ASC
-            LIMIT
-                10000
-        ");
-
-        
-    }
+     
     
     function run($id, $email='', $cmdOpts="")
     {
@@ -687,7 +593,41 @@ class Pman_Core_Notify extends Pman
         $this->domain_queue = false;
         return $ret;
     }
-    
+    function clearOld()
+     {
+          if ($this->server->isFirstServer()) {
+            $p = DB_DataObject::factory($this->table);
+            $p->whereAdd("
+                sent < '2000-01-01'
+                and
+                event_id = 0
+                and
+                act_start < NOW() - INTERVAL {$this->clear_interval}
+            ");
+           // $p->limit(1000);
+            if ($p->count()) {
+                $ev = $this->addEvent('NOTIFY', false, "RETRY TIME EXCEEDED");
+                $p = DB_DataObject::factory($this->table);
+                $p->query("
+                    UPDATE
+                        {$this->table}
+                    SET
+                        sent = NOW(),
+                        msgid = '',
+                        event_id = {$ev->id}
+                    WHERE
+                        sent < '2000-01-01'
+                        and
+                        event_id = 0
+                        and
+                        act_start < NOW() - INTERVAL {$this->clear_interval}
+                    LIMIT
+                        1000
+                ");
+                
+            }
+        }
+     }
     
 
     function output()
index 132930b..00e6168 100644 (file)
@@ -71,6 +71,8 @@ class Pman_Core_NotifySend extends Pman
     );
     var $table = 'core_notify';
     var $error_handler = 'die';
+    var $poolname = 'core';
+    var $server; // core_notify_server
     
     function getAuth()
     {
@@ -106,6 +108,10 @@ class Pman_Core_NotifySend extends Pman
              
             $this->errorHandler("already sent - repeat to early\n");
         }
+        
+        $this->server = DB_DataObject::Factory('core_notify_server')->getCurrent($this);
+
+        
         if (!empty($opts['debug'])) {
             print_r($w);
             $ff = HTML_FlexyFramework::get();
@@ -119,7 +125,7 @@ class Pman_Core_NotifySend extends Pman
         
         if (!$force && (!empty($w->msgid) || $sent)) {
             $ww = clone($w);
-            if (!$sent) { 
+            if (!$sent) {   // fix sent.
                 $w->sent = $w->sent == '0000-00-00 00:00:00' ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent.....
                 $w->update($ww);
             }    
@@ -129,17 +135,10 @@ class Pman_Core_NotifySend extends Pman
         $o = $w->object();
         
         if ($o === false)  {
-            
-            $ev = $this->addEvent('NOTIFY', $w,
-                            "Notification event cleared (underlying object does not exist)" );;
-            $ww = clone($w);
-            $w->sent = $w->sent == '0000-00-00 00:00:00' ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s ') . 
-                     "Notification event cleared (underlying object does not exist)" 
-                    ."\n");
+             
+            $ev = $this->addEvent('NOTIFY', $w,   "Notification event cleared (underlying object does not exist)" );
+            $w->flagDone($ev, '');
+            $this->errorHandler(  $ev->remarks);
         }
      
         
@@ -147,32 +146,16 @@ class Pman_Core_NotifySend extends Pman
         $p = $w->person();
         
         if (isset($p->active) && empty($p->active)) {
-            $ev = $this->addEvent('NOTIFY', $w,
-                            "Notification event cleared (not user not active any more)" );;
-            $ww = clone($w);
-            $w->sent = $w->sent == '0000-00-00 00:00:00' ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s ') . 
-                     "Notification event cleared (not user not active any more)" 
-                    ."\n");
-            $this->errorHandler("message has been sent already.\n");
+            $ev = $this->addEvent('NOTIFY', $w, "Notification event cleared (not user not active any more)" );;
+             $w->flagDone($ev, '');
+            $this->errorHandler(  $ev->remarks);
         }
         // has it failed mutliple times..
         
         if (!empty($w->field) && isset($p->{$w->field .'_fails'}) && $p->{$w->field .'_fails'} > 9) {
-            $ev = $this->addEvent('NOTIFY', $w,
-                            "Notification event cleared (user has to many failures)" );;
-            $ww = clone($w);
-            $w->sent = $w->sqlValue('NOW()'); // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s ') . 
-                     "Notification event cleared (user has to many failures)" 
-                    ."\n");
-            $this->errorHandler("user has to many failures.\n");
+            $ev = $this->addEvent('NOTIFY', $w, "Notification event cleared (user has to many failures)" );;
+            $w->flagDone($ev, '');
+            $this->errorHandler(  $ev->remarks);
         }
         
         // let's work out the last notification sent to this user..
@@ -216,17 +199,9 @@ class Pman_Core_NotifySend extends Pman
         $email =  $this->makeEmail($o, $p, $last, $w, $force);
         
         if ($email === true)  {
-            
-            $ev = $this->addEvent('NOTIFY', $w,
-                            "Notification event cleared (not required any more)" );;
-            $ww = clone($w);
-            $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s ') . 
-                     "Notification event cleared (not required any more)" 
-                    ."\n");
+            $ev = $this->addEvent('NOTIFY', $w, "Notification event cleared (not required any more) - toEmail=true" );;
+            $w->flagDone($ev, '');
+            $this->errorHandler( $ev->remarks);
         }
         if (is_a($email, 'PEAR_Error')) {
             $email =array(
@@ -243,28 +218,18 @@ class Pman_Core_NotifySend extends Pman
          
         if ($email === false || isset($email['error']) || empty($p)) {
             // object returned 'false' - it does not know how to send it..
-            $ev = $this->addEvent('NOTIFYFAIL', $w, isset($email['error'])  ?
-                            $email['error'] : "INTERNAL ERROR  - We can not handle " . $w->ontable); 
-            $ww = clone($w);
-            $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->to_email = $p->email; 
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s ') . 
-                    (isset($email['error'])  ?
-                            $email['error'] : "INTERNAL ERROR  - We can not handle " . $w->ontable)
-                    ."\n");
+            $ev = $this->addEvent('NOTIFYFAIL', $w, isset($email['error'])  ? $email['error'] : "INTERNAL ERROR  - We can not handle " . $w->ontable); 
+            $w->flagDone($ev, '');
+            $this->errorHandler(  $ev->remarks);
         }
         
          
         
         if (isset($email['later'])) {
-            $old = clone($w);
-            $w->act_when = $email['later'];
-            $this->updateServer($w);
-            $w->update($old);
-            $this->errorHandler(date('Y-m-d h:i:s ') . " Delivery postponed by email creator to {$email['later']}");
+            
+            $this->server->updateNotifyToNextServer($w, $email['later'],true);
+             
+            $this->errorHandler("Delivery postponed by email creator to {$email['later']}");
         }
         
          
@@ -291,18 +256,21 @@ class Pman_Core_NotifySend extends Pman
         
             // since some of them have spaces?!?!
         $p->email = trim($p->email);
+        $core_domain = DB_DataObject::factory('core_domain')->loadOrCreate($dom);
+        $ww = clone($w);
+        $ww->to_email = empty($ww->to_email) ? $p->email : $ww->to_email;
+        $ww->domain_id = $core_domain->id;
+        // if to_email has not been set!?
+        $ww->update($w); // if nothing has changed this will not do anything.
+        $w = clone($ww);
+    
       
         
         require_once 'Validate.php';
         if (!Validate::email($p->email, true)) {
             $ev = $this->addEvent('NOTIFYFAIL', $w, "INVALID ADDRESS: " . $p->email);
-            $ww = clone($w);
-            $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->to_email = $p->email; 
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s ') . "INVALID ADDRESS: " . $p->email. "\n");
+            $w->flagDone($ev, '');
+            $this->errorHandler($ev->remarks);
             
         }
         
@@ -339,19 +307,13 @@ class Pman_Core_NotifySend extends Pman
             // only retry for 1 day if the MX issue..
             if ($retry < 240) {
                 $this->addEvent('NOTIFY', $w, 'MX LOOKUP FAILED ' . $dom );
-                $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES'));
-                $this->updateServer($w);
-                $w->update($ww);
-                $this->errorHandler(date('Y-m-d h:i:s') . " - MX LOOKUP FAILED {$dom}\n");
+                $w->flagLater(date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES')));
+                $this->errorHandler($ev->remarks);
             }
             
             $ev = $this->addEvent('NOTIFYFAIL', $w, "BAD ADDRESS - BAD DOMAIN - ". $p->email );
-            $w->sent =   $w->sqlValue('NOW()'); // why not updated - used to leave as is?
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->to_email = $p->email; 
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s') . " - FAILED -  BAD DOMAIN - {$p->email} \n");
+            $w->flagDone($ev, '');
+            $this->errorHandler($ev->remarks);
             
             
         }
@@ -361,17 +323,11 @@ class Pman_Core_NotifySend extends Pman
         
         if (!$force && strtotime($w->act_start) <  strtotime('NOW - 3 DAY')) {
             $ev = $this->addEvent('NOTIFYFAIL', $w, "BAD ADDRESS - GIVE UP - ". $p->email );
-            $w->sent =  $w->sqlValue('NOW()'); 
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->to_email = $p->email; 
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s') . " - FAILED -  GAVE UP TO OLD - {$p->email} \n");
+            $w->flagDone($ev, '');
+            $this->errorHandler(  $ev->remarks);
         }
         
         
-        
-        $w->to_email = $p->email; 
         //$this->addEvent('NOTIFY', $w, 'GREYLISTED ' . $p->email . ' ' . $res->toString());
         // we can only update act_when if it has not been sent already (only happens when running in force mode..)
         // set act when if it's empty...
@@ -384,17 +340,8 @@ class Pman_Core_NotifySend extends Pman
         $fail = false;
         require_once 'Mail.php';
         
-        $core_domain = DB_DataObject::factory('core_domain');
-        if(!$core_domain->get('domain', $dom)){
-            $core_domain = DB_DataObject::factory('core_domain');
-            $core_domain->setFrom(array(
-                'domain' => $dom
-            ));
-            $core_domain->insert();
-        }
-         
         
-        $this->initHelo();
+        $this->server->initHelo();
         
         if (!isset($ff->Mail['helo'])) {
             $this->errorHandler("config Mail[helo] is not set");
@@ -446,16 +393,14 @@ class Pman_Core_NotifySend extends Pman
                     
                     $core_notify = DB_DataObject::factory($this->table);
                     $core_notify->domain_id = $core_domain->id;
+                    $core_notify->server_id = $this->server->id;
                     $core_notify->whereAdd("
                         sent >= NOW() - INTERVAL $seconds SECOND
                     ");
                     
                     if($core_notify->count()){
-                        $old = clone($w);
-                        $w->act_when = date("Y-m-d H:i:s", time() + $seconds);
-                        $this->updateServer($w);
-                        $w->update($old);
-                        $this->errorHandler(date('Y-m-d h:i:s ') . " Too many emails sent by {$dom}");
+                        $this->server->updateNotifyToNextServer( $w , date("Y-m-d H:i:s", time() + $seconds), true);
+                        $this->errorHandler( " Too many emails sent by {$dom} - requeing");
                     }
                      
                     
@@ -490,40 +435,30 @@ class Pman_Core_NotifySend extends Pman
                 $ev = $this->addEvent($successEventName, $w, "{$w->to_email} - {$email['headers']['Subject']}");
                 
                 $ev->writeEventLog($this->debug_str);
+                 
+                $w->flagDone($ev,$email['headers']['Message-Id']);
                 
-                if(strtotime($w->act_when) > strtotime("NOW")){
-                    $w->act_when = date('Y-m-d H:i:s');
-                }
-                
-                $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent.....
-                $w->msgid = $email['headers']['Message-Id'];
-                $w->event_id = $ev->id; // sent ok.. - no need to record it..
-                $w->domain_id = $core_domain->id;
-                $w->update($ww);
-                
+                 
                 // enable cc in notify..
                 if (!empty($email['headers']['Cc'])) {
                     $cmailer = Mail::factory('smtp',  isset($ff->Mail) ? $ff->Mail : array() );
                     $email['headers']['Subject'] = "(CC): " . $email['headers']['Subject'];
-                    $cmailer->send($email['headers']['Cc'],
-                                  $email['headers'], $email['body']);
+                    $cmailer->send($email['headers']['Cc'],    $email['headers'], $email['body']);
                     
                 }
                 
                 if (!empty($email['bcc'])) {
                     $cmailer = Mail::factory('smtp', isset($ff->Mail) ? $ff->Mail : array() );
                     $email['headers']['Subject'] = "(CC): " . $email['headers']['Subject'];
-                    $res = $cmailer->send($email['bcc'],
-                                  $email['headers'], $email['body']);
+                    $res = $cmailer->send($email['bcc'],  $email['headers'], $email['body']);
                     if (!$res || is_a($res, 'PEAR_Error')) {
                         echo "could not send bcc..\n";
                     } else {
                         echo "Sent BCC to {$email['bcc']}\n";
                     }
                 }
-                
-                
-                $this->errorHandler(date('Y-m-d h:i:s') . " - SENT {$w->id} - {$w->to_email} \n", true);
+                 
+                $this->errorHandler( " SENT {$w->id} - {$w->remarks}", true);
             }
             // what type of error..
             $code = empty($res->userinfo['smtpcode']) ? -1 : $res->userinfo['smtpcode'];
@@ -547,54 +482,59 @@ class Pman_Core_NotifySend extends Pman
                 }
                 //print_r($res);
                 $this->addEvent('NOTIFY', $w, 'GREYLISTED - ' . $errmsg);
-                $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES'));
-                $this->updateServer($w);
-                $w->domain_id = $core_domain->id;
-                $w->update($ww);
                 
+                $this->server->updateNotifyToNextServer($w,  strtotime('NOW + ' . $retry . ' MINUTES'),true);
                 
-                $this->errorHandler(date('Y-m-d h:i:s') . " - GREYLISTED -  $errmsg \n");
+                $this->errorHandler(  $ev->remarks);
             }
+            
             $fail = true;
             break;
         }
-        if ($fail || $next_try_min > (2*24*60)) {
-        // fail.. = log and give up..
-            $errmsg=  $fail ? ($res->userinfo['smtpcode'] . ': ' .$res->toString()) :  " - UNKNOWN ERROR";
+        
+        // after trying all mxs - could not connect...
+        if  (!$fail && ($next_try_min > (2*24*60) || strtotime($w->act_start) < strtotime('NOW - 3 DAYS'))) {
+            
+            $errmsg=  " - UNKNOWN ERROR";
             if (isset($res->userinfo['smtptext'])) {
                 $errmsg=  $res->userinfo['smtpcode'] . ':' . $res->userinfo['smtptext'];
             }
             
-            $ev = $this->addEvent('NOTIFYFAIL', $w, ($fail ? "FAILED - " : "RETRY TIME EXCEEDED - ") .
-                       $errmsg);
-            $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->domain_id = $core_domain->id;
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s') . ' - FAILED - '. ($fail ? $res->toString() : "RETRY TIME EXCEEDED\n"));
+            $ev = $this->addEvent('NOTIFYFAIL', $w,  "RETRY TIME EXCEEDED - " .  $errmsg);
+            $w->flagDone($ev, '');
+            $this->errorHandler( $ev->remarks);
         }
         
-        // handle no host availalbe forever...
-        if (strtotime($w->act_start) < strtotime('NOW - 3 DAYS')) {
-            $ev = $this->addEvent('NOTIFYFAIL', $w, "RETRY TIME EXCEEDED - ". $p->email);
-            $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent.....
-            $w->msgid = '';
-            $w->event_id = $ev->id;
-            $w->domain_id = $core_domain->id;
-            $w->update($ww);
-            $this->errorHandler(date('Y-m-d h:i:s') . " - FAILED - RETRY TIME EXCEEDED\n");
+        if ($fail) { //// !!!!<<< BLACKLIST DETECT?
+        // fail.. = log and give up..
+            $errmsg=   $res->userinfo['smtpcode'] . ': ' .$res->toString();
+            if (isset($res->userinfo['smtptext'])) {
+                $errmsg=  $res->userinfo['smtpcode'] . ':' . $res->userinfo['smtptext'];
+            }
+            
+            $ev = $this->addEvent('NOTIFYFAIL', $w, ($fail ? "FAILED - " : "RETRY TIME EXCEEDED - ") .  $errmsg);
+            $w->flagDone($ev, '');
+            
+            if ($res->userinfo['smtpcode'] == 550) {
+                $this->server->checkSmtpResponse($errmsg, $core_domain);
+            }
+            
+
+            $this->errorHandler( $ev->remarks);
         }
         
+        // at this point we just could not find any MX records..
         
-        $this->addEvent('NOTIFY', $w, 'NO HOST CAN BE CONTACTED:' . $p->email);
-        $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES'));
         
-        $this->updateServer($w);
+        // try again.
         
-        $w->domain_id = $core_domain->id;
-        $w->update($ww);
-        $this->errorHandler(date('Y-m-d h:i:s') ." - NO HOST AVAILABLE\n");
+        $ev = $this->addEvent('NOTIFY', $w, 'NO HOST CAN BE CONTACTED:' . $p->email);
+        
+        $this->server->updateNotifyToNextServer($w,  strtotime('NOW + ' . $retry . ' MINUTES'),true);
+
+        
+         
+        $this->errorHandler($ev->remarks);
 
         
     }
@@ -704,7 +644,7 @@ class Pman_Core_NotifySend extends Pman
             throw new Pman_Core_NotifySend_Exception_Fail($msg);
         }
         
-        die($msg);
+        die(date('Y-m-d h:i:s') . ' ' . $msg ."\n");
         
         
     }
@@ -725,25 +665,6 @@ class Pman_Core_NotifySend extends Pman
          
     }
     
-     function initHelo()
-    {
-        $ff = HTML_FlexyFramework::get();
-        
-        if (isset($ff->Core_Notify['servers-non-pool'])  &&
-            isset($ff->Core_Notify['servers-non-pool'][gethostname()]) &&
-            isset($ff->Core_Notify['servers-non-pool'][gethostname()]['helo']) ) {
-            $ff->Mail['helo'] = $ff->Core_Notify['servers-non-pool'][gethostname()]['helo'];
-            return;
-        }
-        
-        if (empty($ff->Core_Notify['servers'])) {
-            return;
-        }
-        if (!isset($ff->Core_Notify['servers'][gethostname()]) || !isset($ff->Core_Notify['servers'][gethostname()]['helo']) ) {
-            $this->jerr("Core_Notify['servers']['" . gethostname() . "']['helo'] not set");
-        }
-        $ff->Mail['helo'] = $ff->Core_Notify['servers'][gethostname()]['helo'];
-        
-    }
+
     
 }
\ No newline at end of file