]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Lots more work on adapting library. Added more commenting and fixed some stuff on...
authorLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Mon, 14 Jun 2010 18:53:43 +0000 (19:53 +0100)
committerLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Mon, 14 Jun 2010 18:53:43 +0000 (19:53 +0100)
plugins/Msn/MsnPlugin.php
plugins/Msn/extlib/phpmsnclass/msn.class.php
plugins/Msn/msnmanager.php

index 5566b543027cf7598c446a39eeaaaebec782aaf3..9a9e703986979e330958132eefd392aeba107801 100644 (file)
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * Send and receive notices using the AIM network
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  IM
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-// We bundle the phptoclib library...
-set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/extlib/phptoclib');
-
-/**
- * Plugin for AIM
- *
- * @category  Plugin
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class MsnPlugin extends ImPlugin
-{
-    public $user =  null;
-    public $password = null;
-    public $publicFeed = array();
-
-    public $transport = 'msnim';
-
-    function getDisplayName()
-    {
-        return _m('MSN');
-    }
-
-    function normalize($screenname)
-    {
-               $screenname = str_replace(" ","", $screenname);
-        return strtolower($screenname);
-    }
-
-    function daemon_screenname()
-    {
-        return $this->user;
-    }
-
-    function validate($screenname)
-    {
-        if(preg_match('/^[a-z]\w{2,15}$/i', $screenname)) {
-            return true;
-        }else{
-            return false;
-        }
-    }
-
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'Msn':
-            require_once(INSTALLDIR.'/plugins/Msn/extlib/phpmsnclass/msn.class.php');
-            return false;
-        case 'MsnManager':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        case 'Fake_Msn':
-            include_once $dir . '/'. $cls .'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
-    function onStartImDaemonIoManagers(&$classes)
-    {
-        parent::onStartImDaemonIoManagers(&$classes);
-        $classes[] = new MsnManager($this); // handles sending/receiving
-        return true;
-    }
-
-    function microiduri($screenname)
-    {
-        return 'msnim:' . $screenname;    
-    }
-
-    function send_message($screenname, $body)
-    {
-        //$this->fake_aim->sendIm($screenname, $body);
-           //$this->enqueue_outgoing_raw($this->fake_aim->would_be_sent);
-           $this->enqueue_outgoing_raw(array($screenname, $body));
-        return true;
-    }
-
-    /**
-     * Accept a queued input message.
-     *
-     * @return true if processing completed, false if message should be reprocessed
-     */
-    function receive_raw_message($message)
-    {
-        $info=Aim::getMessageInfo($message);
-        $from = $info['from'];
-        $user = $this->get_user($from);
-        $notice_text = $info['message'];
-
-        $this->handle_incoming($from, $notice_text);
-
-        return true;
-    }
-
-    function initialize(){
-        if(!isset($this->user)){
-            throw new Exception("must specify a user");
-        }
-        if(!isset($this->password)){
-            throw new Exception("must specify a password");
-        }
-        if(!isset($this->nickname)) {
-            throw new Exception("must specify a nickname");
-        }
-
-        $this->fake_msn = new Fake_Msn($this->user,$this->password,4);
-        return true;
-    }
-
-    function onPluginVersion(&$versions)
-    {
-        $versions[] = array('name' => 'MSN',
-                            'version' => STATUSNET_VERSION,
-                            'author' => 'Luke Fitzgerald',
-                            'homepage' => 'http://status.net/wiki/Plugin:MSN',
-                            'rawdescription' =>
-                            _m('The MSN plugin allows users to send and receive notices over the MSN network.'));
-        return true;
-    }
-}
+<?php\r
+/**\r
+ * StatusNet - the distributed open-source microblogging tool\r
+ * Copyright (C) 2009, StatusNet, Inc.\r
+ *\r
+ * Send and receive notices using the MSN network\r
+ *\r
+ * PHP version 5\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Affero General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Affero General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Affero General Public License\r
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ * @category  IM\r
+ * @package   StatusNet\r
+ * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>\r
+ * @copyright 2010 StatusNet, Inc.\r
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0\r
+ * @link      http://status.net/\r
+ */\r
+\r
+if (!defined('STATUSNET')) {\r
+    // This check helps protect against security problems;\r
+    // your code file can't be executed directly from the web.\r
+    exit(1);\r
+}\r
+// We bundle the phpmsnclass library...\r
+set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/extlib/phpmsnclass');\r
+\r
+/**\r
+ * Plugin for MSN\r
+ *\r
+ * @category  Plugin\r
+ * @package   StatusNet\r
+ * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>\r
+ * @copyright 2010 StatusNet, Inc.\r
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0\r
+ * @link      http://status.net/\r
+ */\r
+\r
+class MsnPlugin extends ImPlugin {\r
+    public $user = null;\r
+    public $password = null;\r
+    public $nickname = null;\r
+    public $transport = 'msnim';\r
+\r
+    /**\r
+     * Get the internationalized/translated display name of this IM service\r
+     *\r
+     * @return string Name of service\r
+     */\r
+    function getDisplayName() {\r
+        return _m('MSN');\r
+    }\r
+\r
+    /**\r
+     * Normalize a screenname for comparison\r
+     *\r
+     * @param string $screenname screenname to normalize\r
+     * @return string an equivalent screenname in normalized form\r
+     */\r
+    function normalize($screenname) {\r
+               $screenname = str_replace(" ","", $screenname);\r
+        return strtolower($screenname);\r
+    }\r
+\r
+    /**\r
+     * Get the screenname of the daemon that sends and receives messages\r
+     *\r
+     * @return string Screenname\r
+     */\r
+    function daemon_screenname() {\r
+        return $this->user;\r
+    }\r
+\r
+    /**\r
+     * Validate (ensure the validity of) a screenname\r
+     *\r
+     * @param string $screenname screenname to validate\r
+     *\r
+     * @return boolean\r
+     */\r
+    function validate($screenname) {\r
+        //TODO Correct this for MSN screennames\r
+        if(preg_match('/^[a-z]\w{2,15}$/i', $screenname)) {\r
+            return true;\r
+        }else{\r
+            return false;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Load related modules when needed\r
+     *\r
+     * @param string $cls Name of the class to be loaded\r
+     *\r
+     * @return boolean hook value; true means continue processing, false means stop.\r
+     */\r
+    public function onAutoload($cls) {\r
+        $dir = dirname(__FILE__);\r
+\r
+        switch ($cls) {\r
+            case 'Msn':\r
+                require_once(INSTALLDIR.'/plugins/Msn/extlib/phpmsnclass/msn.class.php');\r
+                return false;\r
+            case 'MsnManager':\r
+                include_once $dir . '/'.strtolower($cls).'.php';\r
+                return false;\r
+            default:\r
+                return true;\r
+        }\r
+    }\r
+\r
+    public function onStartImDaemonIoManagers(&$classes) {\r
+        parent::onStartImDaemonIoManagers(&$classes);\r
+        $classes[] = new MsnManager($this); // handles sending/receiving\r
+        return true;\r
+    }\r
+\r
+    /**\r
+    * Get a microid URI for the given screenname\r
+    *\r
+    * @param string $screenname\r
+    * @return string microid URI\r
+    */\r
+    public function microiduri($screenname) {\r
+        return 'msnim:' . $screenname;\r
+    }\r
+\r
+    /**\r
+     * Send a message to a given screenname\r
+     *\r
+     * @param string $screenname Screenname to send to\r
+     * @param string $body Text to send\r
+     * @return boolean success value\r
+     */\r
+    public function send_message($screenname, $body) {\r
+           $this->enqueue_outgoing_raw(array('to' => $screenname, 'message' => $body));\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Accept a queued input message.\r
+     *\r
+     * @param array $data Data\r
+     * @return true if processing completed, false if message should be reprocessed\r
+     */\r
+    public function receive_raw_message($data) {\r
+        $this->handle_incoming($data['sender'], $data['message']);\r
+        return true;\r
+    }\r
+\r
+    /**\r
+    * Initialize plugin\r
+    *\r
+    * @return void\r
+    */\r
+    public function initialize() {\r
+        if (!isset($this->user)) {\r
+            throw new Exception("Must specify a user");\r
+        }\r
+        if (!isset($this->password)) {\r
+            throw new Exception("Must specify a password");\r
+        }\r
+        if (!isset($this->nickname)) {\r
+            throw new Exception("Must specify a nickname");\r
+        }\r
+\r
+        return true;\r
+    }\r
+\r
+    function onPluginVersion(&$versions) {\r
+        $versions[] = array('name' => 'MSN',\r
+                            'version' => STATUSNET_VERSION,\r
+                            'author' => 'Luke Fitzgerald',\r
+                            'homepage' => 'http://status.net/wiki/Plugin:MSN',\r
+                            'rawdescription' =>\r
+                            _m('The MSN plugin allows users to send and receive notices over the MSN network.'));\r
+        return true;\r
+    }\r
+}\r
index 16ce72bde7775f59df2bdd88400240de51433775..9b087ac792a5d3e98016b1a99affc7b3489eeaf0 100644 (file)
@@ -33,33 +33,24 @@ class MSN {
     private $passport_policy = '';\r
     private $alias;\r
     private $psm;\r
-    private $use_ping;\r
     private $retry_wait;\r
-    private $backup_file;\r
     private $update_pending;\r
     private $PhotoStickerFile=false;\r
     private $Emotions=false;\r
-    private $MessageQueue=array();\r
-    private $ChildProcess=array();\r
-    private $MAXChildProcess=3;\r
     private $ReqSBXFRTimeout=60;\r
     private $LastPing;\r
     private $ping_wait=50;\r
     private $SBIdleTimeout=10;\r
     private $SBStreamTimeout=2;\r
-    private $NSStreamTimeout=2;\r
     private $MsnObjArray=array();\r
     private $MsnObjMap=array();\r
-    private $SwitchBoardProcess=false;     // false=>Main Process,1 => sb_control_process,2 => sb_ring_process\r
-    private $SwitchBoardSessionUser=false;\r
-    private $SwitchBoardMessageQueue=array();\r
     private $ABAuthHeader;\r
     private $ABService;\r
     private $Contacts;\r
     private $IgnoreList;\r
 \r
-    public $server = 'messenger.hotmail.com';\r
-    public $port = 1863;\r
+    private $server = 'messenger.hotmail.com';\r
+    private $port = 1863;\r
 \r
 \r
     public $clientid = '';\r
@@ -104,49 +95,61 @@ class MSN {
     // for YIM: 518 bytes\r
     public $max_msn_message_len = 1664;\r
     public $max_yahoo_message_len = 518;\r
-    \r
+\r
     // Begin added for StatusNet\r
-    \r
+\r
     private $aContactList = array();\r
     private $aADL = array();\r
-    private $re_login;\r
     private $switchBoardSessions = array();\r
     private $switchBoardSockets = array();\r
     private $waitingForXFR = array();\r
-    \r
+\r
     /**\r
     * Event Handler Functions\r
     */\r
     private $myEventHandlers = array();\r
-    \r
+\r
     // End added for StatusNet\r
-    \r
+\r
+    /**\r
+    * Constructor method\r
+    *\r
+    * @param array $Configs Array of configuration options\r
+    *                       'user'           - Username\r
+    *                       'password'       - Password\r
+    *                       'alias'          - Bot nickname\r
+    *                       'psm'            - Bot personal status message\r
+    *                       'retry_wait'     - Time to wait before trying to reconnect\r
+    *                       'update_pending' - Whether to update pending contacts\r
+    *                       'PhotoSticker'   - Photo file to use (?)\r
+    *                       'debug'          - Enable/Disable debugging mode\r
+    * @param integer $timeout Connection timeout\r
+    * @param integer $client_id Client id (hexadecimal)\r
+    * @return MSN\r
+    */\r
     public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)\r
     {\r
         $this->user = $Configs['user'];\r
         $this->password = $Configs['password'];\r
         $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';\r
         $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';\r
-        $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;\r
         $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;\r
-        $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;\r
         $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;\r
         $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;\r
-        if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)\r
-        {\r
+\r
+        if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false) {\r
             foreach($this->Emotions as $EmotionFilePath)\r
                 $this->MsnObj($EmotionFilePath,$Type=2);\r
-        }        \r
+        }\r
         $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;\r
         $this->timeout = $timeout;\r
-        \r
-        // check support\r
-        if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");\r
-        if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");\r
-        if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");\r
 \r
-        if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");\r
-        if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");\r
+        // Check support\r
+        if (!function_exists('curl_init')) throw new Exception("curl module not found!\n");\r
+        if (!function_exists('preg_match')) throw new Exception("pcre module not found!\n");\r
+        if (!function_exists('mhash')) throw new Exception("mhash module not found!\n");\r
+        if (!function_exists('mcrypt_cbc')) throw new Exception("mcrypt module not found!\n");\r
+        if (!function_exists('bcmod')) throw new Exception("bcmath module not found!\n");\r
 \r
         /*\r
          http://msnpiki.msnfanatic.com/index.php/Client_ID\r
@@ -198,7 +201,13 @@ class MSN {
         if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);\r
         return $ArrayString;\r
     }\r
-    \r
+\r
+    /**\r
+    * Get Passport ticket\r
+    *\r
+    * @param string $url URL string (Optional)\r
+    * @return mixed Array of tickets or false on failure\r
+    */\r
     private function get_passport_ticket($url = '')\r
     {\r
         $user = $this->user;\r
@@ -427,7 +436,7 @@ class MSN {
             'oim_ticket' => html_entity_decode($matches[9]),\r
             'space_ticket' => html_entity_decode($matches[11]),\r
             'storage_ticket' => html_entity_decode($matches[13])\r
-        );        \r
+        );\r
         $this->ticket=$aTickets;\r
         $this->debug_message(var_export($aTickets, true));\r
         $ABAuthHeaderArray=array(\r
@@ -440,7 +449,7 @@ class MSN {
         $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));\r
         return $aTickets;\r
     }\r
-    \r
+\r
     private function UpdateContacts()\r
     {\r
         $ABApplicationHeaderArray=array(\r
@@ -451,7 +460,7 @@ class MSN {
                 'PartnerScenario'=>'ContactSave'\r
              )\r
         );\r
-        \r
+\r
         $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));\r
         $ABFindAllArray=array(\r
             'ABFindAll'=>array(\r
@@ -479,7 +488,7 @@ class MSN {
         }\r
         return true;\r
     }\r
-    \r
+\r
     private function addContact($email, $network, $display = '', $sendADL = false)\r
     {\r
         if ($network != 1) return true;\r
@@ -532,7 +541,8 @@ class MSN {
         return true;\r
     }\r
 \r
-    function delMemberFromList($memberID, $email, $network, $list) {\r
+    function delMemberFromList($memberID, $email, $network, $list)\r
+    {\r
         if ($network != 1 && $network != 32) return true;\r
         if ($memberID === false) return true;\r
         $user = $email;\r
@@ -641,23 +651,24 @@ class MSN {
         if ($http_code != 200) {\r
             preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
             if (count($matches) == 0) {\r
-                $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");\r
+                $this->debug_message("*** can't delete member (network: $network) $email ($memberID) to $list");\r
                 return false;\r
             }\r
             $faultcode = trim($matches[1]);\r
             $faultstring = trim($matches[2]);\r
             if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {\r
-                $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");\r
+                $this->debug_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");\r
                 return false;\r
             }\r
-            $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
+            $this->debug_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
             return true;\r
         }\r
-        $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");\r
+        $this->debug_message("*** delete member (network: $network) $email ($memberID) from $list");\r
         return true;\r
     }\r
 \r
-    function addMemberToList($email, $network, $list) {\r
+    function addMemberToList($email, $network, $list)\r
+    {\r
         if ($network != 1 && $network != 32) return true;\r
         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
         $user = $email;\r
@@ -771,23 +782,24 @@ class MSN {
         if ($http_code != 200) {\r
             preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
             if (count($matches) == 0) {\r
-                $this->log_message("*** can't add member (network: $network) $email to $list");\r
+                $this->debug_message("*** can't add member (network: $network) $email to $list");\r
                 return false;\r
             }\r
             $faultcode = trim($matches[1]);\r
             $faultstring = trim($matches[2]);\r
             if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {\r
-                $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");\r
+                $this->debug_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");\r
                 return false;\r
             }\r
-            $this->log_message("*** add member (network: $network) $email to $list, already exist!");\r
+            $this->debug_message("*** add member (network: $network) $email to $list, already exist!");\r
             return true;\r
         }\r
-        $this->log_message("*** add member (network: $network) $email to $list");\r
+        $this->debug_message("*** add member (network: $network) $email to $list");\r
         return true;\r
     }\r
 \r
-    function getMembershipList($returnData=false) {\r
+    function getMembershipList($returnData=false)\r
+    {\r
         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
@@ -912,7 +924,7 @@ class MSN {
                     @list($u_name, $u_domain) = @explode('@', $email);\r
                     if ($u_domain == NULL) continue;\r
                     $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;\r
-                    $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
+                    $this->debug_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
                 }\r
             }\r
         }\r
@@ -921,10 +933,11 @@ class MSN {
 \r
     /**\r
      * Connect to the NS server\r
-     * @param $user Username\r
-     * @param $password Password\r
-     * @param $redirect_server Redirect server\r
-     * @param $redirect_port Redirect port\r
+     * @param String $user Username\r
+     * @param String $password Password\r
+     * @param String $redirect_server Redirect server\r
+     * @param Integer $redirect_port Redirect port\r
+     * @return Boolean Returns true if successful\r
      */\r
     private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {\r
         $this->id = 1;\r
@@ -943,7 +956,7 @@ class MSN {
             }\r
         }\r
 \r
-        stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
+        stream_set_timeout($this->NSfp, $this->timeout);\r
         $this->authed = false;\r
         // MSNP9\r
         // NS: >> VER {id} MSNP9 CVR0\r
@@ -952,26 +965,20 @@ class MSN {
         $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
 \r
         $start_tm = time();\r
-        while (!feof($this->NSfp))\r
+        while (!self::socketcheck($this->NSfp))\r
         {\r
             $data = $this->ns_readln();\r
             // no data?\r
             if ($data === false) {\r
-                if ($this->timeout > 0) {\r
-                    $now_tm = time();\r
-                    $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;\r
-                    if ($used_time > $this->timeout) {\r
-                        // logout now\r
-                        // NS: >>> OUT\r
-                        $this->ns_writeln("OUT");\r
-                        fclose($this->NSfp);\r
-                        $this->error = 'Timeout, maybe protocol changed!';\r
-                        $this->debug_message("*** $this->error");\r
-                        return false;\r
-                    }\r
-                }\r
-                continue;\r
+                // logout now\r
+                // NS: >>> OUT\r
+                $this->ns_writeln("OUT");\r
+                @fclose($this->NSfp);\r
+                $this->error = 'Timeout, maybe protocol changed!';\r
+                $this->debug_message("*** $this->error");\r
+                return false;\r
             }\r
+\r
             $code = substr($data, 0, 3);\r
             $start_tm = time();\r
 \r
@@ -1015,7 +1022,7 @@ class MSN {
                         // logout now\r
                         // NS: >>> OUT\r
                         $this->ns_writeln("OUT");\r
-                        fclose($this->NSfp);\r
+                        @fclose($this->NSfp);\r
                         $this->error = 'Passport authenticated fail!';\r
                         $this->debug_message("*** $this->error");\r
                         return false;\r
@@ -1041,7 +1048,7 @@ class MSN {
                     if($Type!='NS') break;\r
                     @list($ip, $port) = @explode(':', $server);\r
                     // this connection will close after XFR\r
-                    fclose($this->NSfp);\r
+                    @fclose($this->NSfp);\r
 \r
                     $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
                     if (!$this->NSfp) {\r
@@ -1050,7 +1057,7 @@ class MSN {
                         return false;\r
                     }\r
 \r
-                    stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
+                    stream_set_timeout($this->NSfp, $this->timeout);\r
                     // MSNP9\r
                     // NS: >> VER {id} MSNP9 CVR0\r
                     // MSNP15\r
@@ -1073,7 +1080,7 @@ class MSN {
                         // logout now\r
                         // NS: >>> OUT\r
                         $this->ns_writeln("OUT");\r
-                        fclose($this->NSfp);\r
+                        @fclose($this->NSfp);\r
                         $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
                         $this->debug_message("*** $this->error");\r
                         return false;\r
@@ -1087,13 +1094,14 @@ class MSN {
 \r
     /**\r
      * Sign onto the NS server and retrieve the address book\r
+     *\r
+     * @return void\r
      */\r
     public function signon() {\r
-        $this->log_message("*** try to connect to MSN network");\r
-        \r
+        $this->debug_message("*** try to connect to MSN network");\r
+\r
         while(true) {\r
-            while(!$this->connect($this->user, $this->password))\r
-            {\r
+            while(!$this->connect($this->user, $this->password)) {\r
                 $this->signonFailure("!!! Can't connect to server: $this->error");\r
             }\r
             if($this->UpdateContacts() === false) {\r
@@ -1101,11 +1109,10 @@ class MSN {
                 continue;\r
             }\r
             $this->LastPing=time();\r
-            $this->log_message("*** connected, wait for command");\r
             $start_tm = time();\r
             $ping_tm = time();\r
             if(($this->aContactList = $this->getMembershipList()) === false) {\r
-                $this->signonFailure('!!! Get Membership list failed');\r
+                $this->signonFailure('!!! Get membership list failed');\r
                 continue;\r
             }\r
             if ($this->update_pending) {\r
@@ -1206,21 +1213,26 @@ class MSN {
             $len = strlen($str);\r
             $this->ns_writeln("UUX $this->id $len");\r
             $this->ns_writedata($str);\r
-            break;\r
+            if(!socketcheck($this->NSfp)) {\r
+                $this->debug_message("*** connected, wait for command");\r
+                break;\r
+            } else {\r
+                $this->NSRetryWait($this->retry_wait);\r
+            }\r
         }\r
     }\r
-    \r
+\r
     /**\r
     * Called if there is an error during signon\r
-    * \r
-    * @param $message Error message to log\r
+    *\r
+    * @param string $message Error message to log\r
     */\r
     private function signonFailure($message) {\r
-        $this->log_message($message);\r
-        $this->callHandler('ConnectFailed', NULL);\r
+        $this->debug_message($message);\r
+        $this->callHandler('ConnectFailed');\r
         $this->NSRetryWait($this->retry_wait);\r
     }\r
-    \r
+\r
     function derive_key($key, $magic) {\r
         $hash1 = mhash(MHASH_SHA1, $magic, $key);\r
         $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);\r
@@ -1340,861 +1352,123 @@ class MSN {
         $header_array = array(\r
             'SOAPAction: '.$this->oim_read_soap,\r
             'Content-Type: text/xml; charset=utf-8',\r
-            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
-        );\r
-\r
-        $this->debug_message("*** URL: $this->oim_read_url");\r
-        $this->debug_message("*** Sending SOAP:\n$XML");\r
-        $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);\r
-        curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-        curl_setopt($curl, CURLOPT_POST, 1);\r
-        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-        $data = curl_exec($curl);\r
-        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-        curl_close($curl);\r
-        $this->debug_message("*** Get Result:\n$data");\r
-\r
-        if ($http_code != 200) {\r
-            $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");\r
-            return false;\r
-        }\r
-\r
-        // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?\r
-        // multi-lines?\r
-        $start = strpos($data, '<GetMessageResult>');\r
-        $end = strpos($data, '</GetMessageResult>');\r
-        if ($start === false || $end === false || $start > $end) {\r
-            $this->debug_message("*** Can't get OIM: $msgid");\r
-            return false;\r
-        }\r
-        $lines = substr($data, $start + 18, $end - $start);\r
-        $aLines = @explode("\n", $lines);\r
-        $header = true;\r
-        $ignore = false;\r
-        $sOIM = '';\r
-        foreach ($aLines as $line) {\r
-            $line = rtrim($line);\r
-            if ($header) {\r
-                if ($line === '') {\r
-                    $header = false;\r
-                    continue;\r
-                }\r
-                continue;\r
-            }\r
-            // stop at empty lines\r
-            if ($line === '') break;\r
-            $sOIM .= $line;\r
-        }\r
-        $sMsg = base64_decode($sOIM);\r
-        $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
-\r
-        // delete OIM\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
-               xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
-               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
-<soap:Header>\r
-  <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
-    <t>'.$t.'</t>\r
-    <p>'.$p.'</p>\r
-  </PassportCookie>\r
-</soap:Header>\r
-<soap:Body>\r
-  <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
-    <messageIds>\r
-      <messageId>'.$msgid.'</messageId>\r
-    </messageIds>\r
-  </DeleteMessages>\r
-</soap:Body>\r
-</soap:Envelope>';\r
-\r
-        $header_array = array(\r
-            'SOAPAction: '.$this->oim_del_soap,\r
-            'Content-Type: text/xml; charset=utf-8',\r
-            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
-        );\r
-\r
-        $this->debug_message("*** URL: $this->oim_del_url");\r
-        $this->debug_message("*** Sending SOAP:\n$XML");\r
-        $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);\r
-        curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-        curl_setopt($curl, CURLOPT_POST, 1);\r
-        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-        $data = curl_exec($curl);\r
-        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-        curl_close($curl);\r
-        $this->debug_message("*** Get Result:\n$data");\r
-\r
-        if ($http_code != 200)\r
-            $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");\r
-        else\r
-            $this->debug_message("*** OIM ($msgid) deleted");\r
-        return $sMsg;\r
-    }\r
-    private function NSLogout() {\r
-        if (is_resource($this->NSfp) && !feof($this->NSfp)) {\r
-            // logout now\r
-            // NS: >>> OUT\r
-            $this->ns_writeln("OUT");\r
-            fclose($this->NSfp);\r
-            $this->NSfp = false;\r
-            $this->log_message("*** logout now!");\r
-        }\r
-\r
-    }\r
-    private function NSRetryWait($Wait) {\r
-        $this->log_message("*** wait for $Wait seconds");\r
-        for($i=0;$i<$Wait;$i++) {\r
-            sleep(1);\r
-            if($this->kill_me) return false;\r
-        }\r
-        return true;\r
-    }\r
-    public function ProcessSendMessageFileQueue() {\r
-        $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');\r
-        if (!is_array($aFiles)) return true;\r
-        clearstatcache();\r
-        foreach ($aFiles as $filename) {\r
-            $fp = fopen($filename, 'rt');\r
-            if (!$fp) continue;\r
-            $aTo = array();\r
-            $sMessage = '';\r
-            $buf = trim(fgets($fp));\r
-            if (substr($buf, 0, 3) == 'TO:') {\r
-                $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));\r
-                while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";\r
-            }\r
-            fclose($fp);\r
-            if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')\r
-            $this->log_message("!!! message format error? delete $filename");\r
-            else\r
-            {\r
-                foreach($aTo as $To)\r
-                {\r
-                    @list($user, $domain, $network) = @explode('@', $To);\r
-                    $MessageList[$network]["$user@$domain"]=$sMessage;\r
-                }\r
-            }\r
-            if($this->backup_file)\r
-            {\r
-                $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';\r
-                if (!file_exists($backup_dir)) @mkdir($backup_dir);\r
-                $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);\r
-                if (@rename($filename, $backup_name))\r
-                $this->log_message("*** move file to $backup_name");\r
-            }\r
-            else @unlink($filename);\r
-        }\r
-        foreach ($MessageList as $network => $Messages)\r
-        {\r
-            switch(trim($network))\r
-            {\r
-                case '':\r
-                case 1:   //MSN\r
-                    // okay, try to ask a switchboard (SB) for sending message\r
-                    // NS: >>> XFR {id} SB\r
-                    // $this->ns_writeln("XFR $this->id SB");\r
-                    foreach($Messages as $User => $Message)\r
-                    $this->MessageQueue[$User][]=$Message;\r
-                    break;\r
-                case 'Offline':  //MSN\r
-                    //Send OIM\r
-                    //FIXME: ä¿®æ­£Send OIM\r
-                    foreach($Messages as $To => $Message)\r
-                    {\r
-                        $lockkey='';\r
-                        for ($i = 0; $i < $this->oim_try; $i++)\r
-                        {\r
-                            if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;\r
-                            if (is_array($oim_result) && $oim_result['challenge'] !== false) {\r
-                                // need challenge lockkey\r
-                                $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);\r
-                                $lockkey = $this->getChallenge($oim_result['challenge']);\r
-                                continue;\r
-                            }\r
-                            if ($oim_result === false || $oim_result['auth_policy'] !== false)\r
-                            {\r
-                                if ($this->re_login)\r
-                                {\r
-                                    $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");\r
-                                    break;\r
-                                }\r
-                                $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");\r
-                                // maybe we need to re-login again\r
-                                if(!$this->get_passport_ticket())\r
-                                {\r
-                                    $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
-                                    break;\r
-                                }\r
-                                $this->log_message("**** get new ticket, try it again");\r
-                                continue;\r
-                            }\r
-                        }\r
-                    }\r
-                    break;\r
-                default:  //Other\r
-                    foreach($Messages as $To => $Message) {\r
-                        $Message=$this->getMessage($Message, $network);\r
-                        $len = strlen($Message);\r
-                        $this->ns_writeln("UUM $this->id $To $network 1 $len");\r
-                        $this->ns_writedata($Message);\r
-                        $this->log_message("*** sent to $To (network: $network):\n$Message");\r
-                    }\r
-            }\r
-        }\r
-        if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))\r
-        {\r
-            $this->MessageQueue[$User]['XFRSent']=false;\r
-            $this->MessageQueue[$User]['ReqTime']=false;\r
-        }\r
-        return true;\r
-    }\r
-    public function SignalFunction($signal)\r
-    {\r
-        switch($signal)\r
-        {\r
-            case SIGTRAP:\r
-            case SIGTERM:\r
-            case SIGHUP:\r
-                $this->End();\r
-                return;\r
-            case SIGCHLD:\r
-                $ChildPid=pcntl_wait($status,WUNTRACED);\r
-                if($ChildPid>0)\r
-                {\r
-                    $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);\r
-                    unset($this->ChildProcess[$ChildPid]);\r
-                }\r
-                return;\r
-        }\r
-    }\r
-\r
-    public function Run()\r
-    {\r
-        $this->log_message("*** startup ***");\r
-        if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");\r
-        if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");\r
-        if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");\r
-        $process_file = false;\r
-        $sent = false;\r
-        $aADL = array();\r
-        $aContactList = array();\r
-        while (true)\r
-        {\r
-            if($this->kill_me)\r
-            {\r
-                $this->log_message("*** Okay, kill me now!");\r
-                return $this->NSLogout();\r
-            }\r
-            if (!is_resource($this->NSfp) || feof($this->NSfp))\r
-            {\r
-                $this->log_message("*** try to connect to MSN network");\r
-                if (!$this->connect($this->user, $this->password))\r
-                {\r
-                    $this->log_message("!!! Can't connect to server: $this->error");\r
-                    if(!$this->NSRetryWait($this->retry_wait)) continue;\r
-                }\r
-                $this->UpdateContacts();\r
-                $this->LastPing=time();\r
-                $this->log_message("*** connected, wait for command");\r
-                $start_tm = time();\r
-                $ping_tm = time();\r
-                    $aContactList = $this->getMembershipList();\r
-                    if ($this->update_pending) {\r
-                        if (is_array($aContactList)) {\r
-                            $pending = 'Pending';\r
-                            foreach ($aContactList as $u_domain => $aUserList) {\r
-                                foreach ($aUserList as $u_name => $aNetworks) {\r
-                                    foreach ($aNetworks as $network => $aData) {\r
-                                        if (isset($aData[$pending])) {\r
-                                            // pending list\r
-                                            $cnt = 0;\r
-                                            foreach (array('Allow', 'Reverse') as $list) {\r
-                                                if (isset($aData[$list]))\r
-                                                $cnt++;\r
-                                                else {\r
-                                                    if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
-                                                        $aContactList[$u_domain][$u_name][$network][$list] = false;\r
-                                                        $cnt++;\r
-                                                    }\r
-                                                }\r
-                                            }\r
-                                            if ($cnt >= 2) {\r
-                                                $id = $aData[$pending];\r
-                                                // we can delete it from pending now\r
-                                                if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
-                                                unset($aContactList[$u_domain][$u_name][$network][$pending]);\r
-                                            }\r
-                                        }\r
-                                        else {\r
-                                            // sync list\r
-                                            foreach (array('Allow', 'Reverse') as $list) {\r
-                                                if (!isset($aData[$list])) {\r
-                                                    if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
-                                                    $aContactList[$u_domain][$u_name][$network][$list] = false;\r
-                                                }\r
-                                            }\r
-                                        }\r
-                                    }\r
-                                }\r
-                            }\r
-                        }\r
-                    }\r
-                    $n = 0;\r
-                    $sList = '';\r
-                    $len = 0;\r
-                    if (is_array($aContactList)) {\r
-                        foreach ($aContactList as $u_domain => $aUserList) {\r
-                            $str = '<d n="'.$u_domain.'">';\r
-                            $len += strlen($str);\r
-                            if ($len > 7400) {\r
-                                $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
-                                $n++;\r
-                                $sList = '';\r
-                                $len = strlen($str);\r
-                            }\r
-                            $sList .= $str;\r
-                            foreach ($aUserList as $u_name => $aNetworks) {\r
-                                foreach ($aNetworks as $network => $status) {\r
-                                    $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
-                                    $len += strlen($str);\r
-                                    // max: 7500, but <ml l="1"></d></ml> is 19,\r
-                                    // so we use 7475\r
-                                    if ($len > 7475) {\r
-                                        $sList .= '</d>';\r
-                                        $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
-                                        $n++;\r
-                                        $sList = '<d n="'.$u_domain.'">'.$str;\r
-                                        $len = strlen($sList);\r
-                                    }\r
-                                    else\r
-                                    $sList .= $str;\r
-                                }\r
-                            }\r
-                            $sList .= '</d>';\r
-                        }\r
-                    }\r
-                    $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
-                    // NS: >>> BLP {id} BL\r
-                    $this->ns_writeln("BLP $this->id BL");\r
-                    foreach ($aADL as $str) {\r
-                        $len = strlen($str);\r
-                        // NS: >>> ADL {id} {size}\r
-                        $this->ns_writeln("ADL $this->id $len");\r
-                        $this->ns_writedata($str);\r
-                    }\r
-                    // NS: >>> PRP {id} MFN name\r
-                    if ($this->alias == '') $this->alias = $user;\r
-                    $aliasname = rawurlencode($this->alias);\r
-                    $this->ns_writeln("PRP $this->id MFN $aliasname");\r
-                    //設定個人大頭貼\r
-                    //$MsnObj=$this->PhotoStckObj();\r
-                    // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
-                    $this->ns_writeln("CHG $this->id NLN $this->clientid");                    \r
-                    if($this->PhotoStickerFile!==false)\r
-                        $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
-                    // NS: >>> UUX {id} length\r
-                    $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
-                    $len = strlen($str);\r
-                    $this->ns_writeln("UUX $this->id $len");\r
-                    $this->ns_writedata($str);               \r
-            }\r
-            $data = $this->ns_readln();\r
-            if($data===false)\r
-            {\r
-                //If No NS Message Process SendMessageFileQueue\r
-                if (time()-$this->LastPing > $this->ping_wait)\r
-                {\r
-                    // NS: >>> PNG\r
-                    $this->ns_writeln("PNG");\r
-                    $this->LastPing = time();\r
-                }\r
-                if(count($this->ChildProcess)<$this->MAXChildProcess)\r
-                {\r
-                    $Index=0;\r
-                    foreach($this->MessageQueue as $User => $Message)\r
-                    {\r
-                        if(!trim($User)) continue;\r
-                        if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;\r
-                        if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))\r
-                        {\r
-                            $this->MessageQueue[$User]['XFRSent']=true;\r
-                            $this->MessageQueue[$User]['ReqTime']=time();\r
-                            $this->log_message("*** Request SB for $User");\r
-                            $this->ns_writeln("XFR $this->id SB");\r
-                            $Index++;\r
-                        }\r
-                    }\r
-                }\r
-                if($this->ProcessSendMessageFileQueue()) continue;\r
-                break;\r
-            }\r
-            switch (substr($data,0,3))\r
-            {\r
-                case 'SBS':\r
-                    // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
-                    // NS: <<< SBS 0 null\r
-                    break;\r
-\r
-                case 'RFS':\r
-                    // FIXME:\r
-                    // NS: <<< RFS ???\r
-                    // refresh ADL, so we re-send it again\r
-                    if (is_array($aADL)) {\r
-                        foreach ($aADL as $str) {\r
-                            $len = strlen($str);\r
-                            // NS: >>> ADL {id} {size}\r
-                            $this->ns_writeln("ADL $this->id $len");\r
-                            $this->ns_writedata($str);\r
-                        }\r
-                    }\r
-                    break;\r
-\r
-                case 'LST':\r
-                    // NS: <<< LST {email} {alias} 11 0\r
-                    @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\r
-                    @list($u_name, $u_domain) = @explode('@', $email);\r
-                    if (!isset($aContactList[$u_domain][$u_name][1])) {\r
-                        $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
-                        $this->log_message("*** add to our contact list: $u_name@$u_domain");\r
-                    }\r
-                    break;\r
-\r
-                case 'ADL':\r
-                    // randomly, we get ADL command, someome add us to their contact list for MSNP15\r
-                    // NS: <<< ADL 0 {size}\r
-                    @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0)\r
-                    {\r
-                        $data = $this->ns_readdata($size);\r
-                        preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
-                        if (is_array($matches) && count($matches) > 0)\r
-                        {\r
-                            $u_domain = $matches[1];\r
-                            $u_name = $matches[2];\r
-                            $network = $matches[4];\r
-                            if (isset($aContactList[$u_domain][$u_name][$network]))\r
-                            $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
-                            else\r
-                            {\r
-                                $this->re_login = false;\r
-                                $cnt = 0;\r
-                                foreach (array('Allow', 'Reverse') as $list)\r
-                                {\r
-                                    if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
-                                    {\r
-                                        if ($this->re_login) {\r
-                                            $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
-                                            continue;\r
-                                        }\r
-                                        $aTickets = $this->get_passport_ticket();\r
-                                        if (!$aTickets || !is_array($aTickets)) {\r
-                                            // failed to login? ignore it\r
-                                            $this->log_message("*** can't re-login, something wrong here");\r
-                                            $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
-                                            continue;\r
-                                        }\r
-                                        $this->re_login = true;\r
-                                        $this->ticket = $aTickets;\r
-                                        $this->log_message("**** get new ticket, try it again");\r
-                                        if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
-                                        {\r
-                                            $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
-                                            continue;\r
-                                        }\r
-                                    }\r
-                                    $aContactList[$u_domain][$u_name][$network][$list] = false;\r
-                                    $cnt++;\r
-                                }\r
-                                $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
-                            }\r
-                            $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
-                            $len = strlen($str);\r
-                        }\r
-                        else\r
-                        $this->log_message("*** someone add us to their list: $data");\r
-                        $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
-                    }\r
-                    break;\r
-\r
-                case 'RML':\r
-                    // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
-                    // NS: <<< RML 0 {size}\r
-                    @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0)\r
-                    {\r
-                        $data = $this->ns_readdata($size);\r
-                        preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
-                        if (is_array($matches) && count($matches) > 0)\r
-                        {\r
-                            $u_domain = $matches[1];\r
-                            $u_name = $matches[2];\r
-                            $network = $matches[4];\r
-                            if (isset($aContactList[$u_domain][$u_name][$network]))\r
-                            {\r
-                                $aData = $aContactList[$u_domain][$u_name][$network];\r
-                                foreach ($aData as $list => $id)\r
-                                $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
-                                unset($aContactList[$u_domain][$u_name][$network]);\r
-                                $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
-                            }\r
-                            else\r
-                            $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
-                            $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);\r
-                        }\r
-                        else\r
-                        $this->log_message("*** someone remove us from their list: $data");\r
-                    }\r
-                    break;\r
-\r
-                case 'MSG':\r
-                    // randomly, we get MSG notification from server\r
-                    // NS: <<< MSG Hotmail Hotmail {size}\r
-                    @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0) {\r
-                        $data = $this->ns_readdata($size);\r
-                        $aLines = @explode("\n", $data);\r
-                        $header = true;\r
-                        $ignore = false;\r
-                        $maildata = '';\r
-                        foreach ($aLines as $line) {\r
-                            $line = rtrim($line);\r
-                            if ($header) {\r
-                                if ($line === '') {\r
-                                    $header = false;\r
-                                    continue;\r
-                                }\r
-                                if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
-                                    if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&\r
-                                    strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
-                                        // we just need text/x-msmsgsinitialmdatanotification\r
-                                        // or text/x-msmsgsoimnotification\r
-                                        $ignore = true;\r
-                                        break;\r
-                                    }\r
-                                }\r
-                                continue;\r
-                            }\r
-                            if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
-                                $maildata = trim(substr($line, 10));\r
-                                break;\r
-                            }\r
-                        }\r
-                        if ($ignore) {\r
-                            $this->log_message("*** ingnore MSG for: $line");\r
-                            break;\r
-                        }\r
-                        if ($maildata == '') {\r
-                            $this->log_message("*** ingnore MSG not for OIM");\r
-                            break;\r
-                        }\r
-                        $this->re_login = false;\r
-                        if (strcasecmp($maildata, 'too-large') == 0) {\r
-                            $this->log_message("*** large mail-data, need to get the data via SOAP");\r
-                            $maildata = $this->getOIM_maildata();\r
-                            if ($maildata === false) {\r
-                                $this->log_message("*** can't get mail-data via SOAP");\r
-                                // maybe we need to re-login again\r
-                                $aTickets = $this->get_passport_ticket();\r
-                                if (!$aTickets || !is_array($aTickets)) {\r
-                                    // failed to login? ignore it\r
-                                    $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
-                                    break;\r
-                                }\r
-                                $this->re_login = true;\r
-                                $this->ticket = $aTickets;\r
-                                $this->log_message("**** get new ticket, try it again");\r
-                                $maildata = $this->getOIM_maildata();\r
-                                if ($maildata === false) {\r
-                                    $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
-                                    break;\r
-                                }\r
-                            }\r
-                        }\r
-                        // could be a lots of <M>...</M>, so we can't use preg_match here\r
-                        $p = $maildata;\r
-                        $aOIMs = array();\r
-                        while (1) {\r
-                            $start = strpos($p, '<M>');\r
-                            $end = strpos($p, '</M>');\r
-                            if ($start === false || $end === false || $start > $end) break;\r
-                            $end += 4;\r
-                            $sOIM = substr($p, $start, $end - $start);\r
-                            $aOIMs[] = $sOIM;\r
-                            $p = substr($p, $end);\r
-                        }\r
-                        if (count($aOIMs) == 0) {\r
-                            $this->log_message("*** ingnore empty OIM");\r
-                            break;\r
-                        }\r
-                        foreach ($aOIMs as $maildata) {\r
-                            // T: 11 for MSN, 13 for Yahoo\r
-                            // S: 6 for MSN, 7 for Yahoo\r
-                            // RT: the datetime received by server\r
-                            // RS: already read or not\r
-                            // SZ: size of message\r
-                            // E: sender\r
-                            // I: msgid\r
-                            // F: always 00000000-0000-0000-0000-000000000009\r
-                            // N: sender alias\r
-                            preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
-                            if (count($matches) == 0) {\r
-                                $this->log_message("*** ingnore OIM maildata without <T>type</T>");\r
-                                continue;\r
-                            }\r
-                            $oim_type = $matches[1];\r
-                            if ($oim_type = 13)\r
-                            $network = 32;\r
-                            else\r
-                            $network = 1;\r
-                            preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
-                            if (count($matches) == 0) {\r
-                                $this->log_message("*** ingnore OIM maildata without <E>sender</E>");\r
-                                continue;\r
-                            }\r
-                            $oim_sender = $matches[1];\r
-                            preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
-                            if (count($matches) == 0) {\r
-                                $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");\r
-                                continue;\r
-                            }\r
-                            $oim_msgid = $matches[1];\r
-                            preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
-                            $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
-                            preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
-                            $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
-                            $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
-                            $sMsg = $this->getOIM_message($oim_msgid);\r
-                            if ($sMsg === false) {\r
-                                $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
-                                if ($this->re_login) {\r
-                                    $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
-                                    continue;\r
-                                }\r
-                                $aTickets = $this->get_passport_ticket();\r
-                                if (!$aTickets || !is_array($aTickets)) {\r
-                                    // failed to login? ignore it\r
-                                    $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
-                                    continue;\r
-                                }\r
-                                $this->re_login = true;\r
-                                $this->ticket = $aTickets;\r
-                                $this->log_message("**** get new ticket, try it again");\r
-                                $sMsg = $this->getOIM_message($oim_msgid);\r
-                                if ($sMsg === false) {\r
-                                    $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
-                                    continue;\r
-                                }\r
-                            }\r
-                            $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
-\r
-                            $this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
-                        }\r
-                    }\r
-                    break;\r
-\r
-                case 'UBM':\r
-                    // randomly, we get UBM, this is the message from other network, like Yahoo!\r
-                    // NS: <<< UBM {email} $network $type {size}\r
-                    @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0)\r
-                    {\r
-                        $data = $this->ns_readdata($size);\r
-                        $aLines = @explode("\n", $data);\r
-                        $header = true;\r
-                        $ignore = false;\r
-                        $sMsg = '';\r
-                        foreach ($aLines as $line) {\r
-                            $line = rtrim($line);\r
-                            if ($header) {\r
-                                if ($line === '') {\r
-                                    $header = false;\r
-                                    continue;\r
-                                }\r
-                                if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
-                                    $ignore = true;\r
-                                    break;\r
-                                }\r
-                                continue;\r
-                            }\r
-                            $aSubLines = @explode("\r", $line);\r
-                            foreach ($aSubLines as $str) {\r
-                                if ($sMsg !== '')\r
-                                $sMsg .= "\n";\r
-                                $sMsg .= $str;\r
-                            }\r
-                        }\r
-                        if($ignore)\r
-                        {\r
-                            $this->log_message("*** ingnore from $from_email: $line");\r
-                            break;\r
-                        }\r
-                        $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
-                        $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
-                    }\r
-                    break;\r
-\r
-                case 'UBX':\r
-                    // randomly, we get UBX notification from server\r
-                    // NS: <<< UBX email {network} {size}\r
-                    @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);\r
-                    // we don't need the notification data, so just ignore it\r
-                    if (is_numeric($size) && $size > 0)\r
-                    $this->ns_readdata($size);\r
-                    break;\r
-\r
-                case 'CHL':\r
-                    // randomly, we'll get challenge from server\r
-                    // NS: <<< CHL 0 {code}\r
-                    @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);\r
-                    $fingerprint = $this->getChallenge($chl_code);\r
-                    // NS: >>> QRY {id} {product_id} 32\r
-                    // NS: >>> fingerprint\r
-                    $this->ns_writeln("QRY $this->id $this->prod_id 32");\r
-                    $this->ns_writedata($fingerprint);\r
-                    $this->ns_writeln("CHG $this->id NLN $this->clientid");                    \r
-                    if($this->PhotoStickerFile!==false)\r
-                        $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
-                    break;\r
-                case 'CHG':\r
-                    // NS: <<< CHG {id} {status} {code}\r
-                    // ignore it\r
-                    // change our status to online first\r
-                    break;\r
-\r
-                case 'XFR':\r
-                    // sometimes, NS will redirect to another NS\r
-                    // MSNP9\r
-                    // NS: <<< XFR {id} NS {server} 0 {server}\r
-                    // MSNP15\r
-                    // NS: <<< XFR {id} NS {server} U D\r
-                    // for normal switchboard XFR\r
-                    // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
-                    @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);\r
-                    @list($ip, $port) = @explode(':', $server);\r
-                    if ($server_type != 'SB') {\r
-                        // maybe exit?\r
-                        // this connection will close after XFR\r
-                        $this->NSLogout();\r
-                        continue;\r
-                    }\r
-                    if(count($this->MessageQueue))\r
-                    {\r
-                        foreach($this->MessageQueue as $User => $Message)\r
-                        {\r
-                            //$this->ChildProcess[$ChildPid]\r
-                            $this->log_message("*** XFR SB $User");\r
-                            $pid=pcntl_fork();\r
-                            if($pid)\r
-                            {\r
-                                //Parrent Process\r
-                                $this->ChildProcess[$pid]=$User;\r
-                                break;\r
-                            }\r
-                            elseif($pid==-1)\r
-                            {\r
-                                $this->log_message("*** Fork Error $User");\r
-                                break;\r
-                            }\r
-                            else\r
-                            {\r
-                                //Child Process\r
-                                $this->log_message("*** Child Process Start for $User");\r
-                                unset($Message['XFRSent']);\r
-                                unset($Message['ReqTime']);\r
-                                $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
-                                if ($bSBresult === false)\r
-                                {\r
-                                    // error for switchboard\r
-                                    $this->log_message("!!! error for sending message to ".$User);\r
-                                }\r
-                                die;\r
-                            }\r
-                        }\r
-                        unset($this->MessageQueue[$User]);\r
-                    }\r
-                    /*\r
-                     $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
-                     if ($bSBresult === false) {\r
-                     // error for switchboard\r
-                     $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
-                     $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
-                     }*/\r
-                    break;\r
-                case 'QNG':\r
-                    // NS: <<< QNG {time}\r
-                    @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);\r
-                    if ($this->ping_wait == 0) $this->ping_wait = 50;\r
-                    //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;\r
-                    //Mod by Ricky Set Online\r
-                    break;\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+        );\r
 \r
-                case 'RNG':\r
-                    if($this->PhotoStickerFile!==false)\r
-                        $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
-                    else\r
-                        $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
-                    // someone is trying to talk to us\r
-                    // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
-                    $this->log_message("NS: <<< RNG $data");\r
-                    @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
-                    @list($sb_ip, $sb_port) = @explode(':', $server);\r
-                    if($this->IsIgnoreMail($email)) \r
-                    {\r
-                        $this->log_message("*** Ignore RNG from $email");\r
-                        break;\r
-                    }\r
-                    $this->log_message("*** RING from $email, $sb_ip:$sb_port");\r
-                    $this->addContact($email,1,$email, true);\r
-                    $pid=pcntl_fork();\r
-                    if($pid)\r
-                    {\r
-                        //Parrent Process\r
-                        $this->ChildProcess[$pid]='RNG';\r
-                        break;\r
-                    }\r
-                    elseif($pid==-1)\r
-                    {\r
-                        $this->log_message("*** Fork Error $User");\r
-                        break;\r
-                    }\r
-                    else\r
-                    {\r
-                        //Child Process\r
-                        $this->log_message("*** Ring Child Process Start for $User");\r
-                        $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
-                        die;\r
-                    }\r
-                    break;\r
-                case 'OUT':\r
-                    // force logout from NS\r
-                    // NS: <<< OUT xxx\r
-                    fclose($this->NSfp);\r
-                    $this->log_message("*** LOGOUT from NS");\r
-                    break;\r
+        $this->debug_message("*** URL: $this->oim_read_url");\r
+        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        $curl = curl_init();\r
+        curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);\r
+        curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
 \r
-                default:\r
-                    $code = substr($data,0,3);\r
-                    if (is_numeric($code)) {\r
-                        $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
-                        $this->debug_message("*** NS: $this->error");\r
+        if ($http_code != 200) {\r
+            $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");\r
+            return false;\r
+        }\r
 \r
-                        return $this->NsLogout();\r
-                    }\r
-                    break;\r
+        // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?\r
+        // multi-lines?\r
+        $start = strpos($data, '<GetMessageResult>');\r
+        $end = strpos($data, '</GetMessageResult>');\r
+        if ($start === false || $end === false || $start > $end) {\r
+            $this->debug_message("*** Can't get OIM: $msgid");\r
+            return false;\r
+        }\r
+        $lines = substr($data, $start + 18, $end - $start);\r
+        $aLines = @explode("\n", $lines);\r
+        $header = true;\r
+        $ignore = false;\r
+        $sOIM = '';\r
+        foreach ($aLines as $line) {\r
+            $line = rtrim($line);\r
+            if ($header) {\r
+                if ($line === '') {\r
+                    $header = false;\r
+                    continue;\r
+                }\r
+                continue;\r
             }\r
+            // stop at empty lines\r
+            if ($line === '') break;\r
+            $sOIM .= $line;\r
+        }\r
+        $sMsg = base64_decode($sOIM);\r
+        $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
+\r
+        // delete OIM\r
+        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+               xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
+<soap:Header>\r
+  <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+    <t>'.$t.'</t>\r
+    <p>'.$p.'</p>\r
+  </PassportCookie>\r
+</soap:Header>\r
+<soap:Body>\r
+  <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+    <messageIds>\r
+      <messageId>'.$msgid.'</messageId>\r
+    </messageIds>\r
+  </DeleteMessages>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+        $header_array = array(\r
+            'SOAPAction: '.$this->oim_del_soap,\r
+            'Content-Type: text/xml; charset=utf-8',\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+        );\r
+\r
+        $this->debug_message("*** URL: $this->oim_del_url");\r
+        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        $curl = curl_init();\r
+        curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);\r
+        curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+\r
+        if ($http_code != 200)\r
+            $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");\r
+        else\r
+            $this->debug_message("*** OIM ($msgid) deleted");\r
+        return $sMsg;\r
+    }\r
+\r
+    private function NSLogout() {\r
+        if (is_resource($this->NSfp) && !feof($this->NSfp)) {\r
+            // logout now\r
+            // NS: >>> OUT\r
+            $this->ns_writeln("OUT");\r
+            fclose($this->NSfp);\r
+            $this->NSfp = false;\r
+            $this->debug_message("*** logout now!");\r
         }\r
-        return $this->NsLogout();\r
+\r
+    }\r
+\r
+    private function NSRetryWait($wait) {\r
+        $this->debug_message("*** wait for $Wait seconds");\r
+        sleep($wait);\r
     }\r
 \r
     function getChallenge($code)\r
@@ -2323,7 +1597,7 @@ class MSN {
             $data = $this->SB_readln();\r
             if($this->kill_me)\r
             {\r
-                $this->log_message("*** SB Okay, kill me now!");\r
+                $this->debug_message("*** SB Okay, kill me now!");\r
                 break;\r
             }\r
             if($data === false)\r
@@ -2362,11 +1636,11 @@ class MSN {
                 case 'IRO':\r
                     // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}\r
                     @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);\r
-                    $this->log_message("*** $email join us");\r
+                    $this->debug_message("*** $email join us");\r
                     $Joined=true;\r
                     break;\r
                 case 'BYE':\r
-                    $this->log_message("*** Quit for BYE");\r
+                    $this->debug_message("*** Quit for BYE");\r
                     $SessionEnd=true;\r
                     break;\r
                 case 'USR':\r
@@ -2453,7 +1727,7 @@ class MSN {
                     }\r
                     if ($ignore)\r
                     {\r
-                        $this->log_message("*** ingnore from $from_email: $line");\r
+                        $this->debug_message("*** ingnore from $from_email: $line");\r
                         break;\r
                     }\r
                     if ($is_p2p)\r
@@ -2461,18 +1735,18 @@ class MSN {
                         // we will ignore any p2p message after sending acknowledgement\r
                         $ignore = true;\r
                         $len = strlen($sMsg);\r
-                        $this->log_message("*** p2p message from $from_email, size $len");\r
+                        $this->debug_message("*** p2p message from $from_email, size $len");\r
                         // header = 48 bytes\r
                         // content >= 0 bytes\r
                         // footer = 4 bytes\r
                         // so it need to >= 52 bytes\r
                         /*if ($len < 52) {\r
-                            $this->log_message("*** p2p: size error, less than 52!");\r
+                            $this->debug_message("*** p2p: size error, less than 52!");\r
                             break;\r
                         }*/\r
                         $aDwords = @unpack("V12dword", $sMsg);\r
                         if (!is_array($aDwords)) {\r
-                            $this->log_message("*** p2p: header unpack error!");\r
+                            $this->debug_message("*** p2p: header unpack error!");\r
                             break;\r
                         }\r
                         $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));\r
@@ -2532,9 +1806,9 @@ class MSN {
                             $len = strlen($message);\r
                             $this->SB_writeln("MSG $this->id D $len");\r
                             $this->SB_writedata($message);\r
-                            $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");\r
-                            $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));                            \r
-                            $this->SB_readln();//Read ACK;                            \r
+                            $this->debug_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");\r
+                            $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));\r
+                            $this->SB_readln();//Read ACK;\r
                             $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));\r
                             $new_id-=3;\r
                             //Send 200 OK message\r
@@ -2570,7 +1844,7 @@ class MSN {
                             $this->SB_writedata($message);\r
                             $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));\r
                             $this->SB_readln();//Read ACK;\r
-                            \r
+\r
                             $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));\r
                             //send Data preparation message\r
                             //send 4 null bytes as data\r
@@ -2639,7 +1913,7 @@ class MSN {
                                 "BYE MSNMSGR:MSNSLP/1.0\r\n".\r
                                 "To: <msnmsgr:$from_email>\r\n".\r
                                 "From: <msnmsgr:".$this->user.">\r\n".\r
-                                "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".                            \r
+                                "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".\r
                                 "CSeq: 0\r\n".\r
                                 "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
                                 "Max-Forwards: 0\r\n".\r
@@ -2649,7 +1923,7 @@ class MSN {
                             $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
                             $hdr_TotalDataSizeHigh=0;\r
                             $new_id++;\r
-                            $hdr = pack("LLLLLLLLLLLL", \r
+                            $hdr = pack("LLLLLLLLLLLL",\r
                             0,\r
                             $new_id,\r
                             0, 0,\r
@@ -2721,16 +1995,16 @@ class MSN {
                          $this->SB_writeln("MSG $id D $len");\r
                          $id++;\r
                          $this->SB_writedata($message);\r
-                         $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");\r
+                         $this->debug_message("*** p2p: send acknowledgement for $hdr_SessionID");\r
                          $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));\r
                          */\r
                         break;\r
                     }\r
-                    $this->log_message("*** MSG from $from_email: $sMsg");\r
+                    $this->debug_message("*** MSG from $from_email: $sMsg");\r
                     $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
                     break;\r
                 case '217':\r
-                    $this->log_message("*** User $user is offline. Try OIM.");\r
+                    $this->debug_message("*** User $user is offline. Try OIM.");\r
                     foreach($this->SwitchBoardMessageQueue as $Message)\r
                     $this->SendMessage($Message,"$user@Offline");\r
                     $SessionEnd=true;\r
@@ -2902,21 +2176,10 @@ class MSN {
         return $buf;\r
     }\r
 \r
-    // write log\r
-    function log_message($str) {\r
-        /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';\r
-        $fp = fopen($fname, 'at');\r
-        if ($fp) {\r
-            fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");\r
-            fclose($fp);\r
-        }*/\r
-        $this->debug_message($str);\r
-        return;\r
-    }\r
     /**\r
      *\r
      * @param $FilePath åœ–檔路徑\r
-     * @param $Type     æª”案類型 3=>大頭貼,2表情圖案    \r
+     * @param $Type     æª”案類型 3=>大頭貼,2表情圖案\r
      * @return array\r
      */\r
     private function MsnObj($FilePath,$Type=3)\r
@@ -2933,7 +2196,7 @@ class MSN {
         $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");\r
         return $MsnObj;\r
     }\r
-    \r
+\r
     private function linetoArray($lines) {\r
         $lines=str_replace("\r",'',$lines);\r
         $lines=explode("\n",$lines);\r
@@ -2944,7 +2207,7 @@ class MSN {
         }\r
         return $Data;\r
     }\r
-    \r
+\r
     private function GetPictureFilePath($Context)\r
     {\r
         $MsnObj=base64_decode($Context);\r
@@ -2955,7 +2218,7 @@ class MSN {
         return $this->MsnObjArray[$location];\r
         return false;\r
     }\r
-    \r
+\r
     private function GetMsnObjDefine($Message)\r
     {\r
         $DefineString='';\r
@@ -2967,23 +2230,26 @@ class MSN {
         }\r
         return $DefineString;\r
     }\r
-    \r
+\r
     /**\r
      * Read and handle incoming command from NS\r
      */\r
-    public function nsReceive() {\r
+    private function nsReceive() {\r
         // Sign in again if not signed in or socket failed\r
-        if (!is_resource($this->NSfp) || feof($this->NSfp)) {\r
-            $this->callHandler('Reconnect', NULL);\r
+        if (!is_resource($this->NSfp) || self::socketcheck($this->NSfp)) {\r
+            $this->callHandler('Reconnect');\r
+            $this->NSRetryWait($this->retry_wait);\r
             $this->signon();\r
             return;\r
         }\r
-        \r
+\r
         $data = $this->ns_readln();\r
         if($data === false) {\r
             // There was no data / an error when reading from the socket so reconnect\r
-            $this->callHandler('Reconnect', NULL);\r
+            $this->callHandler('Reconnect');\r
+            $this->NSRetryWait($this->retry_wait);\r
             $this->signon();\r
+            return;\r
         } else {\r
             switch (substr($data,0,3))\r
             {\r
@@ -2991,7 +2257,7 @@ class MSN {
                     // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
                     // NS: <<< SBS 0 null\r
                     break;\r
-    \r
+\r
                 case 'RFS':\r
                     // FIXME:\r
                     // NS: <<< RFS ???\r
@@ -3005,17 +2271,17 @@ class MSN {
                         }\r
                     }\r
                     break;\r
-    \r
+\r
                 case 'LST':\r
                     // NS: <<< LST {email} {alias} 11 0\r
                     @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\r
                     @list($u_name, $u_domain) = @explode('@', $email);\r
                     if (!isset($this->aContactList[$u_domain][$u_name][1])) {\r
                         $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
-                        $this->log_message("*** add to our contact list: $u_name@$u_domain");\r
+                        $this->debug_message("*** add to our contact list: $u_name@$u_domain");\r
                     }\r
                     break;\r
-    \r
+\r
                 case 'ADL':\r
                     // randomly, we get ADL command, someome add us to their contact list for MSNP15\r
                     // NS: <<< ADL 0 {size}\r
@@ -3030,49 +2296,48 @@ class MSN {
                             $u_name = $matches[2];\r
                             $network = $matches[4];\r
                             if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
-                            $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
-                            else\r
-                            {\r
-                                $this->re_login = false;\r
+                                $this->debug_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
+                            else {\r
+                                $re_login = false;\r
                                 $cnt = 0;\r
                                 foreach (array('Allow', 'Reverse') as $list)\r
                                 {\r
                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
                                     {\r
-                                        if ($this->re_login) {\r
-                                            $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+                                        if ($re_login) {\r
+                                            $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
                                             continue;\r
                                         }\r
                                         $aTickets = $this->get_passport_ticket();\r
                                         if (!$aTickets || !is_array($aTickets)) {\r
                                             // failed to login? ignore it\r
-                                            $this->log_message("*** can't re-login, something wrong here");\r
-                                            $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+                                            $this->debug_message("*** can't re-login, something wrong here");\r
+                                            $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
                                             continue;\r
                                         }\r
-                                        $this->re_login = true;\r
+                                        $re_login = true;\r
                                         $this->ticket = $aTickets;\r
-                                        $this->log_message("**** get new ticket, try it again");\r
+                                        $this->debug_message("**** get new ticket, try it again");\r
                                         if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
                                         {\r
-                                            $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+                                            $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
                                             continue;\r
                                         }\r
                                     }\r
                                     $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
                                     $cnt++;\r
                                 }\r
-                                $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
+                                $this->debug_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
                             }\r
                             $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
                             $len = strlen($str);\r
                         }\r
                         else\r
-                        $this->log_message("*** someone add us to their list: $data");\r
+                        $this->debug_message("*** someone add us to their list: $data");\r
                         $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
                     }\r
                     break;\r
-    \r
+\r
                 case 'RML':\r
                     // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
                     // NS: <<< RML 0 {size}\r
@@ -3092,17 +2357,17 @@ class MSN {
                                 foreach ($aData as $list => $id)\r
                                 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
                                 unset($this->aContactList[$u_domain][$u_name][$network]);\r
-                                $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
+                                $this->debug_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
                             }\r
                             else\r
-                            $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
+                            $this->debug_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
                             $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);\r
                         }\r
                         else\r
-                        $this->log_message("*** someone remove us from their list: $data");\r
+                        $this->debug_message("*** someone remove us from their list: $data");\r
                     }\r
                     break;\r
-    \r
+\r
                 case 'MSG':\r
                     // randomly, we get MSG notification from server\r
                     // NS: <<< MSG Hotmail Hotmail {size}\r
@@ -3137,32 +2402,32 @@ class MSN {
                             }\r
                         }\r
                         if ($ignore) {\r
-                            $this->log_message("*** ingnore MSG for: $line");\r
+                            $this->debug_message("*** ingnore MSG for: $line");\r
                             break;\r
                         }\r
                         if ($maildata == '') {\r
-                            $this->log_message("*** ingnore MSG not for OIM");\r
+                            $this->debug_message("*** ingnore MSG not for OIM");\r
                             break;\r
                         }\r
-                        $this->re_login = false;\r
+                        $re_login = false;\r
                         if (strcasecmp($maildata, 'too-large') == 0) {\r
-                            $this->log_message("*** large mail-data, need to get the data via SOAP");\r
+                            $this->debug_message("*** large mail-data, need to get the data via SOAP");\r
                             $maildata = $this->getOIM_maildata();\r
                             if ($maildata === false) {\r
-                                $this->log_message("*** can't get mail-data via SOAP");\r
+                                $this->debug_message("*** can't get mail-data via SOAP");\r
                                 // maybe we need to re-login again\r
                                 $aTickets = $this->get_passport_ticket();\r
                                 if (!$aTickets || !is_array($aTickets)) {\r
                                     // failed to login? ignore it\r
-                                    $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+                                    $this->debug_message("*** can't re-login, something wrong here, ignore this OIM");\r
                                     break;\r
                                 }\r
-                                $this->re_login = true;\r
+                                $re_login = true;\r
                                 $this->ticket = $aTickets;\r
-                                $this->log_message("**** get new ticket, try it again");\r
+                                $this->debug_message("*** get new ticket, try it again");\r
                                 $maildata = $this->getOIM_maildata();\r
                                 if ($maildata === false) {\r
-                                    $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
+                                    $this->debug_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
                                     break;\r
                                 }\r
                             }\r
@@ -3180,7 +2445,7 @@ class MSN {
                             $p = substr($p, $end);\r
                         }\r
                         if (count($aOIMs) == 0) {\r
-                            $this->log_message("*** ingnore empty OIM");\r
+                            $this->debug_message("*** ingnore empty OIM");\r
                             break;\r
                         }\r
                         foreach ($aOIMs as $maildata) {\r
@@ -3195,7 +2460,7 @@ class MSN {
                             // N: sender alias\r
                             preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
                             if (count($matches) == 0) {\r
-                                $this->log_message("*** ingnore OIM maildata without <T>type</T>");\r
+                                $this->debug_message("*** ingnore OIM maildata without <T>type</T>");\r
                                 continue;\r
                             }\r
                             $oim_type = $matches[1];\r
@@ -3205,13 +2470,13 @@ class MSN {
                             $network = 1;\r
                             preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
                             if (count($matches) == 0) {\r
-                                $this->log_message("*** ingnore OIM maildata without <E>sender</E>");\r
+                                $this->debug_message("*** ingnore OIM maildata without <E>sender</E>");\r
                                 continue;\r
                             }\r
                             $oim_sender = $matches[1];\r
                             preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
                             if (count($matches) == 0) {\r
-                                $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");\r
+                                $this->debug_message("*** ingnore OIM maildata without <I>msgid</I>");\r
                                 continue;\r
                             }\r
                             $oim_msgid = $matches[1];\r
@@ -3219,37 +2484,37 @@ class MSN {
                             $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
                             preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
                             $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
-                            $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
+                            $this->debug_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
                             $sMsg = $this->getOIM_message($oim_msgid);\r
                             if ($sMsg === false) {\r
-                                $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
-                                if ($this->re_login) {\r
-                                    $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
+                                $this->debug_message("*** can't get OIM, msgid = $oim_msgid");\r
+                                if ($re_login) {\r
+                                    $this->debug_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
                                     continue;\r
                                 }\r
                                 $aTickets = $this->get_passport_ticket();\r
                                 if (!$aTickets || !is_array($aTickets)) {\r
                                     // failed to login? ignore it\r
-                                    $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+                                    $this->debug_message("*** can't re-login, something wrong here, ignore this OIM");\r
                                     continue;\r
                                 }\r
-                                $this->re_login = true;\r
+                                $re_login = true;\r
                                 $this->ticket = $aTickets;\r
-                                $this->log_message("**** get new ticket, try it again");\r
+                                $this->debug_message("*** get new ticket, try it again");\r
                                 $sMsg = $this->getOIM_message($oim_msgid);\r
                                 if ($sMsg === false) {\r
-                                    $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
+                                    $this->debug_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
                                     continue;\r
                                 }\r
                             }\r
-                            $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
-    \r
+                            $this->debug_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
+\r
                             //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
                             $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));\r
                         }\r
                     }\r
                     break;\r
-    \r
+\r
                 case 'UBM':\r
                     // randomly, we get UBM, this is the message from other network, like Yahoo!\r
                     // NS: <<< UBM {email} $network $type {size}\r
@@ -3283,15 +2548,15 @@ class MSN {
                         }\r
                         if($ignore)\r
                         {\r
-                            $this->log_message("*** ingnore from $from_email: $line");\r
+                            $this->debug_message("*** ingnore from $from_email: $line");\r
                             break;\r
                         }\r
-                        $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
+                        $this->debug_message("*** MSG from $from_email (network: $network): $sMsg");\r
                         //$this->ReceivedMessage($from_email,$sMsg,$network,false);\r
                         $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));\r
                     }\r
                     break;\r
-    \r
+\r
                 case 'UBX':\r
                     // randomly, we get UBX notification from server\r
                     // NS: <<< UBX email {network} {size}\r
@@ -3300,7 +2565,7 @@ class MSN {
                     if (is_numeric($size) && $size > 0)\r
                     $this->ns_readdata($size);\r
                     break;\r
-    \r
+\r
                 case 'CHL':\r
                     // randomly, we'll get challenge from server\r
                     // NS: <<< CHL 0 {code}\r
@@ -3310,7 +2575,7 @@ class MSN {
                     // NS: >>> fingerprint\r
                     $this->ns_writeln("QRY $this->id $this->prod_id 32");\r
                     $this->ns_writedata($fingerprint);\r
-                    $this->ns_writeln("CHG $this->id NLN $this->clientid");                    \r
+                    $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
                     if($this->PhotoStickerFile!==false)\r
                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
                     break;\r
@@ -3319,7 +2584,7 @@ class MSN {
                     // ignore it\r
                     // change our status to online first\r
                     break;\r
-    \r
+\r
                 case 'XFR':\r
                     // sometimes, NS will redirect to another NS\r
                     // MSNP9\r
@@ -3341,7 +2606,7 @@ class MSN {
                         foreach($this->MessageQueue as $User => $Message)\r
                         {\r
                             //$this->ChildProcess[$ChildPid]\r
-                            $this->log_message("*** XFR SB $User");\r
+                            $this->debug_message("*** XFR SB $User");\r
                             $pid=pcntl_fork();\r
                             if($pid)\r
                             {\r
@@ -3351,20 +2616,20 @@ class MSN {
                             }\r
                             elseif($pid==-1)\r
                             {\r
-                                $this->log_message("*** Fork Error $User");\r
+                                $this->debug_message("*** Fork Error $User");\r
                                 break;\r
                             }\r
                             else\r
                             {\r
                                 //Child Process\r
-                                $this->log_message("*** Child Process Start for $User");\r
+                                $this->debug_message("*** Child Process Start for $User");\r
                                 unset($Message['XFRSent']);\r
                                 unset($Message['ReqTime']);\r
                                 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
                                 if ($bSBresult === false)\r
                                 {\r
                                     // error for switchboard\r
-                                    $this->log_message("!!! error for sending message to ".$User);\r
+                                    $this->debug_message("!!! error for sending message to ".$User);\r
                                 }\r
                                 die;\r
                             }\r
@@ -3375,20 +2640,16 @@ class MSN {
                      $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
                      if ($bSBresult === false) {\r
                      // error for switchboard\r
-                     $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
+                     $this->debug_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
                      $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
                      }*/\r
                     break;\r
                 case 'QNG':\r
                     // NS: <<< QNG {time}\r
                     @list(/* QNG */, $ping_wait) = @explode(' ', $data);\r
-                    //if ($this->ping_wait == 0) $this->ping_wait = 50;\r
-                    //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;\r
-                    //Mod by Ricky Set Online\r
-                    \r
                     $this->callHandler('Pong', $ping_wait);\r
                     break;\r
-    \r
+\r
                 case 'RNG':\r
                     if($this->PhotoStickerFile!==false)\r
                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
@@ -3396,15 +2657,15 @@ class MSN {
                         $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
                     // someone is trying to talk to us\r
                     // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
-                    $this->log_message("NS: <<< RNG $data");\r
+                    $this->debug_message("NS: <<< RNG $data");\r
                     @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
                     @list($sb_ip, $sb_port) = @explode(':', $server);\r
                     if($this->IsIgnoreMail($email))\r
                     {\r
-                        $this->log_message("*** Ignore RNG from $email");\r
+                        $this->debug_message("*** Ignore RNG from $email");\r
                         break;\r
                     }\r
-                    $this->log_message("*** RING from $email, $sb_ip:$sb_port");\r
+                    $this->debug_message("*** RING from $email, $sb_ip:$sb_port");\r
                     $this->addContact($email,1,$email, true);\r
                     $pid=pcntl_fork();\r
                     if($pid)\r
@@ -3415,13 +2676,13 @@ class MSN {
                     }\r
                     elseif($pid==-1)\r
                     {\r
-                        $this->log_message("*** Fork Error $User");\r
+                        $this->debug_message("*** Fork Error $User");\r
                         break;\r
                     }\r
                     else\r
                     {\r
                         //Child Process\r
-                        $this->log_message("*** Ring Child Process Start for $User");\r
+                        $this->debug_message("*** Ring Child Process Start for $User");\r
                         $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
                         die;\r
                     }\r
@@ -3429,104 +2690,131 @@ class MSN {
                 case 'OUT':\r
                     // force logout from NS\r
                     // NS: <<< OUT xxx\r
-                    $this->log_message("*** LOGOUT from NS");\r
+                    $this->debug_message("*** LOGOUT from NS");\r
                     return $this->NsLogout();\r
-    \r
+\r
                 default:\r
                     $code = substr($data,0,3);\r
                     if (is_numeric($code)) {\r
                         $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
                         $this->debug_message("*** NS: $this->error");\r
-    \r
+\r
                         return $this->NsLogout();\r
                     }\r
                     break;\r
             }\r
         }\r
     }\r
-    \r
+\r
     /**\r
      * Read and handle incoming command/message from\r
      * a switchboard session socket\r
      */\r
-    public function sbReceive() {\r
-        \r
+    private function sbReceive() {\r
+\r
+    }\r
+\r
+    /**\r
+     * Checks for new data and calls appropriate methods\r
+     *\r
+     * This method is usually called in an infinite loop to keep checking for new data\r
+     *\r
+     * @return void\r
+     */\r
+    public function receive() {\r
+        //First, get an array of sockets that have data that is ready to be read\r
+        $ready = array();\r
+        $ready = $this->getSockets();\r
+        $numrdy = stream_select($ready, $w = NULL, $x = NULL,NULL);\r
+\r
+        //Now that we've waited for something, go through the $ready\r
+        //array and read appropriately\r
+\r
+        for($i = 0;$i<sizeof($ready);$i++) {\r
+            if ($ready[$i] == $this->NSfp) {\r
+                $this->nsReceive();\r
+            } else {\r
+                $this->sbReceive($socket);\r
+            }\r
+        }\r
     }\r
 \r
     /**\r
      * Send a request for a switchboard session\r
-     * @param $to Target email for switchboard session\r
+     * @param String $to Target email for switchboard session\r
      */\r
     private function reqSBSession($to) {\r
-        $this->log_message("*** Request SB for $to");\r
+        $this->debug_message("*** Request SB for $to");\r
         $this->ns_writeln("XFR $this->id SB");\r
-        \r
+\r
         // Add to the queue of those waiting for a switchboard session reponse\r
         $this->switchBoardSessions[$to] = array('socket' => NULL, 'id' => 1, 'lastActive' => NULL, 'joined' => false, 'XFRReqTime' => time());\r
         $this->waitingForXFR[] = &$this->switchBoardSessions[$to];\r
     }\r
-    \r
+\r
     /**\r
      * Following an XFR or RNG, connect to the switchboard session\r
-     * @param $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case or RNG)\r
-     * @param $ip IP of Switchboard\r
-     * @param $port Port of Switchboard\r
-     * @param $to User on other end of Switchboard\r
-     * @param $param Array of parameters - 'cki', 'ticket', 'sid'\r
-     * @return Whether successful\r
+     *\r
+     * @param string $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case or RNG)\r
+     * @param string $ip IP of Switchboard\r
+     * @param integer $port Port of Switchboard\r
+     * @param string $to User on other end of Switchboard\r
+     * @param array $param Array of parameters - 'cki', 'ticket', 'sid'\r
+     * @return boolean true if successful\r
      */\r
     private function connectToSBSession($mode, $ip, $port, $to, $param) {\r
         $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");\r
-        \r
+\r
         $this->switchBoardSessions[$to]['socket'] = @fsockopen($ip, $port, $errno, $errstr, 5);\r
         $socket = $this->switchBoardSessions[$to]['socket'];\r
         if(!$socket) {\r
             $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
             return false;\r
         }\r
-        $this->switchBoardSockets[$socket] = $socket;\r
-        \r
+        $this->switchBoardSockets[(int) $socket] = $socket;\r
+\r
         stream_set_timeout($socket, $this->SBStreamTimeout);\r
-        \r
+\r
         $id = &$this->switchBoardSessions[$to]['id'];\r
-        \r
+\r
         if($mode == 'Active') {\r
             $cki_code = $param['cki'];\r
-            \r
+\r
             // SB: >>> USR {id} {user} {cki}\r
             $this->sb_writeln($socket, $id, "USR $id $this->user $cki_code");\r
         } else {\r
             // Passive\r
             $ticket = $param['ticket'];\r
             $sid = $param['sid'];\r
-            \r
+\r
             // SB: >>> ANS {id} {user} {ticket} {session_id}\r
             $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");\r
         }\r
-        \r
+\r
         $this->switchBoardSessions[$to]['lastActive'] = time();\r
     }\r
-    \r
+\r
     /**\r
      * Send a message via an existing SB session\r
-     * @param $message Message\r
-     * @param $to Recipient for message\r
-     * @return Whether successful\r
+     *\r
+     * @param string $to Recipient for message\r
+     * @param string $message Message\r
+     * @return boolean true on success\r
      */\r
-    private function sendMessageViaSB($message, $to) {\r
+    private function sendMessageViaSB($to, $message) {\r
         if(socketcheck($this->switchBoardSessions[$to]['socket'])) {\r
             $this->reqSBSession($to);\r
             return false;\r
         }\r
-        \r
+\r
         if(!$this->switchBoardSessions[$to]['joined']) {\r
             // If our participant has not joined the session yet we can't message them!\r
             return false;\r
         }\r
-        \r
+\r
         $id = &$this->switchBoardSessions[$to]['id'];\r
         $socket = $this->switchBoardSessions[$to]['socket'];\r
-        \r
+\r
         $aMessage = $this->getMessage($Message);\r
         //CheckEmotion...\r
         $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
@@ -3542,17 +2830,17 @@ class MSN {
         // TODO handle failure during write to socket\r
         $this->sb_writeln($socket, $id, "MSG $id N $len");\r
         $this->sb_writedata($socket, $aMessage);\r
-        \r
+\r
         // Don't close the SB session, we might as well leave it open\r
-        \r
+\r
         return true;\r
     }\r
-    \r
+\r
     /**\r
-     * \r
-     * @param $to\r
-     * @param $sMessage\r
-     * @param $lockkey\r
+     * Send offline message\r
+     * @param string $to Intended recipient\r
+     * @param string $sMessage Message\r
+     * @param string $lockkey Lock key\r
      */\r
     private function sendOIM($to, $sMessage, $lockkey) {\r
         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
@@ -3657,65 +2945,74 @@ X-OIM-Sequence-Num: 1
         }\r
         return array('challenge' => $challenge, 'auth_policy' => $auth_policy);\r
     }\r
-    \r
+\r
     /**\r
      * Send a message to a user on another network\r
+     *\r
      * @param $message Message\r
      * @param $to Intended recipient\r
      * @param $network Network\r
+     * @return void\r
      */\r
     private function sendOtherNetworkMessage($message, $to, $network) {\r
-        $message=$this->getMessage($nessage, $network);\r
+        $message = $this->getMessage($message, $network);\r
         $len = strlen($message);\r
         $this->ns_writeln("UUM $this->id $to $network 1 $len");\r
         $this->ns_writedata($Message);\r
-        $this->log_message("*** sent to $to (network: $network):\n$Message");\r
+        $this->debug_message("*** Sent to $to (network: $network):\n$Message");\r
     }\r
-    \r
+\r
     /**\r
      * Send a message\r
-     * @param $message Message\r
-     * @param $to To address in form user@host.com@network\r
-     *            where network is 1 for MSN, 32 for Yahoo\r
-     *            and 'Offline' for offline messages\r
+     *\r
+     * @param string $to To address in form user@host.com(@network)\r
+     *                   where network is 1 for MSN, 32 for Yahoo\r
+     *                   and 'Offline' for offline messages\r
+     * @param string $message Message\r
      */\r
-    public function sendMessage($message, $to) {\r
+    public function sendMessage($to, $message) {\r
         if($message != '') {\r
-            list($name,$host,$network)=explode('@',$to);\r
-            $network=$network==''?1:$network;\r
-            \r
-            if($network === 1 && $this->switchBoardSessions[$to]['socket'] != NULL && time()-$this->switchBoardSessions[$to]['lastActive'] < $this->SBIdleTimeout) {\r
+            list($name, $host, $network) = explode('@', $to);\r
+            $network = $network == '' ? 1 : $network;\r
+\r
+            if ($network === 1 && $this->switchBoardSessions[$to]['socket'] !== NULL) {\r
                 $recipient = $name . $host;\r
-                $this->debug_message("*** Sending Message to $recipient using existing SB session");\r
-                return $this->sendMessageViaSB($message, $recipient);\r
-            } elseif($network == 'Offline') {\r
+                $this->debug_message("*** Attempting to send message to $recipient using existing SB session");\r
+\r
+                if ($this->sendMessageViaSB($message, $recipient)) {\r
+                    $this->debug_message('*** Message sent successfully');\r
+                    return true;\r
+                } else {\r
+                    $this->debug_message('*** Message sending failed, requesting new SB session');\r
+                    $this->reqSBSession($to);\r
+                    return false;\r
+                }\r
+            } elseif ($network == 'Offline') {\r
                 //Send OIM\r
                 //FIXME: ä¿®æ­£Send OIM\r
-                $lockkey='';\r
-                for ($i = 0; $i < $this->oim_try; $i++)\r
-                {\r
-                    if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;\r
+                $lockkey = '';\r
+                $re_login = false;\r
+                for ($i = 0; $i < $this->oim_try; $i++) {\r
+                    if (($oim_result = $this->sendOIM($to, $message, $lockkey)) === true) break;\r
                     if (is_array($oim_result) && $oim_result['challenge'] !== false) {\r
                         // need challenge lockkey\r
-                        $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);\r
+                        $this->debug_message("*** Need challenge code for ".$oim_result['challenge']);\r
                         $lockkey = $this->getChallenge($oim_result['challenge']);\r
                         continue;\r
                     }\r
-                    if ($oim_result === false || $oim_result['auth_policy'] !== false)\r
-                    {\r
-                        if ($this->re_login)\r
-                        {\r
-                            $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");\r
-                            break;\r
+                    if ($oim_result === false || $oim_result['auth_policy'] !== false) {\r
+                        if ($re_login) {\r
+                            $this->debug_message("*** Can't send OIM, but we already re-logged-in again, so ignore this OIM");\r
+                            return true;\r
                         }\r
-                        $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");\r
-                        // maybe we need to re-login again\r
-                        if(!$this->get_passport_ticket())\r
-                        {\r
-                            $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
-                            break;\r
+                        $this->debug_message("*** Can't send OIM, maybe ticket expired, trying to login again");\r
+\r
+                        // Maybe we need to re-login again\r
+                        if (!$this->get_passport_ticket()) {\r
+                            $this->debug_message("*** Can't re-login, something went wrong here, ignore this OIM");\r
+                            return false;\r
                         }\r
-                        $this->log_message("**** get new ticket, try it again");\r
+                        $this->debug_message("*** Getting new ticket and trying again");\r
                         continue;\r
                     }\r
                 }\r
@@ -3727,11 +3024,10 @@ X-OIM-Sequence-Num: 1
         }\r
         return true;\r
     }\r
-    \r
+\r
     //FIXME Not sure if this is needed?\r
     private function endSBSession($socket) {\r
-        if (feof($socket))\r
-        {\r
+        if (feof($socket)) {\r
             // lost connection? error? try OIM later\r
             @fclose($socket);\r
             return false;\r
@@ -3741,76 +3037,95 @@ X-OIM-Sequence-Num: 1
         @fclose($socket);\r
         return true;\r
     }\r
-    \r
+\r
     /**\r
      * Sends a ping command\r
-     * \r
+     *\r
      * Should be called about every 50 seconds\r
+     *\r
+     * @return void\r
      */\r
     public function sendPing() {\r
         // NS: >>> PNG\r
         $this->ns_writeln("PNG");\r
     }\r
-    \r
+\r
+    /**\r
+    * Methods to return sockets / check socket status\r
+    */\r
+\r
     /**\r
      * Get the NS socket\r
+     *\r
+     * @return resource NS socket\r
      */\r
     public function getNSSocket() {\r
         return $this->NSfp;\r
     }\r
-    \r
+\r
     /**\r
      * Get the Switchboard sockets currently in use\r
+     *\r
+     * @return array Array of Switchboard sockets\r
      */\r
     public function getSBSockets() {\r
         return $this->switchBoardSockets;\r
     }\r
-    \r
+\r
     /**\r
      * Get all the sockets currently in use\r
+     *\r
+     * @return array Array of socket resources\r
      */\r
     public function getSockets() {\r
         return array_merge($this->NSfp, $this->switchBoardSockets);\r
     }\r
-    \r
-    /** \r
+\r
+    /**\r
      * Checks socket for end of file\r
      *\r
-     * @access public\r
-     * @param Resource $socket Socket to check\r
-     * @return boolean true if end of file (socket) \r
+     * @param resource $socket Socket to check\r
+     * @return boolean true if end of file (socket)\r
      */\r
     private static function socketcheck($socket){\r
         $info = stream_get_meta_data($socket);\r
         return $info['eof'];\r
     }\r
-    \r
+\r
+    /**\r
+    * Methods to add / call callbacks\r
+    */\r
+\r
     /**\r
      * Calls User Handler\r
      *\r
      * Calls registered handler for a specific event.\r
-     * \r
-     * @param String $event Command (event) name (Rvous etc)\r
-     * @param String $data Raw message from server\r
+     *\r
+     * @param string $event Command (event) name (Rvous etc)\r
+     * @param array $data Data\r
      * @see registerHandler\r
      * @return void\r
      */\r
-    private function callHandler($event, $data) {\r
+    private function callHandler($event, $data = NULL) {\r
         if (isset($this->myEventHandlers[$event])) {\r
-            call_user_func($this->myEventHandlers[$event], $data);\r
+            if ($data !== NULL) {\r
+                call_user_func($this->myEventHandlers[$event], $data);\r
+            } else {\r
+                call_user_func($this->myEventHandlers[$event]);\r
+            }\r
         }\r
     }\r
-    \r
-    /** \r
+\r
+    /**\r
      * Registers a user handler\r
-     * \r
+     *\r
      * Handler List\r
      * IMIn, Pong, ConnectFailed, Reconnect\r
      *\r
-     * @param String $event Event name\r
-     * @param String $handler User function to call\r
+     * @param string $event Event name\r
+     * @param string $handler User function to call\r
      * @see callHandler\r
-     * @return boolean Returns true if successful\r
+     * @return boolean true if successful\r
      */\r
     public function registerHandler($event, $handler) {\r
         if (is_callable($handler)) {\r
index b0540c46e5284f59d0ba3a62943fad4694cbebde..8f436bdff8e2842a62fd720f6ee0bbf3efbfd990 100644 (file)
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-/**
- * AIM background connection manager for AIM-using queue handlers,
- * allowing them to send outgoing messages on the right connection.
- *
- * Input is handled during socket select loop, keepalive pings during idle.
- * Any incoming messages will be handled.
- *
- * In a multi-site queuedaemon.php run, one connection will be instantiated
- * for each site being handled by the current process that has XMPP enabled.
- */
-
-class MsnManager extends ImManager
-{
-    public $conn = null;
-    
-    protected $lastping = null;
-    
-    private $pingInterval;
-    
-    /**
-     * Initialize connection to server.
-     * @return boolean true on success
-     */
-    public function start($master)
-    {
-        if(parent::start($master))
-        {
-            $this->connect();
-            return true;
-        }else{
-            return false;
-        }
-    }
-
-    public function getSockets()
-    {
-        $this->connect();
-        if($this->conn){
-            return $this->conn->getSockets();
-        }else{
-            return array();
-        }
-    }
-    
-    /**
-     * Idle processing for io manager's execution loop.
-     * Send keepalive pings to server.
-     */
-    public function idle($timeout=0)
-    {
-        $now = time();
-        if (empty($this->lastping) || $now - $this->lastping > $pingInterval) {
-            $this->send_ping();
-        }
-    }
-    
-    /**
-     * Process MSN events that have come in over the wire.
-     * @param resource $socket
-     */
-    public function handleInput($socket)
-    {
-        common_log(LOG_DEBUG, "Servicing the MSN queue.");
-        $this->stats('msn_process');
-        $this->conn->receive();
-    }
-
-    function connect()
-    {
-        if (!$this->conn) {
-            $this->conn=new MSN(array(
-                                      'user' => $this->plugin->user,
-                                      'password' => $this->plugin->password,
-                                      'alias' => $this->plugin->nickname,
-                                      'psm' => 'Send me a message to post a notice',
-                                      'debug' => true
-                                )
-                        );
-            $this->conn->registerHandler("IMIn", array($this, 'handle_msn_message'));
-            $this->conn->registerHandler('Pong', array($this, 'update_ping_time'));
-            $this->conn->registerHandler('ConnectFailed', array($this, 'handle_connect_failed'));
-            $this->conn->registerHandler('Reconnect', array($this, 'handle_reconnect'));
-            $this->conn->signon();
-            $this->lastping = time();
-        }
-        return $this->conn;
-    }
-    
-    function send_ping() {
-        $this->connect();
-        if (!$this->conn) {
-            return false;
-        }
-        
-        $now = time();
-        
-        $this->conn->sendPing();
-        $this->lastping = $now;
-        return true;
-    }
-    
-    /**
-     * Update the time till the next ping
-     * @param $data Time till next ping
-     */
-    function update_ping_time($data) {
-        $pingInterval = $data;
-    }
-    
-    function handle_msn_message($data)
-    {
-        $this->plugin->enqueue_incoming_raw($data);
-        return true;
-    }
-    
-    function handle_connect_failed($data) {
-        common_log(LOG_NOTICE, 'MSN connect failed, retrying');
-    }
-    
-    function handle_reconnect($data) {
-        common_log(LOG_NOTICE, 'MSN reconnecting');
-    }
-
-    function send_raw_message($data)
-    {
-        $this->connect();
-        if (!$this->conn) {
-            return false;
-        }
-        $this->conn->sflapSend($data[0],$data[1],$data[2],$data[3]);
-        
-        // Sending a command updates the time till next ping
-        $this->lastping = time();
-        $this->pingInterval = 50;
-        return true;
-    }
-}
+<?php\r
+/*\r
+ * StatusNet - the distributed open-source microblogging tool\r
+ * Copyright (C) 2008, 2009, StatusNet, Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Affero General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Affero General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Affero General Public License\r
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+\r
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\r
+\r
+/**\r
+ * MSN background connection manager for MSN-using queue handlers,\r
+ * allowing them to send outgoing messages on the right connection.\r
+ *\r
+ * Input is handled during socket select loop, keepalive pings during idle.\r
+ * Any incoming messages will be handled.\r
+ *\r
+ * In a multi-site queuedaemon.php run, one connection will be instantiated\r
+ * for each site being handled by the current process that has MSN enabled.\r
+ */\r
+\r
+class MsnManager extends ImManager {\r
+    public $conn = null;\r
+    private $lastping = null;\r
+    private $pingInterval;\r
+\r
+    /**\r
+     * Initialise connection to server.\r
+     *\r
+     * @return boolean true on success\r
+     */\r
+    public function start($master) {\r
+        if (parent::start($master)) {\r
+            $this->connect();\r
+            return true;\r
+        } else {\r
+            return false;\r
+        }\r
+    }\r
+\r
+    /**\r
+    * Return any open sockets that the run loop should listen\r
+    * for input on.\r
+    *\r
+    * @return array Array of socket resources\r
+    */\r
+    public function getSockets() {\r
+        $this->connect();\r
+        if ($this->conn) {\r
+            return $this->conn->getSockets();\r
+        } else {\r
+            return array();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Idle processing for io manager's execution loop.\r
+     * Send keepalive pings to server.\r
+     *\r
+     * @return void\r
+     */\r
+    public function idle($timeout = 0) {\r
+        if (empty($this->lastping) || time() - $this->lastping > $this->pingInterval) {\r
+            $this->send_ping();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Process MSN events that have come in over the wire.\r
+     *\r
+     * @param resource $socket Socket ready\r
+     * @return void\r
+     */\r
+    public function handleInput($socket) {\r
+        common_log(LOG_DEBUG, 'Servicing the MSN queue.');\r
+        $this->stats('msn_process');\r
+        $this->conn->receive();\r
+    }\r
+\r
+    /**\r
+    * Initiate connection\r
+    *\r
+    * @return void\r
+    */\r
+    function connect() {\r
+        if (!$this->conn) {\r
+            $this->conn = new MSN(array('user' => $this->plugin->user,\r
+                                        'password' => $this->plugin->password,\r
+                                        'alias' => $this->plugin->nickname,\r
+                                        'psm' => 'Send me a message to post a notice',\r
+                                        'debug' => true));\r
+            $this->conn->registerHandler("IMIn", array($this, 'handle_msn_message'));\r
+            $this->conn->registerHandler('Pong', array($this, 'update_ping_time'));\r
+            $this->conn->registerHandler('ConnectFailed', array($this, 'handle_connect_failed'));\r
+            $this->conn->registerHandler('Reconnect', array($this, 'handle_reconnect'));\r
+            $this->conn->signon();\r
+            $this->lastping = time();\r
+        }\r
+        return $this->conn;\r
+    }\r
+\r
+    /**\r
+    * Called by the idle process to send a ping\r
+    * when necessary\r
+    *\r
+    * @return void\r
+    */\r
+    private function send_ping() {\r
+        $this->connect();\r
+        if (!$this->conn) {\r
+            return false;\r
+        }\r
+\r
+        $this->conn->sendPing();\r
+        $this->lastping = time();\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Update the time till the next ping\r
+     * @param $data Time till next ping\r
+     */\r
+    private function update_ping_time($data) {\r
+        $pingInterval = $data;\r
+    }\r
+\r
+    /**\r
+    * Called via a callback when a message is received\r
+    *\r
+    * Passes it back to the queuing system\r
+    *\r
+    * @param array $data Data\r
+    */\r
+    private function handle_msn_message($data) {\r
+        $this->plugin->enqueue_incoming_raw($data);\r
+        return true;\r
+    }\r
+\r
+    /**\r
+    * Called by callback to log failure during connect\r
+    *\r
+    * @param void $data Not used (there to keep callback happy)\r
+    */\r
+    function handle_connect_failed($data) {\r
+        common_log(LOG_NOTICE, 'MSN connect failed, retrying');\r
+    }\r
+\r
+    /**\r
+    * Called by callback to log reconnection\r
+    *\r
+    * @param void $data Not used (there to keep callback happy)\r
+    */\r
+    function handle_reconnect($data) {\r
+        common_log(LOG_NOTICE, 'MSN reconnecting');\r
+    }\r
+\r
+    function send_raw_message($data) {\r
+        $this->connect();\r
+        if (!$this->conn) {\r
+            return false;\r
+        }\r
+\r
+        if (!$this->conn->sendMessage($data['to'], $data['message'])) {\r
+            return false;\r
+        }\r
+\r
+        // Sending a command updates the time till next ping\r
+        $this->lastping = time();\r
+        $this->pingInterval = 50;\r
+        return true;\r
+    }\r
+}\r