]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Adaptation of library almost complete.
authorLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Tue, 15 Jun 2010 19:51:04 +0000 (20:51 +0100)
committerLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Tue, 15 Jun 2010 19:51:04 +0000 (20:51 +0100)
Bot now signs in correctly when launched using startdaemons.sh

plugins/Msn/MsnPlugin.php
plugins/Msn/extlib/phpmsnclass/msn.class.php
plugins/Msn/msnmanager.php

index 9a9e703986979e330958132eefd392aeba107801..8452f152205cc53771a9b66dae7d5f1325ca33ac 100644 (file)
@@ -91,11 +91,8 @@ class MsnPlugin extends ImPlugin {
      */\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
+        //if(preg_match('/^[a-z]\w{2,15}$/i', $screenname)) {\r
+        return true;\r
     }\r
 \r
     /**\r
@@ -109,7 +106,7 @@ class MsnPlugin extends ImPlugin {
         $dir = dirname(__FILE__);\r
 \r
         switch ($cls) {\r
-            case 'Msn':\r
+            case 'MSN':\r
                 require_once(INSTALLDIR.'/plugins/Msn/extlib/phpmsnclass/msn.class.php');\r
                 return false;\r
             case 'MsnManager':\r
@@ -179,12 +176,14 @@ class MsnPlugin extends ImPlugin {
     }\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
+        $versions[] = array(\r
+            '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
+        );\r
         return true;\r
     }\r
 }\r
index 9b087ac792a5d3e98016b1a99affc7b3489eeaf0..6146bd1c5a36e65523e98adda9da614548047f0c 100644 (file)
@@ -37,7 +37,7 @@ class MSN {
     private $update_pending;\r
     private $PhotoStickerFile=false;\r
     private $Emotions=false;\r
-    private $ReqSBXFRTimeout=60;\r
+    private $XFRReqTimeout=60;\r
     private $LastPing;\r
     private $ping_wait=50;\r
     private $SBIdleTimeout=10;\r
@@ -47,7 +47,6 @@ class MSN {
     private $ABAuthHeader;\r
     private $ABService;\r
     private $Contacts;\r
-    private $IgnoreList;\r
 \r
     private $server = 'messenger.hotmail.com';\r
     private $port = 1863;\r
@@ -81,10 +80,6 @@ class MSN {
 \r
     public $oim_try = 3;\r
 \r
-    public $log_file = '';\r
-\r
-    public $log_path = false;\r
-\r
     public $font_fn = 'Arial';\r
     public $font_co = '333333';\r
     public $font_ef = '';\r
@@ -100,8 +95,27 @@ class MSN {
 \r
     private $aContactList = array();\r
     private $aADL = array();\r
+\r
+    /**\r
+    * Holds session information indexed by screenname if\r
+    * session has no socket or socket if socket present\r
+    *\r
+    * @var array\r
+    */\r
     private $switchBoardSessions = array();\r
-    private $switchBoardSockets = array();\r
+\r
+    /**\r
+    * Holds sockets indexed by screenname\r
+    *\r
+    * @var array\r
+    */\r
+    private $switchBoardSessionLookup = array();\r
+\r
+    /**\r
+    * Holds references to sessions waiting for XFR\r
+    *\r
+    * @var array\r
+    */\r
     private $waitingForXFR = array();\r
 \r
     /**\r
@@ -127,8 +141,7 @@ class MSN {
     * @param integer $client_id Client id (hexadecimal)\r
     * @return MSN\r
     */\r
-    public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)\r
-    {\r
+    public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C) {\r
         $this->user = $Configs['user'];\r
         $this->password = $Configs['password'];\r
         $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';\r
@@ -137,7 +150,7 @@ class MSN {
         $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;\r
         $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;\r
 \r
-        if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false) {\r
+        if ($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false) {\r
             foreach($this->Emotions as $EmotionFilePath)\r
                 $this->MsnObj($EmotionFilePath,$Type=2);\r
         }\r
@@ -169,36 +182,31 @@ class MSN {
         $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));\r
     }\r
 \r
-    private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)\r
-    {\r
-        $ArrayString='';\r
-        foreach($Array as $Key => $Val)\r
-        {\r
-            if($Key{0}==':') continue;\r
-            $Attrib='';\r
-            if(is_array($Val[':']))\r
-            {\r
-                foreach($Val[':'] as $AttribName => $AttribVal)\r
-                $Attrib.=" $AttribName='$AttribVal'";\r
+    private function Array2SoapVar($Array, $ReturnSoapVarObj = true, $TypeName = null, $TypeNameSpace = null) {\r
+        $ArrayString = '';\r
+        foreach($Array as $Key => $Val) {\r
+            if ($Key{0} == ':') continue;\r
+            $Attrib = '';\r
+            if (is_array($Val[':'])) {\r
+                foreach ($Val[':'] as $AttribName => $AttribVal)\r
+                $Attrib .= " $AttribName='$AttribVal'";\r
             }\r
-            if($Key{0}=='!')\r
-            {\r
+            if ($Key{0} == '!') {\r
                 //List Type Define\r
-                $Key=substr($Key,1);\r
-                foreach($Val as $ListKey => $ListVal)\r
-                {\r
-                    if($ListKey{0}==':') continue;\r
-                    if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);\r
-                    elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';\r
-                    $ArrayString.="<$Key$Attrib>$ListVal</$Key>";\r
+                $Key = substr($Key,1);\r
+                foreach ($Val as $ListKey => $ListVal) {\r
+                    if ($ListKey{0} == ':') continue;\r
+                    if (is_array($ListVal)) $ListVal = $this->Array2SoapVar($ListVal, false);\r
+                    elseif (is_bool($ListVal)) $ListVal = $ListVal ? 'true' : 'false';\r
+                    $ArrayString .= "<$Key$Attrib>$ListVal</$Key>";\r
                 }\r
                 continue;\r
             }\r
-            if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);\r
-            elseif(is_bool($Val)) $Val=$Val?'true':'false';\r
-            $ArrayString.="<$Key$Attrib>$Val</$Key>";\r
+            if (is_array($Val)) $Val = $this->Array2SoapVar($Val, false);\r
+            elseif (is_bool($Val)) $Val = $Val ? 'true' : 'false';\r
+            $ArrayString .= "<$Key$Attrib>$Val</$Key>";\r
         }\r
-        if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);\r
+        if ($ReturnSoapVarObj) return new SoapVar($ArrayString, XSD_ANYXML, $TypeName, $TypeNameSpace);\r
         return $ArrayString;\r
     }\r
 \r
@@ -208,8 +216,7 @@ class MSN {
     * @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
+    private function get_passport_ticket($url = '') {\r
         $user = $this->user;\r
         $password = htmlspecialchars($this->password);\r
 \r
@@ -310,8 +317,8 @@ class MSN {
 </Body>\r
 </Envelope>';\r
 \r
-        $this->debug_message("*** URL: $passport_url");\r
-        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        //$this->debug_message("*** URL: $passport_url");\r
+        //$this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
         curl_setopt($curl, CURLOPT_URL, $passport_url);\r
         if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
@@ -323,33 +330,33 @@ class MSN {
         $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
+        //$this->debug_message("*** Get Result:\n$data");\r
 \r
         if ($http_code != 200) {\r
-            // sometimes, rediret to another URL\r
+            // sometimes, redirect to another URL\r
             // MSNP15\r
             //<faultcode>psf:Redirect</faultcode>\r
             //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>\r
             //<faultstring>Authentication Failure</faultstring>\r
             if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {\r
-                $this->debug_message("*** Can't get passport ticket! http code = $http_code");\r
+                $this->debug_message("*** Could not get passport ticket! http code = $http_code");\r
                 return false;\r
             }\r
             preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);\r
             if (count($matches) == 0) {\r
-                $this->debug_message("*** redirect, but can't get redirect URL!");\r
+                $this->debug_message('*** Redirected, but could not get redirect URL!');\r
                 return false;\r
             }\r
             $redirect_url = $matches[1];\r
             if ($redirect_url == $passport_url) {\r
-                $this->debug_message("*** redirect, but redirect to same URL!");\r
+                $this->debug_message('*** Redirected, but to same URL!');\r
                 return false;\r
             }\r
-            $this->debug_message("*** redirect to $redirect_url");\r
+            $this->debug_message("*** Redirected to $redirect_url");\r
             return $this->get_passport_ticket($redirect_url);\r
         }\r
 \r
-        // sometimes, rediret to another URL, also return 200\r
+        // sometimes, redirect to another URL, also return 200\r
         // MSNP15\r
         //<faultcode>psf:Redirect</faultcode>\r
         //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>\r
@@ -359,10 +366,10 @@ class MSN {
             if (count($matches) != 0) {\r
                 $redirect_url = $matches[1];\r
                 if ($redirect_url == $passport_url) {\r
-                    $this->debug_message("*** redirect, but redirect to same URL!");\r
+                    $this->debug_message('*** Redirected, but to same URL!');\r
                     return false;\r
                 }\r
-                $this->debug_message("*** redirect to $redirect_url");\r
+                $this->debug_message("*** Redirected to $redirect_url");\r
                 return $this->get_passport_ticket($redirect_url);\r
             }\r
         }\r
@@ -397,7 +404,7 @@ class MSN {
 \r
         // no ticket found!\r
         if (count($matches) == 0) {\r
-            $this->debug_message("*** Can't get passport ticket!");\r
+            $this->debug_message('*** Could not get passport ticket!');\r
             return false;\r
         }\r
 \r
@@ -437,94 +444,100 @@ class MSN {
             'space_ticket' => html_entity_decode($matches[11]),\r
             'storage_ticket' => html_entity_decode($matches[13])\r
         );\r
-        $this->ticket=$aTickets;\r
-        $this->debug_message(var_export($aTickets, true));\r
-        $ABAuthHeaderArray=array(\r
-            'ABAuthHeader'=>array(\r
-                ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
-                'ManagedGroupRequest'=>false,\r
-                'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),\r
+        $this->ticket = $aTickets;\r
+        //$this->debug_message(var_export($aTickets, true));\r
+        $ABAuthHeaderArray = array(\r
+            'ABAuthHeader' => array(\r
+                ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),\r
+                'ManagedGroupRequest' => false,\r
+                'TicketToken' => htmlspecialchars($this->ticket['contact_ticket']),\r
             )\r
         );\r
-        $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));\r
+        $this->ABAuthHeader = new SoapHeader('http://www.msn.com/webservices/AddressBook', 'ABAuthHeader', $this->Array2SoapVar($ABAuthHeaderArray));\r
         return $aTickets;\r
     }\r
 \r
-    private function UpdateContacts()\r
-    {\r
-        $ABApplicationHeaderArray=array(\r
-            'ABApplicationHeader'=>array(\r
-                ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
-                'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',\r
-                'IsMigration'=>false,\r
-                'PartnerScenario'=>'ContactSave'\r
+    /**\r
+    * Fetch contact list\r
+    *\r
+    * @return boolean true on success\r
+    */\r
+    private function UpdateContacts() {\r
+        $ABApplicationHeaderArray = array(\r
+            'ABApplicationHeader' => array(\r
+                ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),\r
+                'ApplicationId' => 'CFE80F9D-180F-4399-82AB-413F33A1FA11',\r
+                'IsMigration' => false,\r
+                'PartnerScenario' => 'ContactSave'\r
              )\r
         );\r
 \r
-        $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));\r
-        $ABFindAllArray=array(\r
-            'ABFindAll'=>array(\r
-                ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
-                'abId'=>'00000000-0000-0000-0000-000000000000',\r
-                'abView'=>'Full',\r
-                'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',\r
+        $ABApplicationHeader = new SoapHeader('http://www.msn.com/webservices/AddressBook', 'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));\r
+        $ABFindAllArray = array(\r
+            'ABFindAll' => array(\r
+                ':' => array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
+                'abId' => '00000000-0000-0000-0000-000000000000',\r
+                'abView' => 'Full',\r
+                'lastChange' => '0001-01-01T00:00:00.0000000-08:00',\r
             )\r
         );\r
-        $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');\r
-        $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));\r
-        $this->Contacts=array();\r
-        try\r
-        {\r
-            $this->debug_message("*** Update Contacts...");\r
-            $Result=$this->ABService->ABFindAll($ABFindAll);\r
-            $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());\r
+        $ABFindAll = new SoapParam($this->Array2SoapVar($ABFindAllArray), 'ABFindAll');\r
+        $this->ABService->__setSoapHeaders(array($ABApplicationHeader, $this->ABAuthHeader));\r
+        $this->Contacts = array();\r
+        try {\r
+            $this->debug_message('*** Updating Contacts...');\r
+            $Result = $this->ABService->ABFindAll($ABFindAll);\r
+            $this->debug_message("*** Result:\n".print_r($Result, true)."\n".$this->ABService->__getLastResponse());\r
             foreach($Result->ABFindAllResult->contacts->Contact as $Contact)\r
-                $this->Contacts[$Contact->contactInfo->passportName]=$Contact;\r
-        }\r
-        catch(Exception $e)\r
-        {\r
+                $this->Contacts[$Contact->contactInfo->passportName] = $Contact;\r
+        } catch(Exception $e) {\r
             $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
             return false;\r
         }\r
         return true;\r
     }\r
 \r
-    private function addContact($email, $network, $display = '', $sendADL = false)\r
-    {\r
+    /**\r
+    * Add contact\r
+    *\r
+    * @param string $email\r
+    * @param integer $network\r
+    * @param string $display\r
+    * @param boolean $sendADL\r
+    * @return boolean true on success\r
+    */\r
+    private function addContact($email, $network, $display = '', $sendADL = false) {\r
         if ($network != 1) return true;\r
-        if(isset($this->Contacts[$email])) return true;\r
-\r
-        $ABContactAddArray=array(\r
-            'ABContactAdd'=>array(\r
-                ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
-                'abId'=>'00000000-0000-0000-0000-000000000000',\r
-                'contacts'=>array(\r
-                    'Contact'=>array(\r
-                        ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
-                        'contactInfo'=>array(\r
-                            'contactType'=>'LivePending',\r
-                            'passportName'=>$email,\r
-                            'isMessengerUser'=>true,\r
-                            'MessengerMemberInfo'=>array(\r
-                                'DisplayName'=>$email\r
+        if (isset($this->Contacts[$email])) return true;\r
+\r
+        $ABContactAddArray = array(\r
+            'ABContactAdd' => array(\r
+                ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),\r
+                'abId' => '00000000-0000-0000-0000-000000000000',\r
+                'contacts' => array(\r
+                    'Contact' => array(\r
+                        ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),\r
+                        'contactInfo' => array(\r
+                            'contactType' => 'LivePending',\r
+                            'passportName' => $email,\r
+                            'isMessengerUser' => true,\r
+                            'MessengerMemberInfo' => array(\r
+                                'DisplayName' => $email\r
                             )\r
                         )\r
                     )\r
                 ),\r
-                'options'=>array(\r
-                    'EnableAllowListManagement'=>true\r
+                'options' => array(\r
+                    'EnableAllowListManagement' => true\r
                 )\r
             )\r
         );\r
-        $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');\r
-        try\r
-        {\r
-            $this->debug_message("*** Add Contacts $email...");\r
+        $ABContactAdd = new SoapParam($this->Array2SoapVar($ABContactAddArray), 'ABContactAdd');\r
+        try {\r
+            $this->debug_message("*** Adding Contact $email...");\r
             $this->ABService->ABContactAdd($ABContactAdd);\r
-        }\r
-        catch(Exception $e)\r
-        {\r
-            $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
+        } catch(Exception $e) {\r
+            $this->debug_message("*** Add Contact Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
             return false;\r
         }\r
         if ($sendADL && !feof($this->NSfp)) {\r
@@ -533,6 +546,7 @@ class MSN {
                 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';\r
                 $len = strlen($str);\r
                 // NS: >>> ADL {id} {size}\r
+                //TODO introduce error checking\r
                 $this->ns_writeln("ADL $this->id $len");\r
                 $this->ns_writedata($str);\r
             }\r
@@ -541,8 +555,15 @@ class MSN {
         return true;\r
     }\r
 \r
-    function delMemberFromList($memberID, $email, $network, $list)\r
-    {\r
+    /**\r
+    * Remove contact from list\r
+    *\r
+    * @param integer $memberID\r
+    * @param string $email\r
+    * @param integer $network\r
+    * @param string $list\r
+    */\r
+    function delMemberFromList($memberID, $email, $network, $list) {\r
         if ($network != 1 && $network != 32) return true;\r
         if ($memberID === false) return true;\r
         $user = $email;\r
@@ -632,8 +653,8 @@ class MSN {
             'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
         );\r
 \r
-        $this->debug_message("*** URL: $this->delmember_url");\r
-        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        //$this->debug_message("*** URL: $this->delmember_url");\r
+        //$this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
         curl_setopt($curl, CURLOPT_URL, $this->delmember_url);\r
         curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
@@ -646,29 +667,35 @@ class MSN {
         $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
+        //$this->debug_message("*** Get Result:\n$data");\r
 \r
         if ($http_code != 200) {\r
             preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
             if (count($matches) == 0) {\r
-                $this->debug_message("*** can't delete member (network: $network) $email ($memberID) to $list");\r
+                $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list 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->debug_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");\r
+                $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list, error code: $faultcode, $faultstring");\r
                 return false;\r
             }\r
-            $this->debug_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
+            $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list, not present in list");\r
             return true;\r
         }\r
-        $this->debug_message("*** delete member (network: $network) $email ($memberID) from $list");\r
+        $this->debug_message("*** Member successfully deleted (network: $network) $email ($memberID) from $list list");\r
         return true;\r
     }\r
 \r
-    function addMemberToList($email, $network, $list)\r
-    {\r
+    /**\r
+    * Add contact to list\r
+    *\r
+    * @param string $email\r
+    * @param integer $network\r
+    * @param string $list\r
+    */\r
+    function addMemberToList($email, $network, $list) {\r
         if ($network != 1 && $network != 32) return true;\r
         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
         $user = $email;\r
@@ -763,8 +790,8 @@ class MSN {
             'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
         );\r
 \r
-        $this->debug_message("*** URL: $this->addmember_url");\r
-        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        //$this->debug_message("*** URL: $this->addmember_url");\r
+        //$this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
         curl_setopt($curl, CURLOPT_URL, $this->addmember_url);\r
         curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
@@ -777,29 +804,33 @@ class MSN {
         $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
+        //$this->debug_message("*** Get Result:\n$data");\r
 \r
         if ($http_code != 200) {\r
             preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
             if (count($matches) == 0) {\r
-                $this->debug_message("*** can't add member (network: $network) $email to $list");\r
+                $this->debug_message("*** Could not add member (network: $network) $email to $list 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->debug_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");\r
+                $this->debug_message("*** Could not add member (network: $network) $email to $list list, error code: $faultcode, $faultstring");\r
                 return false;\r
             }\r
-            $this->debug_message("*** add member (network: $network) $email to $list, already exist!");\r
+            $this->debug_message("*** Could not add member (network: $network) $email to $list list, already present");\r
             return true;\r
         }\r
-        $this->debug_message("*** add member (network: $network) $email to $list");\r
+        $this->debug_message("*** Member successfully added (network: $network) $email to $list list");\r
         return true;\r
     }\r
 \r
-    function getMembershipList($returnData=false)\r
-    {\r
+    /**\r
+    * Get membership lists\r
+    *\r
+    * @param mixed $returnData Membership list or false on failure\r
+    */\r
+    function getMembershipList($returnData = false) {\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
@@ -836,8 +867,8 @@ class MSN {
             'Content-Type: text/xml; charset=utf-8',\r
             'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
         );\r
-        $this->debug_message("*** URL: $this->membership_url");\r
-        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        //$this->debug_message("*** URL: $this->membership_url");\r
+        //$this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
         curl_setopt($curl, CURLOPT_URL, $this->membership_url);\r
         curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
@@ -850,8 +881,8 @@ class MSN {
         $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
-        if($http_code != 200) return false;\r
+        //$this->debug_message("*** Get Result:\n$data");\r
+        if ($http_code != 200) return false;\r
         $p = $data;\r
         $aMemberships = array();\r
         while (1) {\r
@@ -924,7 +955,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->debug_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
+                    $this->debug_message("*** Adding new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
                 }\r
             }\r
         }\r
@@ -933,6 +964,7 @@ class MSN {
 \r
     /**\r
      * Connect to the NS server\r
+     *\r
      * @param String $user Username\r
      * @param String $password Password\r
      * @param String $redirect_server Redirect server\r
@@ -942,21 +974,19 @@ class MSN {
     private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {\r
         $this->id = 1;\r
         if ($redirect_server === '') {\r
-            $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);\r
+            $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);\r
             if (!$this->NSfp) {\r
-                $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";\r
+                $this->error = "!!! Could not connect to $this->server:$this->port, error => $errno, $errstr";\r
                 return false;\r
             }\r
         }\r
         else {\r
-            $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);\r
+            $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, $this->timeout);\r
             if (!$this->NSfp) {\r
-                $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";\r
+                $this->error = "!!! Could not connect to $redirect_server:$redirect_port, error => $errno, $errstr";\r
                 return false;\r
             }\r
         }\r
-\r
-        stream_set_timeout($this->NSfp, $this->timeout);\r
         $this->authed = false;\r
         // MSNP9\r
         // NS: >> VER {id} MSNP9 CVR0\r
@@ -965,8 +995,7 @@ class MSN {
         $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
 \r
         $start_tm = time();\r
-        while (!self::socketcheck($this->NSfp))\r
-        {\r
+        while (!self::socketcheck($this->NSfp)) {\r
             $data = $this->ns_readln();\r
             // no data?\r
             if ($data === false) {\r
@@ -975,7 +1004,6 @@ class MSN {
                 $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
@@ -1024,7 +1052,6 @@ class MSN {
                         $this->ns_writeln("OUT");\r
                         @fclose($this->NSfp);\r
                         $this->error = 'Passport authenticated fail!';\r
-                        $this->debug_message("*** $this->error");\r
                         return false;\r
                     }\r
 \r
@@ -1045,19 +1072,17 @@ class MSN {
                     // MSNP15\r
                     // NS: <<< XFR {id} NS {server} U D\r
                     @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);\r
-                    if($Type!='NS') break;\r
+                    if ($Type!='NS') break;\r
                     @list($ip, $port) = @explode(':', $server);\r
                     // this connection will close after XFR\r
                     @fclose($this->NSfp);\r
 \r
-                    $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
+                    $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, $this->timeout);\r
                     if (!$this->NSfp) {\r
                         $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";\r
-                        $this->debug_message("*** $this->error");\r
                         return false;\r
                     }\r
 \r
-                    stream_set_timeout($this->NSfp, $this->timeout);\r
                     // MSNP9\r
                     // NS: >> VER {id} MSNP9 CVR0\r
                     // MSNP15\r
@@ -1071,7 +1096,7 @@ class MSN {
                     @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);\r
                     // we don't need the data, just read it and drop\r
                     if (is_numeric($size) && $size > 0)\r
-                    $this->ns_readdata($size);\r
+                        $this->ns_readdata($size);\r
                     break;\r
 \r
                 default:\r
@@ -1082,7 +1107,6 @@ class MSN {
                         $this->ns_writeln("OUT");\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
                     }\r
                     // unknown response from server, just ignore it\r
@@ -1098,23 +1122,26 @@ class MSN {
      * @return void\r
      */\r
     public function signon() {\r
-        $this->debug_message("*** try to connect to MSN network");\r
-\r
-        while(true) {\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
-                $this->signonFailure('!!! Update contacts failed');\r
+        /* FIXME Don't implement the signon as a loop or we could hang\r
+        *        the queue handler! */\r
+        $this->debug_message('*** Trying to connect to MSN network');\r
+\r
+        while (true) {\r
+            // Connect\r
+            if (!$this->connect($this->user, $this->password)) {\r
+                $this->signonFailure("!!! Could not connect to server: $this->error");\r
                 continue;\r
             }\r
-            $this->LastPing=time();\r
-            $start_tm = time();\r
-            $ping_tm = time();\r
-            if(($this->aContactList = $this->getMembershipList()) === false) {\r
+\r
+            // Update contacts\r
+            if ($this->UpdateContacts() === false) continue;\r
+\r
+            // Get membership lists\r
+            if (($this->aContactList = $this->getMembershipList()) === false) {\r
                 $this->signonFailure('!!! Get membership list failed');\r
                 continue;\r
             }\r
+\r
             if ($this->update_pending) {\r
                 if (is_array($this->aContactList)) {\r
                     $pending = 'Pending';\r
@@ -1206,15 +1233,15 @@ class MSN {
             //$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
+            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
-            if(!socketcheck($this->NSfp)) {\r
-                $this->debug_message("*** connected, wait for command");\r
+            if (!self::socketcheck($this->NSfp)) {\r
+                $this->debug_message('*** Connected, waiting for commands');\r
                 break;\r
             } else {\r
                 $this->NSRetryWait($this->retry_wait);\r
@@ -1262,10 +1289,15 @@ class MSN {
         return base64_encode($blob);\r
     }\r
 \r
+    /**\r
+    * Get OIM mail data\r
+    *\r
+    * @return string mail data or false on failure\r
+    */\r
     function getOIM_maildata() {\r
         preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);\r
         if (count($matches) == 0) {\r
-            $this->debug_message('*** no web ticket?');\r
+            $this->debug_message('*** No web ticket?');\r
             return false;\r
         }\r
         $t = htmlspecialchars($matches[1]);\r
@@ -1291,8 +1323,8 @@ class MSN {
             '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_maildata_url");\r
-        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        //$this->debug_message("*** URL: $this->oim_maildata_url");\r
+        //$this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
         curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);\r
         curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
@@ -1305,26 +1337,32 @@ class MSN {
         $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
+        //$this->debug_message("*** Get Result:\n$data");\r
 \r
         if ($http_code != 200) {\r
-            $this->debug_message("*** Can't get OIM maildata! http code: $http_code");\r
+            $this->debug_message("*** Could not get OIM maildata! http code: $http_code");\r
             return false;\r
         }\r
 \r
         // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>\r
         preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);\r
         if (count($matches) == 0) {\r
-            $this->debug_message("*** Can't get OIM maildata");\r
-            return '';\r
+            $this->debug_message('*** Could not get OIM maildata');\r
+            return false;\r
         }\r
         return $matches[2];\r
     }\r
 \r
+    /**\r
+    * Fetch OIM message with given id\r
+    *\r
+    * @param string $msgid\r
+    * @return string Message or false on failure\r
+    */\r
     function getOIM_message($msgid) {\r
         preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);\r
         if (count($matches) == 0) {\r
-            $this->debug_message('*** no web ticket?');\r
+            $this->debug_message('*** No web ticket?');\r
             return false;\r
         }\r
         $t = htmlspecialchars($matches[1]);\r
@@ -1355,8 +1393,8 @@ class MSN {
             '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
+        //$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
@@ -1369,7 +1407,7 @@ class MSN {
         $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
+        //$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
@@ -1403,7 +1441,7 @@ class MSN {
             $sOIM .= $line;\r
         }\r
         $sMsg = base64_decode($sOIM);\r
-        $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
+        //$this->debug_message("*** we get OIM ($msgid): $sMsg");\r
 \r
         // delete OIM\r
         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
@@ -1431,8 +1469,8 @@ class MSN {
             '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
+        //$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
@@ -1445,15 +1483,20 @@ class MSN {
         $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
+        //$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
+            $this->debug_message("*** Could not delete OIM: $msgid, http code = $http_code");\r
         else\r
             $this->debug_message("*** OIM ($msgid) deleted");\r
         return $sMsg;\r
     }\r
 \r
+    /**\r
+    * Log out and close the NS connection\r
+    *\r
+    * @return void\r
+    */\r
     private function NSLogout() {\r
         if (is_resource($this->NSfp) && !feof($this->NSfp)) {\r
             // logout now\r
@@ -1461,18 +1504,27 @@ class MSN {
             $this->ns_writeln("OUT");\r
             fclose($this->NSfp);\r
             $this->NSfp = false;\r
-            $this->debug_message("*** logout now!");\r
+            $this->debug_message("*** Logged out");\r
         }\r
-\r
     }\r
 \r
+    /**\r
+    * Sleep for the given number of seconds\r
+    *\r
+    * @param integer $wait Number of seconds to sleep for\r
+    */\r
     private function NSRetryWait($wait) {\r
-        $this->debug_message("*** wait for $Wait seconds");\r
+        $this->debug_message("*** Sleeping for $wait seconds before retrying");\r
         sleep($wait);\r
     }\r
 \r
-    function getChallenge($code)\r
-    {\r
+    /**\r
+    * Generate challenge response\r
+    *\r
+    * @param string $code\r
+    * @return string challenge response code\r
+    */\r
+    function getChallenge($code) {\r
         // MSNP15\r
         // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges\r
         // Step 1: The MD5 Hash\r
@@ -1546,514 +1598,23 @@ class MSN {
         return $hash;\r
     }\r
 \r
-    private function getMessage($sMessage, $network = 1)\r
-    {\r
+    /**\r
+    * Generate the data to send a message\r
+    *\r
+    * @param string $sMessage Message\r
+    * @param integer $network Network\r
+    */\r
+    private function getMessage($sMessage, $network = 1) {\r
         $msg_header = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nX-MMS-IM-Format: FN=$this->font_fn; EF=$this->font_ef; CO=$this->font_co; CS=0; PF=22\r\n\r\n";\r
         $msg_header_len = strlen($msg_header);\r
         if ($network == 1)\r
             $maxlen = $this->max_msn_message_len - $msg_header_len;\r
         else\r
             $maxlen = $this->max_yahoo_message_len - $msg_header_len;\r
-        $sMessage=str_replace("\r", '', $sMessage);\r
-        $msg=substr($sMessage,0,$maxlen);\r
+        $sMessage = str_replace("\r", '', $sMessage);\r
+        $msg = substr($sMessage, 0, $maxlen);\r
         return $msg_header.$msg;\r
     }\r
-    /**\r
-     *\r
-     * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息\r
-     * @param $Param\r
-     * @return boolean\r
-     */\r
-    private function DoSwitchBoard($Action,$Param)\r
-    {\r
-        $SessionEnd=false;\r
-        $Joined=false;\r
-        $this->id=1;\r
-        $LastActive=time();\r
-        stream_set_timeout($this->SBfp, $this->SBStreamTimeout);\r
-        switch($Action)\r
-        {\r
-            case 'Active':\r
-                $cki_code=$Param['cki'];\r
-                $user=$Param['user'];\r
-                $this->SwitchBoardMessageQueue=$Param['Msg'];\r
-                // SB: >>> USR {id} {user} {cki}\r
-                $this->SB_writeln("USR $this->id $this->user $cki_code");\r
-                $this->SwitchBoardSessionUser=$user;\r
-                break;\r
-            case 'Passive':\r
-                $ticket=$Param['ticket'];\r
-                $sid=$Param['sid'];\r
-                $user=$Param['user'];\r
-                // SB: >>> ANS {id} {user} {ticket} {session_id}\r
-                $this->SB_writeln("ANS $this->id $this->user $ticket $sid");\r
-                $this->SwitchBoardSessionUser=$user;\r
-                break;\r
-            default:\r
-                return false;\r
-        }\r
-        while((!feof($this->SBfp))&&(!$SessionEnd))\r
-        {\r
-            $data = $this->SB_readln();\r
-            if($this->kill_me)\r
-            {\r
-                $this->debug_message("*** SB Okay, kill me now!");\r
-                break;\r
-            }\r
-            if($data === false)\r
-            {\r
-                if(time()-$LastActive > $this->SBIdleTimeout)\r
-                {\r
-                    $this->debug_message("*** SB Idle Timeout!");\r
-                    break;\r
-                }\r
-                if(!$Joined) continue;\r
-                foreach($this->SwitchBoardMessageQueue as $Message)\r
-                {\r
-                    if($Message=='') continue;\r
-                    $aMessage = $this->getMessage($Message);\r
-                    //CheckEmotion...\r
-                    $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
-                    if($MsnObjDefine!=='')\r
-                    {\r
-                        $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";\r
-                        $len = strlen($SendString);\r
-                        $this->SB_writeln("MSG $this->id N $len");\r
-                        $this->SB_writedata($SendString);\r
-                        $this->id++;\r
-                    }\r
-                    $len = strlen($aMessage);\r
-                    $this->SB_writeln("MSG $this->id N $len");\r
-                    $this->SB_writedata($aMessage);\r
-                }\r
-                $this->SwitchBoardMessageQueue=array();\r
-                if(!$this->IsIgnoreMail($user)) $LastActive = time();\r
-                continue;\r
-            }\r
-            $code = substr($data, 0, 3);\r
-            switch($code)\r
-            {\r
-                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->debug_message("*** $email join us");\r
-                    $Joined=true;\r
-                    break;\r
-                case 'BYE':\r
-                    $this->debug_message("*** Quit for BYE");\r
-                    $SessionEnd=true;\r
-                    break;\r
-                case 'USR':\r
-                    // SB: <<< USR {id} OK {user} {alias}\r
-                    // we don't need the data, just ignore it\r
-                    // request user to join this switchboard\r
-                    // SB: >>> CAL {id} {user}\r
-                    $this->SB_writeln("CAL $this->id $user");\r
-                    break;\r
-                case 'CAL':\r
-                    // SB: <<< CAL {id} RINGING {?}\r
-                    // we don't need this, just ignore, and wait for other response\r
-                    $this->id++;\r
-                    break;\r
-                case 'JOI':\r
-                    // SB: <<< JOI {user} {alias} {clientid?}\r
-                    // someone join us\r
-                    // we don't need the data, just ignore it\r
-                    // no more user here\r
-                    $Joined=true;\r
-                    break;\r
-                case 'MSG':\r
-                    // SB: <<< MSG {email} {alias} {len}\r
-                    @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);\r
-                    $len = trim($len);\r
-                    $data = $this->SB_readdata($len);\r
-                    $aLines = @explode("\n", $data);\r
-                    $header = true;\r
-                    $ignore = false;\r
-                    $is_p2p = false;\r
-                    $sMsg = '';\r
-                    foreach ($aLines as $line)\r
-                    {\r
-                        $line = rtrim($line);\r
-                        if ($header) {\r
-                            if ($line === '') {\r
-                                $header = false;\r
-                                continue;\r
-                            }\r
-                            if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
-                                // typing notification, just ignore\r
-                                $ignore = true;\r
-                                break;\r
-                            }\r
-                            if (strncasecmp($line, 'Chunk:', 6) == 0) {\r
-                                // we don't handle any split message, just ignore\r
-                                $ignore = true;\r
-                                break;\r
-                            }\r
-                            if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {\r
-                                // p2p message, ignore it, but we need to send acknowledgement for it...\r
-                                $is_p2p = true;\r
-                                $p = strstr($data, "\n\n");\r
-                                $sMsg = '';\r
-                                if ($p === false) {\r
-                                    $p = strstr($data, "\r\n\r\n");\r
-                                    if ($p !== false)\r
-                                    $sMsg = substr($p, 4);\r
-                                }\r
-                                else\r
-                                $sMsg = substr($p, 2);\r
-                                break;\r
-                            }\r
-                            if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {\r
-                                // ignore all application/x-... message\r
-                                // for example:\r
-                                //      application/x-ms-ink        => ink message\r
-                                $ignore = true;\r
-                                break;\r
-                            }\r
-                            if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {\r
-                                // ignore all text/x-... message\r
-                                // for example:\r
-                                //      text/x-msnmsgr-datacast         => nudge, voice clip....\r
-                                //      text/x-mms-animemoticon         => customized animemotion word\r
-                                $ignore = true;\r
-                                break;\r
-                            }\r
-                            continue;\r
-                        }\r
-                        if ($sMsg !== '')\r
-                        $sMsg .= "\n";\r
-                        $sMsg .= $line;\r
-                    }\r
-                    if ($ignore)\r
-                    {\r
-                        $this->debug_message("*** ingnore from $from_email: $line");\r
-                        break;\r
-                    }\r
-                    if ($is_p2p)\r
-                    {\r
-                        // we will ignore any p2p message after sending acknowledgement\r
-                        $ignore = true;\r
-                        $len = strlen($sMsg);\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->debug_message("*** p2p: size error, less than 52!");\r
-                            break;\r
-                        }*/\r
-                        $aDwords = @unpack("V12dword", $sMsg);\r
-                        if (!is_array($aDwords)) {\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
-                        $hdr_SessionID = $aDwords['dword1'];\r
-                        $hdr_Identifier = $aDwords['dword2'];\r
-                        $hdr_DataOffsetLow = $aDwords['dword3'];\r
-                        $hdr_DataOffsetHigh = $aDwords['dword4'];\r
-                        $hdr_TotalDataSizeLow = $aDwords['dword5'];\r
-                        $hdr_TotalDataSizeHigh = $aDwords['dword6'];\r
-                        $hdr_MessageLength = $aDwords['dword7'];\r
-                        $hdr_Flag = $aDwords['dword8'];\r
-                        $hdr_AckID = $aDwords['dword9'];\r
-                        $hdr_AckUID = $aDwords['dword10'];\r
-                        $hdr_AckSizeLow = $aDwords['dword11'];\r
-                        $hdr_AckSizeHigh = $aDwords['dword12'];\r
-                        $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");\r
-                        $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");\r
-                        $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");\r
-                        $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");\r
-                        $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");\r
-                        $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");\r
-                        $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");\r
-                        $this->debug_message("*** p2p: header Flag = $hdr_Flag");\r
-                        $this->debug_message("*** p2p: header AckID = $hdr_AckID");\r
-                        $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");\r
-                        $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");\r
-                        $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");\r
-                        if($hdr_Flag==2) {\r
-                            //This is an ACK from SB ignore....\r
-                            $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");\r
-                            break;\r
-                        }\r
-                        $MsgBody=$this->linetoArray(substr($sMsg,48,-4));\r
-                        $this->debug_message("*** p2p: body".print_r($MsgBody,true));\r
-                        if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))\r
-                        {\r
-                            while(true)\r
-                            {\r
-                                if($this->SB_readln()===false) break;\r
-                            }\r
-                            $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));\r
-                            preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);\r
-                            $BranchGUID=$Matches[1];\r
-                            //it's an invite to send a display picture.\r
-                            $new_id = ~$hdr_Identifier;\r
-                            $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
-                            $new_id,\r
-                            0, 0,\r
-                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
-                            0,\r
-                            2,\r
-                            $hdr_Identifier,\r
-                            $hdr_AckID,\r
-                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);\r
-                            $footer = pack("L", 0);\r
-                            $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
-                            $len = strlen($message);\r
-                            $this->SB_writeln("MSG $this->id D $len");\r
-                            $this->SB_writedata($message);\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
-                            $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);\r
-                            $MessagePayload=\r
-                                "MSNSLP/1.0 200 OK\r\n".\r
-                                "To: <msnmsgr:".$from_email.">\r\n".\r
-                                "From: <msnmsgr:".$this->user.">\r\n".\r
-                                "Via: ".$MsgBody['Via']."\r\n".\r
-                                "CSeq: ".($MsgBody['CSeq']+1)."\r\n".\r
-                                "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
-                                "Max-Forwards: 0\r\n".\r
-                                "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".\r
-                                "Content-Length: ".strlen($MessageContent)."\r\n\r\n".\r
-                            $MessageContent;\r
-                            $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
-                            $hdr_TotalDataSizeHigh=0;\r
-                            $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
-                            $new_id,\r
-                            0, 0,\r
-                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
-                            strlen($MessagePayload),\r
-                            0,\r
-                            rand(),\r
-                            0,\r
-                            0,0);\r
-\r
-                            $message =\r
-                                "MIME-Version: 1.0\r\n".\r
-                                "Content-Type: application/x-msnmsgrp2p\r\n".\r
-                                "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
-                            $this->SB_writeln("MSG $this->id D ".strlen($message));\r
-                            $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
-                            $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));\r
-                            //send Data preparation message\r
-                            //send 4 null bytes as data\r
-                            $hdr_TotalDataSizeLow=4;\r
-                            $hdr_TotalDataSizeHigh=0;\r
-                            $new_id++;\r
-                            $hdr = pack("LLLLLLLLLLLL",\r
-                            $MsgBody['SessionID'],\r
-                            $new_id,\r
-                            0, 0,\r
-                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
-                            $hdr_TotalDataSizeLow,\r
-                            0,\r
-                            rand(),\r
-                            0,\r
-                            0,0);\r
-                            $message =\r
-                                "MIME-Version: 1.0\r\n".\r
-                                "Content-Type: application/x-msnmsgrp2p\r\n".\r
-                                "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";\r
-                            $this->SB_writeln("MSG $this->id D ".strlen($message));\r
-                            $this->SB_writedata($message);\r
-                            $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));\r
-                            $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));\r
-                            $this->SB_readln();//Read ACK;\r
-\r
-                            //send Data Content..\r
-                            $footer=pack('N',1);\r
-                            $new_id++;\r
-                            $FileSize=filesize($PictureFilePath);\r
-                            if($hTitle=fopen($PictureFilePath,'rb'))\r
-                            {\r
-                                $Offset=0;\r
-                                //$new_id++;\r
-                                while(!feof($hTitle))\r
-                                {\r
-                                    $FileContent=fread($hTitle,1024);\r
-                                    $FileContentSize=strlen($FileContent);\r
-                                    $hdr = pack("LLLLLLLLLLLL",\r
-                                    $MsgBody['SessionID'],\r
-                                    $new_id,\r
-                                    $Offset, 0,\r
-                                    $FileSize,0,\r
-                                    $FileContentSize,\r
-                                    0x20,\r
-                                    rand(),\r
-                                    0,\r
-                                    0,0\r
-                                    );\r
-                                    $message =\r
-                                        "MIME-Version: 1.0\r\n".\r
-                                        "Content-Type: application/x-msnmsgrp2p\r\n".\r
-                                        "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";\r
-                                    $this->SB_writeln("MSG $this->id D ".strlen($message));\r
-                                    $this->SB_writedata($message);\r
-                                    $this->debug_message("*** p2p: dump send Data Content message  $Offset / $FileSize :\n".$this->dump_binary($message));\r
-                                    $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));\r
-                                    //$this->SB_readln();//Read ACK;\r
-                                    $Offset+=$FileContentSize;\r
-                                }\r
-                            }\r
-                            //Send Bye\r
-                            /*\r
-                            $MessageContent="\r\n".pack("C", 0);\r
-                            $MessagePayload=\r
-                                "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
-                                "CSeq: 0\r\n".\r
-                                "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
-                                "Max-Forwards: 0\r\n".\r
-                                "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".\r
-                                "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;\r
-                            $footer=pack('N',0);\r
-                            $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
-                            $hdr_TotalDataSizeHigh=0;\r
-                            $new_id++;\r
-                            $hdr = pack("LLLLLLLLLLLL",\r
-                            0,\r
-                            $new_id,\r
-                            0, 0,\r
-                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
-                            0,\r
-                            0,\r
-                            rand(),\r
-                            0,\r
-                            0,0);\r
-                            $message =\r
-                                        "MIME-Version: 1.0\r\n".\r
-                                        "Content-Type: application/x-msnmsgrp2p\r\n".\r
-                                        "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
-                            $this->SB_writeln("MSG $id D ".strlen($message));\r
-                            $id++;\r
-                            $this->SB_writedata($message);\r
-                            $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));\r
-                            */\r
-                            break;\r
-                        }\r
-                        //TODO:\r
-                        //if ($hdr_Flag == 2) {\r
-                        // just send ACK...\r
-                        //    $this->SB_writeln("ACK $id");\r
-                        //    break;\r
-                        //}\r
-                        if ($hdr_SessionID == 4) {\r
-                            // ignore?\r
-                            $this->debug_message("*** p2p: ignore flag 4");\r
-                            break;\r
-                        }\r
-                        $finished = false;\r
-                        if ($hdr_TotalDataSizeHigh == 0) {\r
-                            // only 32 bites size\r
-                            if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)\r
-                            $finished = true;\r
-                        }\r
-                        else {\r
-                            // we won't accept any file transfer\r
-                            // so I think we won't get any message size need to use 64 bits\r
-                            // 64 bits size here, can't count directly...\r
-                            $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);\r
-                            $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);\r
-                            $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);\r
-                            $now_size = bcadd($dataoffset, $messagelength);\r
-                            if (bccomp($now_size, $totalsize) >= 0)\r
-                            $finished = true;\r
-                        }\r
-                        if (!$finished) {\r
-                            // ignore not finished split packet\r
-                            $this->debug_message("*** p2p: ignore split packet, not finished");\r
-                            break;\r
-                        }\r
-                        //$new_id = ~$hdr_Identifier;\r
-                        /*\r
-                         $new_id++;\r
-                         $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
-                         $new_id,\r
-                         0, 0,\r
-                         $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
-                         0,\r
-                         2,\r
-                         $hdr_Identifier,\r
-                         $hdr_AckID,\r
-                         $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);\r
-                         $footer = pack("L", 0);\r
-                         $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
-                         $len = strlen($message);\r
-                         $this->SB_writeln("MSG $id D $len");\r
-                         $id++;\r
-                         $this->SB_writedata($message);\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->debug_message("*** MSG from $from_email: $sMsg");\r
-                    $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
-                    break;\r
-                case '217':\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
-                    break;\r
-                default:\r
-                    if (is_numeric($code))\r
-                    {\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("*** SB: $this->error");\r
-                        $SessionEnd=true;\r
-                    }\r
-                    break;\r
-            }\r
-            if(!$this->IsIgnoreMail($user)) $LastActive = time();\r
-        }\r
-        if (feof($this->SBfp))\r
-        {\r
-            // lost connection? error? try OIM later\r
-            @fclose($this->SBfp);\r
-            return false;\r
-        }\r
-        $this->SB_writeln("OUT");\r
-        @fclose($this->SBfp);\r
-        return true;\r
-    }\r
-    /*private function switchboard_control($ip, $port, $cki_code, $user, $Messages)\r
-    {\r
-        $this->SwitchBoardProcess=1;\r
-        $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");\r
-        $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
-        if (!$this->SBfp)\r
-        {\r
-            $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
-            return false;\r
-        }\r
-        return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));\r
-    }\r
-    private function switchboard_ring($ip, $port, $sid, $ticket,$user)\r
-    {\r
-        $this->SwitchBoardProcess=2;\r
-        $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");\r
-        $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
-        if (!$this->SBfp)\r
-        {\r
-            $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
-            return false;\r
-        }\r
-        return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));\r
-    }*/\r
 \r
     // read data for specified size\r
     private function ns_readdata($size) {\r
@@ -2098,8 +1659,8 @@ class MSN {
     private function sb_readdata($socket, $size) {\r
         $data = '';\r
         $count = 0;\r
-        while (!feof($this->SBfp)) {\r
-            $buf = @fread($this->SBfp, $size - $count);\r
+        while (!feof($socket)) {\r
+            $buf = @fread($socket, $size - $count);\r
             $data .= $buf;\r
             $count += strlen($buf);\r
             if ($count >= $size) break;\r
@@ -2137,7 +1698,7 @@ class MSN {
     // show debug information\r
     function debug_message($str) {\r
         if (!$this->debug) return;\r
-        if($this->debug===STDOUT) echo $str."\n";\r
+        if ($this->debug===STDOUT) echo $str."\n";\r
         /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';\r
         $fp = fopen($fname, 'at');\r
         if ($fp) {\r
@@ -2184,55 +1745,54 @@ class MSN {
      */\r
     private function MsnObj($FilePath,$Type=3)\r
     {\r
-        if(!($FileSize=filesize($FilePath))) return '';\r
-        $Location=md5($FilePath);\r
-        $Friendly=md5($FilePath.$Type);\r
-        if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];\r
-        $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));\r
-        $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));\r
-        $this->MsnObjArray[$Location]=$FilePath;\r
-        $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';\r
-        $this->MsnObjMap[$Location]=$MsnObj;\r
+        if (!($FileSize=filesize($FilePath))) return '';\r
+        $Location = md5($FilePath);\r
+        $Friendly = md5($FilePath.$Type);\r
+        if (isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];\r
+        $sha1d = base64_encode(sha1(file_get_contents($FilePath), true));\r
+        $sha1c = base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));\r
+        $this->MsnObjArray[$Location] = $FilePath;\r
+        $MsnObj = '<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';\r
+        $this->MsnObjMap[$Location] = $MsnObj;\r
         $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");\r
         return $MsnObj;\r
     }\r
 \r
     private function linetoArray($lines) {\r
-        $lines=str_replace("\r",'',$lines);\r
-        $lines=explode("\n",$lines);\r
-        foreach($lines as $line) {\r
-            if(!isset($line{3})) continue;\r
-            list($Key,$Val)=explode(':',$line);\r
-            $Data[trim($Key)]=trim($Val);\r
+        $lines = str_replace("\r", '', $lines);\r
+        $lines = explode("\n", $lines);\r
+        foreach ($lines as $line) {\r
+            if (!isset($line{3})) continue;\r
+            list($Key,$Val) = explode(':', $line);\r
+            $Data[trim($Key)] = trim($Val);\r
         }\r
         return $Data;\r
     }\r
 \r
-    private function GetPictureFilePath($Context)\r
-    {\r
-        $MsnObj=base64_decode($Context);\r
-        if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))\r
-        $location=$Match[1];\r
+    private function GetPictureFilePath($Context) {\r
+        $MsnObj = base64_decode($Context);\r
+        if (preg_match('/location="(.*?)"/i', $MsnObj, $Match))\r
+            $location = $Match[1];\r
         $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");\r
-        if($location&&(isset($this->MsnObjArray[$location])))\r
-        return $this->MsnObjArray[$location];\r
+        if ($location && isset($this->MsnObjArray[$location]))\r
+            return $this->MsnObjArray[$location];\r
         return false;\r
     }\r
 \r
-    private function GetMsnObjDefine($Message)\r
-    {\r
-        $DefineString='';\r
-        if(is_array($this->Emotions))\r
-        foreach($this->Emotions as $Pattern => $FilePath)\r
-        {\r
-            if(strpos($Message,$Pattern)!==false)\r
-            $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";\r
-        }\r
+    private function GetMsnObjDefine($Message) {\r
+        $DefineString = '';\r
+        if (is_array($this->Emotions))\r
+            foreach ($this->Emotions as $Pattern => $FilePath) {\r
+                if (strpos($Message, $Pattern)!==false)\r
+                $DefineString .= "$Pattern\t".$this->MsnObj($FilePath, 2)."\t";\r
+            }\r
         return $DefineString;\r
     }\r
 \r
     /**\r
      * Read and handle incoming command from NS\r
+     *\r
+     * @return void\r
      */\r
     private function nsReceive() {\r
         // Sign in again if not signed in or socket failed\r
@@ -2244,15 +1804,14 @@ class MSN {
         }\r
 \r
         $data = $this->ns_readln();\r
-        if($data === false) {\r
+        if ($data === false) {\r
             // There was no data / an error when reading from the socket so reconnect\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
+            switch (substr($data, 0, 3)) {\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
@@ -2274,67 +1833,63 @@ class MSN {
 \r
                 case 'LST':\r
                     // NS: <<< LST {email} {alias} 11 0\r
-                    @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\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->debug_message("*** add to our contact list: $u_name@$u_domain");\r
+                        $this->debug_message("*** Added to 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
+                    // randomly, we get ADL command, someone 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
+                    if (is_numeric($size) && $size > 0) {\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
+                        if (is_array($matches) && count($matches) > 0) {\r
                             $u_domain = $matches[1];\r
                             $u_name = $matches[2];\r
                             $network = $matches[4];\r
                             if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
-                                $this->debug_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
+                                $this->debug_message("*** Someone (network: $network) added 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
+                                foreach (array('Allow', 'Reverse') as $list) {\r
+                                    if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
                                         if ($re_login) {\r
-                                            $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+                                            $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
                                             continue;\r
                                         }\r
                                         $aTickets = $this->get_passport_ticket();\r
                                         if (!$aTickets || !is_array($aTickets)) {\r
                                             // failed to login? ignore it\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
+                                            $this->debug_message("*** Could not re-login, something wrong here");\r
+                                            $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
                                             continue;\r
                                         }\r
                                         $re_login = true;\r
                                         $this->ticket = $aTickets;\r
-                                        $this->debug_message("**** get new ticket, try it again");\r
-                                        if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
-                                        {\r
-                                            $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+                                        $this->debug_message("**** Got new ticket, trying again");\r
+                                        if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
+                                            $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
                                             continue;\r
                                         }\r
                                     }\r
                                     $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
                                     $cnt++;\r
                                 }\r
-                                $this->debug_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
+                                $this->debug_message("*** Someone (network: $network) added 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
+                            $this->callHandler('AddedToList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));\r
                         }\r
                         else\r
-                        $this->debug_message("*** someone add us to their list: $data");\r
-                        $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
+                            $this->debug_message("*** Someone added us to their list: $data");\r
                     }\r
                     break;\r
 \r
@@ -2342,29 +1897,29 @@ class MSN {
                     // 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
+                    if (is_numeric($size) && $size > 0) {\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
+                        if (is_array($matches) && count($matches) > 0) {\r
                             $u_domain = $matches[1];\r
                             $u_name = $matches[2];\r
                             $network = $matches[4];\r
-                            if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
-                            {\r
+                            if (isset($this->aContactList[$u_domain][$u_name][$network])) {\r
                                 $aData = $this->aContactList[$u_domain][$u_name][$network];\r
+\r
                                 foreach ($aData as $list => $id)\r
-                                $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
+                                    $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
+\r
                                 unset($this->aContactList[$u_domain][$u_name][$network]);\r
-                                $this->debug_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
+                                $this->debug_message("*** Someone (network: $network) removed us from their list: $u_name@$u_domain");\r
                             }\r
                             else\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
+                                $this->debug_message("*** Someone (network: $network) removed us from their list (but not in our list): $u_name@$u_domain");\r
+\r
+                            $this->callHandler('RemovedFromList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));\r
                         }\r
                         else\r
-                        $this->debug_message("*** someone remove us from their list: $data");\r
+                            $this->debug_message("*** Someone removed us from their list: $data");\r
                     }\r
                     break;\r
 \r
@@ -2386,8 +1941,7 @@ class MSN {
                                     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
+                                    if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false && strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
                                         // we just need text/x-msmsgsinitialmdatanotification\r
                                         // or text/x-msmsgsoimnotification\r
                                         $ignore = true;\r
@@ -2402,32 +1956,33 @@ class MSN {
                             }\r
                         }\r
                         if ($ignore) {\r
-                            $this->debug_message("*** ingnore MSG for: $line");\r
+                            $this->debug_message("*** Ignoring MSG for: $line");\r
                             break;\r
                         }\r
                         if ($maildata == '') {\r
-                            $this->debug_message("*** ingnore MSG not for OIM");\r
+                            $this->debug_message("*** Ignoring MSG not for OIM");\r
                             break;\r
                         }\r
                         $re_login = false;\r
                         if (strcasecmp($maildata, 'too-large') == 0) {\r
-                            $this->debug_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->debug_message("*** can't get mail-data via SOAP");\r
+                                $this->debug_message("*** Could not get mail-data via SOAP");\r
+\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->debug_message("*** can't re-login, something wrong here, ignore this OIM");\r
+                                    $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");\r
                                     break;\r
                                 }\r
                                 $re_login = true;\r
                                 $this->ticket = $aTickets;\r
-                                $this->debug_message("*** get new ticket, try it again");\r
+                                $this->debug_message("*** Got new ticket, trying again");\r
                                 $maildata = $this->getOIM_maildata();\r
                                 if ($maildata === false) {\r
-                                    $this->debug_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
+                                    $this->debug_message("*** Could not get mail-data via SOAP, and re-login already attempted, ignoring this OIM");\r
                                     break;\r
                                 }\r
                             }\r
@@ -2445,7 +2000,7 @@ class MSN {
                             $p = substr($p, $end);\r
                         }\r
                         if (count($aOIMs) == 0) {\r
-                            $this->debug_message("*** ingnore empty OIM");\r
+                            $this->debug_message("*** Ignoring empty OIM");\r
                             break;\r
                         }\r
                         foreach ($aOIMs as $maildata) {\r
@@ -2460,23 +2015,23 @@ class MSN {
                             // N: sender alias\r
                             preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
                             if (count($matches) == 0) {\r
-                                $this->debug_message("*** ingnore OIM maildata without <T>type</T>");\r
+                                $this->debug_message("*** Ignoring OIM maildata without <T>type</T>");\r
                                 continue;\r
                             }\r
                             $oim_type = $matches[1];\r
                             if ($oim_type = 13)\r
-                            $network = 32;\r
+                                $network = 32;\r
                             else\r
-                            $network = 1;\r
+                                $network = 1;\r
                             preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
                             if (count($matches) == 0) {\r
-                                $this->debug_message("*** ingnore OIM maildata without <E>sender</E>");\r
+                                $this->debug_message("*** Ignoring 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->debug_message("*** ingnore OIM maildata without <I>msgid</I>");\r
+                                $this->debug_message("*** Ignoring OIM maildata without <I>msgid</I>");\r
                                 continue;\r
                             }\r
                             $oim_msgid = $matches[1];\r
@@ -2484,18 +2039,18 @@ 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->debug_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
+                            $this->debug_message("*** OIM received from $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
                             $sMsg = $this->getOIM_message($oim_msgid);\r
                             if ($sMsg === false) {\r
-                                $this->debug_message("*** can't get OIM, msgid = $oim_msgid");\r
+                                $this->debug_message("*** Could not 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
+                                    $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring 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->debug_message("*** can't re-login, something wrong here, ignore this OIM");\r
+                                    $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");\r
                                     continue;\r
                                 }\r
                                 $re_login = true;\r
@@ -2503,13 +2058,11 @@ class MSN {
                                 $this->debug_message("*** get new ticket, try it again");\r
                                 $sMsg = $this->getOIM_message($oim_msgid);\r
                                 if ($sMsg === false) {\r
-                                    $this->debug_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
+                                    $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");\r
                                     continue;\r
                                 }\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
@@ -2519,8 +2072,7 @@ class MSN {
                     // 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
+                    if (is_numeric($size) && $size > 0) {\r
                         $data = $this->ns_readdata($size);\r
                         $aLines = @explode("\n", $data);\r
                         $header = true;\r
@@ -2546,13 +2098,11 @@ class MSN {
                                 $sMsg .= $str;\r
                             }\r
                         }\r
-                        if($ignore)\r
-                        {\r
-                            $this->debug_message("*** ingnore from $from_email: $line");\r
+                        if ($ignore) {\r
+                            $this->debug_message("*** Ignoring message from $from_email: $line");\r
                             break;\r
                         }\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
@@ -2563,7 +2113,7 @@ class MSN {
                     @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
+                        $this->ns_readdata($size);\r
                     break;\r
 \r
                 case 'CHL':\r
@@ -2576,7 +2126,7 @@ class MSN {
                     $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
+                    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
@@ -2601,41 +2151,10 @@ class MSN {
                         $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->debug_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->debug_message("*** Fork Error $User");\r
-                                break;\r
-                            }\r
-                            else\r
-                            {\r
-                                //Child Process\r
-                                $this->debug_message("*** Child Process Start for $User");\r
-                                unset($Message['XFRSent']);\r
-                                unset($Message['ReqTime']);\r
+\r
+                    $this->debug_message("NS: <<< XFR SB");\r
+                    $user = array_shift($this->waitingForXFR);\r
                                 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
-                                if ($bSBresult === false)\r
-                                {\r
-                                    // error for switchboard\r
-                                    $this->debug_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
@@ -2651,7 +2170,7 @@ class MSN {
                     break;\r
 \r
                 case 'RNG':\r
-                    if($this->PhotoStickerFile!==false)\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
@@ -2660,32 +2179,9 @@ class MSN {
                     $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->debug_message("*** Ignore RNG from $email");\r
-                        break;\r
-                    }\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
-                    {\r
-                        //Parrent Process\r
-                        $this->ChildProcess[$pid]='RNG';\r
-                        break;\r
-                    }\r
-                    elseif($pid==-1)\r
-                    {\r
-                        $this->debug_message("*** Fork Error $User");\r
-                        break;\r
-                    }\r
-                    else\r
-                    {\r
-                        //Child Process\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
+                    $this->addContact($email, 1, $email, true);\r
+                    $this->connectToSBSession('Passive', $sb_ip, $sb_port, $email, array('sid' => $sid, 'ticket' => $ticket));\r
                     break;\r
                 case 'OUT':\r
                     // force logout from NS\r
@@ -2710,8 +2206,428 @@ class MSN {
      * Read and handle incoming command/message from\r
      * a switchboard session socket\r
      */\r
-    private function sbReceive() {\r
+    private function sbReceive($socket) {\r
+        $intsocket = (int) $socket;\r
+        $session = &$this->switchBoardSessions[$intsocket];\r
+\r
+        if (feof($socket)) {\r
+            // Unset session lookup value\r
+            unset($this->switchBoardSessionLookup[$session['to']]);\r
+\r
+            // Unset session itself\r
+            unset($this->switchBoardSessions[$intsocket]);\r
+            return;\r
+        }\r
+\r
+        $id = &$session['id'];\r
+\r
+        $data = $this->sb_readln($socket);\r
+        $code = substr($data, 0, 3);\r
+        switch($code) {\r
+            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->debug_message("*** $email joined session");\r
+                $session['joined'] = true;\r
+                break;\r
+            case 'BYE':\r
+                $this->debug_message("*** Quit for BYE");\r
+                $this->endSBSession();\r
+                break;\r
+            case 'USR':\r
+                // SB: <<< USR {id} OK {user} {alias}\r
+                // we don't need the data, just ignore it\r
+                // request user to join this switchboard\r
+                // SB: >>> CAL {id} {user}\r
+                $this->sb_writeln($socket, $id, "CAL $this->id $user");\r
+                break;\r
+            case 'CAL':\r
+                // SB: <<< CAL {id} RINGING {?}\r
+                // we don't need this, just ignore, and wait for other response\r
+                $session['id']++;\r
+                break;\r
+            case 'JOI':\r
+                // SB: <<< JOI {user} {alias} {clientid?}\r
+                // someone join us\r
+                // we don't need the data, just ignore it\r
+                // no more user here\r
+                $session['joined'] = true;\r
+                break;\r
+            case 'MSG':\r
+                // SB: <<< MSG {email} {alias} {len}\r
+                @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);\r
+                $len = trim($len);\r
+                $data = $this->sb_readdata($socket, $len);\r
+                $aLines = @explode("\n", $data);\r
+                $header = true;\r
+                $ignore = false;\r
+                $is_p2p = 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
+                            // typing notification, just ignore\r
+                            $ignore = true;\r
+                            break;\r
+                        }\r
+                        if (strncasecmp($line, 'Chunk:', 6) == 0) {\r
+                            // we don't handle any split message, just ignore\r
+                            $ignore = true;\r
+                            break;\r
+                        }\r
+                        if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {\r
+                            // p2p message, ignore it, but we need to send acknowledgement for it...\r
+                            $is_p2p = true;\r
+                            $p = strstr($data, "\n\n");\r
+                            $sMsg = '';\r
+                            if ($p === false) {\r
+                                $p = strstr($data, "\r\n\r\n");\r
+                                if ($p !== false)\r
+                                $sMsg = substr($p, 4);\r
+                            }\r
+                            else\r
+                            $sMsg = substr($p, 2);\r
+                            break;\r
+                        }\r
+                        if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {\r
+                            // ignore all application/x-... message\r
+                            // for example:\r
+                            //      application/x-ms-ink        => ink message\r
+                            $ignore = true;\r
+                            break;\r
+                        }\r
+                        if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {\r
+                            // ignore all text/x-... message\r
+                            // for example:\r
+                            //      text/x-msnmsgr-datacast         => nudge, voice clip....\r
+                            //      text/x-mms-animemoticon         => customized animemotion word\r
+                            $ignore = true;\r
+                            break;\r
+                        }\r
+                        continue;\r
+                    }\r
+                    if ($sMsg !== '')\r
+                        $sMsg .= "\n";\r
+                    $sMsg .= $line;\r
+                }\r
+                if ($ignore) {\r
+                    $this->debug_message("*** Ignoring SB data from $from_email: $line");\r
+                    break;\r
+                }\r
+                if ($is_p2p) {\r
+                    // we will ignore any p2p message after sending acknowledgement\r
+                    $ignore = true;\r
+                    $len = strlen($sMsg);\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->debug_message("*** p2p: size error, less than 52!");\r
+                        break;\r
+                    }*/\r
+                    $aDwords = @unpack("V12dword", $sMsg);\r
+                    if (!is_array($aDwords)) {\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
+                    $hdr_SessionID = $aDwords['dword1'];\r
+                    $hdr_Identifier = $aDwords['dword2'];\r
+                    $hdr_DataOffsetLow = $aDwords['dword3'];\r
+                    $hdr_DataOffsetHigh = $aDwords['dword4'];\r
+                    $hdr_TotalDataSizeLow = $aDwords['dword5'];\r
+                    $hdr_TotalDataSizeHigh = $aDwords['dword6'];\r
+                    $hdr_MessageLength = $aDwords['dword7'];\r
+                    $hdr_Flag = $aDwords['dword8'];\r
+                    $hdr_AckID = $aDwords['dword9'];\r
+                    $hdr_AckUID = $aDwords['dword10'];\r
+                    $hdr_AckSizeLow = $aDwords['dword11'];\r
+                    $hdr_AckSizeHigh = $aDwords['dword12'];\r
+                    $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");\r
+                    $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");\r
+                    $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");\r
+                    $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");\r
+                    $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");\r
+                    $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");\r
+                    $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");\r
+                    $this->debug_message("*** p2p: header Flag = $hdr_Flag");\r
+                    $this->debug_message("*** p2p: header AckID = $hdr_AckID");\r
+                    $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");\r
+                    $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");\r
+                    $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");\r
+                    if ($hdr_Flag == 2) {\r
+                        //This is an ACK from SB ignore....\r
+                        $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");\r
+                        break;\r
+                    }\r
+                    $MsgBody = $this->linetoArray(substr($sMsg, 48, -4));\r
+                    $this->debug_message("*** p2p: body".print_r($MsgBody, true));\r
+                    if (($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context']))) {\r
+                        while (true) {\r
+                            if ($this->sb_readln($socket) === false) break;\r
+                        }\r
+                        $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg, 0, 48)));\r
+                        preg_match('/{([0-9A-F\-]*)}/i', $MsgBody['Via'], $Matches);\r
+                        $BranchGUID = $Matches[1];\r
+                        //it's an invite to send a display picture.\r
+                        $new_id = ~$hdr_Identifier;\r
+                        $hdr = pack(\r
+                            "LLLLLLLLLLLL", $hdr_SessionID,\r
+                            $new_id,\r
+                            0, 0,\r
+                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+                            0,\r
+                            2,\r
+                            $hdr_Identifier,\r
+                            $hdr_AckID,\r
+                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh\r
+                        );\r
+                        $footer = pack("L", 0);\r
+                        $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
+                        $len = strlen($message);\r
+                        $this->sb_writeln($socket, $id, "MSG $this->id D $len");\r
+                        $this->sb_writedata($socket, $message);\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($socket); // 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
+                        $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);\r
+                        $MessagePayload=\r
+                            "MSNSLP/1.0 200 OK\r\n".\r
+                            "To: <msnmsgr:".$from_email.">\r\n".\r
+                            "From: <msnmsgr:".$this->user.">\r\n".\r
+                            "Via: ".$MsgBody['Via']."\r\n".\r
+                            "CSeq: ".($MsgBody['CSeq']+1)."\r\n".\r
+                            "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
+                            "Max-Forwards: 0\r\n".\r
+                            "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".\r
+                            "Content-Length: ".strlen($MessageContent)."\r\n\r\n".\r
+                        $MessageContent;\r
+                        $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
+                        $hdr_TotalDataSizeHigh=0;\r
+                        $hdr = pack(\r
+                            "LLLLLLLLLLLL", $hdr_SessionID,\r
+                            $new_id,\r
+                            0, 0,\r
+                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+                            strlen($MessagePayload),\r
+                            0,\r
+                            rand(),\r
+                            0,\r
+                            0, 0\r
+                        );\r
+\r
+                        $message =\r
+                            "MIME-Version: 1.0\r\n".\r
+                            "Content-Type: application/x-msnmsgrp2p\r\n".\r
+                            "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
+                        $this->sb_writeln($socket, $id, "MSG $this->id D ".strlen($message));\r
+                        $this->sb_writedata($socket, $message);\r
+                        $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));\r
+                        $this->sb_readln($socket); // Read ACK;\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
+                        $hdr_TotalDataSizeLow = 4;\r
+                        $hdr_TotalDataSizeHigh = 0 ;\r
+                        $new_id++;\r
+                        $hdr = pack(\r
+                            "LLLLLLLLLLLL",\r
+                            $MsgBody['SessionID'],\r
+                            $new_id,\r
+                            0, 0,\r
+                            $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+                            $hdr_TotalDataSizeLow,\r
+                            0,\r
+                            rand(),\r
+                            0,\r
+                            0, 0\r
+                        );\r
+                        $message =\r
+                            "MIME-Version: 1.0\r\n".\r
+                            "Content-Type: application/x-msnmsgrp2p\r\n".\r
+                            "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L', 0)."$footer";\r
+                        $this->sb_writeln($socket, $id, "MSG $this->id D ".strlen($message));\r
+                        $this->sb_writedata($socket, $message);\r
+                        $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));\r
+                        $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));\r
+                        $this->sb_readln($socket); // Read ACK;\r
+\r
+                        // send Data Content..\r
+                        $footer=pack('N',1);\r
+                        $new_id++;\r
+                        $FileSize=filesize($PictureFilePath);\r
+                        if ($hTitle=fopen($PictureFilePath,'rb')) {\r
+                            $Offset = 0;\r
+                            //$new_id++;\r
+                            while (!feof($hTitle)) {\r
+                                $FileContent = fread($hTitle, 1024);\r
+                                $FileContentSize = strlen($FileContent);\r
+                                $hdr = pack(\r
+                                    "LLLLLLLLLLLL",\r
+                                    $MsgBody['SessionID'],\r
+                                    $new_id,\r
+                                    $Offset, 0,\r
+                                    $FileSize, 0,\r
+                                    $FileContentSize,\r
+                                    0x20,\r
+                                    rand(),\r
+                                    0,\r
+                                    0, 0\r
+                                );\r
+                                $message =\r
+                                    "MIME-Version: 1.0\r\n".\r
+                                    "Content-Type: application/x-msnmsgrp2p\r\n".\r
+                                    "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";\r
+                                $this->sb_writeln($socket, $id, "MSG $this->id D ".strlen($message));\r
+                                $this->sb_writedata($socket, $message);\r
+                                $this->debug_message("*** p2p: dump send Data Content message  $Offset / $FileSize :\n".$this->dump_binary($message));\r
+                                $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));\r
+                                //$this->SB_readln($socket);//Read ACK;\r
+                                $Offset += $FileContentSize;\r
+                            }\r
+                        }\r
+                        //Send Bye\r
+                        /*\r
+                        $MessageContent="\r\n".pack("C", 0);\r
+                        $MessagePayload=\r
+                            "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
+                            "CSeq: 0\r\n".\r
+                            "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
+                            "Max-Forwards: 0\r\n".\r
+                            "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".\r
+                            "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;\r
+                        $footer=pack('N',0);\r
+                        $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
+                        $hdr_TotalDataSizeHigh=0;\r
+                        $new_id++;\r
+                        $hdr = pack("LLLLLLLLLLLL",\r
+                        0,\r
+                        $new_id,\r
+                        0, 0,\r
+                        $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+                        0,\r
+                        0,\r
+                        rand(),\r
+                        0,\r
+                        0,0);\r
+                        $message =\r
+                                    "MIME-Version: 1.0\r\n".\r
+                                    "Content-Type: application/x-msnmsgrp2p\r\n".\r
+                                    "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
+                        $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));\r
+                        $id++;\r
+                        $this->sb_writedata($socket, $message);\r
+                        $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));\r
+                        */\r
+                        break;\r
+                    }\r
+                    //TODO:\r
+                    //if ($hdr_Flag == 2) {\r
+                    // just send ACK...\r
+                    //    $this->sb_writeln($socket, $id, "ACK $id");\r
+                    //    break;\r
+                    //}\r
+                    if ($hdr_SessionID == 4) {\r
+                        // ignore?\r
+                        $this->debug_message("*** p2p: ignore flag 4");\r
+                        break;\r
+                    }\r
+                    $finished = false;\r
+                    if ($hdr_TotalDataSizeHigh == 0) {\r
+                        // only 32 bites size\r
+                        if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)\r
+                        $finished = true;\r
+                    }\r
+                    else {\r
+                        // we won't accept any file transfer\r
+                        // so I think we won't get any message size need to use 64 bits\r
+                        // 64 bits size here, can't count directly...\r
+                        $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);\r
+                        $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);\r
+                        $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);\r
+                        $now_size = bcadd($dataoffset, $messagelength);\r
+                        if (bccomp($now_size, $totalsize) >= 0)\r
+                        $finished = true;\r
+                    }\r
+                    if (!$finished) {\r
+                        // ignore not finished split packet\r
+                        $this->debug_message("*** p2p: ignore split packet, not finished");\r
+                        break;\r
+                    }\r
+                    //$new_id = ~$hdr_Identifier;\r
+                    /*\r
+                     $new_id++;\r
+                     $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
+                     $new_id,\r
+                     0, 0,\r
+                     $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+                     0,\r
+                     2,\r
+                     $hdr_Identifier,\r
+                     $hdr_AckID,\r
+                     $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);\r
+                     $footer = pack("L", 0);\r
+                     $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
+                     $len = strlen($message);\r
+                     $this->sb_writeln($socket, $id, "MSG $id D $len");\r
+                     $id++;\r
+                     $this->sb_writedata($socket, $message);\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->debug_message("*** MSG from $from_email: $sMsg");\r
+                $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));\r
+                break;\r
+            case '217':\r
+                $this->debug_message("*** User $user is offline. Trying OIM.");\r
+                $session['offline'] = true;\r
+                break;\r
+            default:\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("*** SB: $this->error");\r
+                    $sessionEnd=true;\r
+                }\r
+                break;\r
+        }\r
+    }\r
 \r
+    /**\r
+    * Called when we want to end a switchboard session\r
+    * or a switchboard session ends\r
+    *\r
+    * @param resource $socket Socket\r
+    * @param boolean $killsession Whether to delete the session\r
+    * @return void\r
+    */\r
+    private function endSBSession($socket, $killsession = false) {\r
+        if (!self::socketcheck($socket)) {\r
+            $this->sb_writeln($socket, $fake = 0, 'OUT');\r
+        }\r
+        @fclose($socket);\r
+\r
+        // Unset session lookup value\r
+        $intsocket = (int) $socket;\r
+        unset($this->switchBoardSessionLookup[$this->switchBoardSessions[$intsocket]['to']]);\r
+\r
+        // Unset session itself\r
+        unset($this->switchBoardSessions[$intsocket]);\r
     }\r
 \r
     /**\r
@@ -2722,16 +2638,16 @@ class MSN {
      * @return void\r
      */\r
     public function receive() {\r
-        //First, get an array of sockets that have data that is ready to be read\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
+        $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
+        // 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
+        foreach ($ready as $socket) {\r
+            if ($socket == $this->NSfp) {\r
                 $this->nsReceive();\r
             } else {\r
                 $this->sbReceive($socket);\r
@@ -2741,21 +2657,29 @@ class MSN {
 \r
     /**\r
      * Send a request for a switchboard session\r
-     * @param String $to Target email for switchboard session\r
+     *\r
+     * @param string $to Target email for switchboard session\r
      */\r
     private function reqSBSession($to) {\r
         $this->debug_message("*** Request SB for $to");\r
         $this->ns_writeln("XFR $this->id SB");\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->switchBoardSessions[$to] = array(\r
+            'to' => $to,\r
+            'socket' => NULL,\r
+            'id' => 1,\r
+            'joined' => false,\r
+            'offline' => false,\r
+            'XFRReqTime' => time()\r
+        );\r
         $this->waitingForXFR[] = &$this->switchBoardSessions[$to];\r
     }\r
 \r
     /**\r
      * Following an XFR or RNG, connect to the switchboard session\r
      *\r
-     * @param string $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case or RNG)\r
+     * @param string $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case of 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
@@ -2763,21 +2687,35 @@ class MSN {
      * @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
+        $this->debug_message("*** SB: Trying to connect to switchboard server $ip:$port");\r
 \r
-        $this->switchBoardSessions[$to]['socket'] = @fsockopen($ip, $port, $errno, $errstr, 5);\r
-        $socket = $this->switchBoardSessions[$to]['socket'];\r
-        if(!$socket) {\r
+        $socket = @fsockopen($ip, $port, $errno, $errstr, $this->timeout);\r
+        if (!$socket) {\r
             $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
             return false;\r
         }\r
-        $this->switchBoardSockets[(int) $socket] = $socket;\r
 \r
-        stream_set_timeout($socket, $this->SBStreamTimeout);\r
+        // Store the socket in the lookup array\r
+        $this->switchBoardSessionLookup[$to] = $socket;\r
+\r
+        // Store the socket in the sessions array\r
+        $intsocket = (int) $socket;\r
+        $this->switchBoardSessions[$to] = array(\r
+            'to' => $to,\r
+            'socket' => $socket,\r
+            'id' => 1,\r
+            'joined' => false,\r
+            'offline' => false,\r
+            'XFRReqTime' => time()\r
+        );\r
+\r
+        // Change the index of the session to the socket\r
+        $this->switchBoardSessions[$intsocket] = $this->switchBoardSessions[$to];\r
+        unset($this->switchBoardSessions[$to]);\r
 \r
-        $id = &$this->switchBoardSessions[$to]['id'];\r
+        $id = &$this->switchBoardSessions[$intsocket]['id'];\r
 \r
-        if($mode == 'Active') {\r
+        if ($mode == 'Active') {\r
             $cki_code = $param['cki'];\r
 \r
             // SB: >>> USR {id} {user} {cki}\r
@@ -2790,8 +2728,6 @@ class MSN {
             // SB: >>> ANS {id} {user} {ticket} {session_id}\r
             $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");\r
         }\r
-\r
-        $this->switchBoardSessions[$to]['lastActive'] = time();\r
     }\r
 \r
     /**\r
@@ -2802,24 +2738,23 @@ class MSN {
      * @return boolean true on success\r
      */\r
     private function sendMessageViaSB($to, $message) {\r
-        if(socketcheck($this->switchBoardSessions[$to]['socket'])) {\r
-            $this->reqSBSession($to);\r
+        $socket = $this->switchBoardSessionLookup[$to];\r
+        if (self::socketcheck($socket)) {\r
             return false;\r
         }\r
 \r
-        if(!$this->switchBoardSessions[$to]['joined']) {\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
-        $id = &$this->switchBoardSessions[$to]['id'];\r
-        $socket = $this->switchBoardSessions[$to]['socket'];\r
+        $intsocket = (int) $socket;\r
+        $id = &$this->switchBoardSessions[$intsocket]['id'];\r
 \r
         $aMessage = $this->getMessage($Message);\r
         //CheckEmotion...\r
         $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
-        if($MsnObjDefine !== '')\r
-        {\r
+        if ($MsnObjDefine !== '') {\r
             $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";\r
             $len = strlen($SendString);\r
             // TODO handle failure during write to socket\r
@@ -2838,9 +2773,11 @@ class MSN {
 \r
     /**\r
      * Send offline message\r
+     *\r
      * @param string $to Intended recipient\r
      * @param string $sMessage Message\r
      * @param string $lockkey Lock key\r
+     * @return mixed true on success or error data\r
      */\r
     private function sendOIM($to, $sMessage, $lockkey) {\r
         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
@@ -2949,17 +2886,19 @@ X-OIM-Sequence-Num: 1
     /**\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
+     * @param string $to Intended recipient\r
+     * @param string $message Message\r
+     * @param integer $network Network\r
      * @return void\r
      */\r
-    private function sendOtherNetworkMessage($message, $to, $network) {\r
+    private function sendOtherNetworkMessage($to, $message, $network) {\r
         $message = $this->getMessage($message, $network);\r
         $len = strlen($message);\r
+        // TODO Introduce error checking for message sending\r
         $this->ns_writeln("UUM $this->id $to $network 1 $len");\r
         $this->ns_writedata($Message);\r
         $this->debug_message("*** Sent to $to (network: $network):\n$Message");\r
+        return true;\r
     }\r
 \r
     /**\r
@@ -2971,21 +2910,35 @@ X-OIM-Sequence-Num: 1
      * @param string $message Message\r
      */\r
     public function sendMessage($to, $message) {\r
-        if($message != '') {\r
+        if ($message != '') {\r
             list($name, $host, $network) = explode('@', $to);\r
             $network = $network == '' ? 1 : $network;\r
+            $recipient = $name.$host;\r
 \r
-            if ($network === 1 && $this->switchBoardSessions[$to]['socket'] !== NULL) {\r
-                $recipient = $name . $host;\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
+            if ($network === 1) {\r
+                if (!isset($this->switchBoardSessionLookup[$recipient]) && (!isset($this->switchBoardSessions[$recipient])\r
+                    || time() - $this->switchBoardSessions[$recipient]['XFRReqTime'] > $this->XFRReqTimeout)) {\r
+                    $this->debug_message("*** No existing SB session or request has timed out");\r
+                    $this->reqSBSession($recipient);\r
                     return false;\r
+                } else {\r
+                    $socket = $this->switchBoardSessionLookup[$to];\r
+                    if ($this->switchBoardSessions[(int) $socket]['offline']) {\r
+                        $this->debug_message("*** Contact ($recipient) offline, sending OIM");\r
+                        $this->endSBSession($socket);\r
+                        return $this->sendMessage($recipient.'@Offline', $message);\r
+                    } else {\r
+                        $this->debug_message("*** Attempting to send message to $recipient using existing SB session");\r
+\r
+                        if ($this->sendMessageViaSB($recipient, $message)) {\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
+                    }\r
                 }\r
             } elseif ($network == 'Offline') {\r
                 //Send OIM\r
@@ -2993,7 +2946,7 @@ X-OIM-Sequence-Num: 1
                 $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 (($oim_result = $this->sendOIM($recipient, $message, $lockkey)) === true) break;\r
                     if (is_array($oim_result) && $oim_result['challenge'] !== false) {\r
                         // need challenge lockkey\r
                         $this->debug_message("*** Need challenge code for ".$oim_result['challenge']);\r
@@ -3002,14 +2955,14 @@ X-OIM-Sequence-Num: 1
                     }\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
+                            $this->debug_message("*** Can't send OIM, but we already re-logged-in again, so returning false");\r
+                            return false;\r
                         }\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
+                            $this->debug_message("*** Can't re-login, something went wrong here, returning false");\r
                             return false;\r
                         }\r
                         $this->debug_message("*** Getting new ticket and trying again");\r
@@ -3017,27 +2970,13 @@ X-OIM-Sequence-Num: 1
                     }\r
                 }\r
             } else {\r
-                $this->debug_message("*** Not MSN network or no existing SB session");\r
-                $this->reqSBSession($to);\r
-                return false;\r
+                // Other network\r
+                return $this->sendOtherNetworkMessage($recipient, $message, $network);\r
             }\r
         }\r
         return true;\r
     }\r
 \r
-    //FIXME Not sure if this is needed?\r
-    private function endSBSession($socket) {\r
-        if (feof($socket)) {\r
-            // lost connection? error? try OIM later\r
-            @fclose($socket);\r
-            return false;\r
-        }\r
-        $fake = 0;\r
-        $this->sb_writeln($socket, $fake, "OUT");\r
-        @fclose($socket);\r
-        return true;\r
-    }\r
-\r
     /**\r
      * Sends a ping command\r
      *\r
@@ -3069,7 +3008,7 @@ X-OIM-Sequence-Num: 1
      * @return array Array of Switchboard sockets\r
      */\r
     public function getSBSockets() {\r
-        return $this->switchBoardSockets;\r
+        return $this->switchBoardSessionLookup;\r
     }\r
 \r
     /**\r
@@ -3078,7 +3017,7 @@ X-OIM-Sequence-Num: 1
      * @return array Array of socket resources\r
      */\r
     public function getSockets() {\r
-        return array_merge($this->NSfp, $this->switchBoardSockets);\r
+        return array_merge(array($this->NSfp), $this->switchBoardSessionLookup);\r
     }\r
 \r
     /**\r
@@ -3120,7 +3059,8 @@ X-OIM-Sequence-Num: 1
      * Registers a user handler\r
      *\r
      * Handler List\r
-     * IMIn, Pong, ConnectFailed, Reconnect\r
+     * IMIn, Pong, ConnectFailed, Reconnect,\r
+     * AddedToList, RemovedFromList\r
      *\r
      * @param string $event Event name\r
      * @param string $handler User function to call\r
index 8f436bdff8e2842a62fd720f6ee0bbf3efbfd990..5b04995c1804ef991cbe559aef1ba2b44c7686a4 100644 (file)
@@ -95,11 +95,15 @@ class MsnManager extends ImManager {
     */\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 = new MSN(\r
+                array(\r
+                    '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
+                )\r
+            );\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
@@ -124,6 +128,7 @@ class MsnManager extends ImManager {
 \r
         $this->conn->sendPing();\r
         $this->lastping = time();\r
+        $this->pingInterval = 50;\r
         return true;\r
     }\r
 \r