]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
More work on adapting phpmsnclass
authorLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Mon, 14 Jun 2010 02:47:44 +0000 (03:47 +0100)
committerLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Mon, 14 Jun 2010 02:47:44 +0000 (03:47 +0100)
plugins/Msn/MsnPlugin.php
plugins/Msn/Queued_Msn.php [deleted file]
plugins/Msn/extlib/phpmsnclass/msn.class.php
plugins/Msn/msnmanager.php

index 6737e727abe9aa488b02009c3e0ba9b1a1903577..5566b543027cf7598c446a39eeaaaebec782aaf3 100644 (file)
@@ -170,4 +170,3 @@ class MsnPlugin extends ImPlugin
         return true;
     }
 }
-
diff --git a/plugins/Msn/Queued_Msn.php b/plugins/Msn/Queued_Msn.php
deleted file mode 100644 (file)
index bc8e0a1..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Queue-mediated proxy class for outgoing MSN messages.
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Network
- * @package   StatusNet
- * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class Queued_XMPP extends MSN {
-    /**
-     * Reference to the MsnPlugin object we're hooked up to.
-     */
-    public $plugin;
-
-    /**
-     * Constructor
-     *
-     * @param MsnPlugin $plugin
-     * @param string  $host
-     * @param integer $port
-     * @param string  $user
-     * @param string  $password
-     * @param string  $resource
-     * @param string  $server
-     * @param boolean $printlog
-     * @param string  $loglevel
-     */
-    public function __construct($plugin, $host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null)
-    {
-        $this->plugin = $plugin;
-
-        parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
-
-        // We use $host to connect, but $server to build JIDs if specified.
-        // This seems to fix an upstream bug where $host was used to build
-        // $this->basejid, never seen since it isn't actually used in the base
-        // classes.
-        if (!$server) {
-            $server = $this->host;
-        }
-        $this->basejid = $this->user . '@' . $server;
-
-        // Normally the fulljid is filled out by the server at resource binding
-        // time, but we need to do it since we're not talking to a real server.
-        $this->fulljid = "{$this->basejid}/{$this->resource}";
-    }
-
-    /**
-     * Send a formatted message to the outgoing queue for later forwarding
-     * to a real XMPP connection.
-     *
-     * @param string $msg
-     */
-    public function send($msg, $timeout=NULL)
-    {
-        $this->plugin->enqueue_outgoing_raw($msg);
-    }
-
-    //@{
-    /**
-     * Stream i/o functions disabled; only do output
-     */
-    public function connect($timeout = 30, $persistent = false, $sendinit = true)
-    {
-        throw new Exception("Can't connect to server from fake XMPP.");
-    }
-
-    public function disconnect()
-    {
-        throw new Exception("Can't connect to server from fake XMPP.");
-    }
-
-    public function process()
-    {
-        throw new Exception("Can't read stream from fake XMPP.");
-    }
-
-    public function processUntil($event, $timeout=-1)
-    {
-        throw new Exception("Can't read stream from fake XMPP.");
-    }
-
-    public function read()
-    {
-        throw new Exception("Can't read stream from fake XMPP.");
-    }
-
-    public function readyToProcess()
-    {
-        throw new Exception("Can't read stream from fake XMPP.");
-    }
-    //@}
-
-}
-
index 36b47f8e9c749e06cb01ab446dfbd49262a6b0bc..317acd0d532de3ec84a0b2eca768074cd52f67e7 100644 (file)
@@ -25,14 +25,11 @@ class MSN {
     private $login_method = 'SSO';\r
     private $oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';\r
     private $oim_send_soap = 'http://messenger.live.com/ws/2006/09/oim/Store2';\r
-    private $windows;\r
-    private $kill_me = false;\r
     private $id;\r
     private $ticket;\r
     private $user = '';\r
     private $password = '';\r
     private $NSfp=false;\r
-    private $SBfp;\r
     private $passport_policy = '';\r
     private $alias;\r
     private $psm;\r
@@ -46,11 +43,10 @@ class MSN {
     private $ChildProcess=array();\r
     private $MAXChildProcess=3;\r
     private $ReqSBXFRTimeout=60;\r
-    private $SBTimeout=2;\r
     private $LastPing;\r
     private $ping_wait=50;\r
     private $SBIdleTimeout=10;\r
-    private $SBStreamTimeout=10;\r
+    private $SBStreamTimeout=2;\r
     private $NSStreamTimeout=2;\r
     private $MsnObjArray=array();\r
     private $MsnObjMap=array();\r
@@ -113,7 +109,10 @@ class MSN {
     \r
     private $aContactList = array();\r
     private $aADL = array();\r
+    private $re_login;\r
     private $switchBoardSessions = array();\r
+    private $switchBoardSockets = array();\r
+    private $waitingForXFR = array();\r
     \r
     /**\r
     * Event Handler Functions\r
@@ -121,72 +120,22 @@ class MSN {
     private $myEventHandlers = array();\r
     \r
     // End added for StatusNet\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
-            }\r
-            if($Key{0}=='!')\r
-            {\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
-                }\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
-        }\r
-        if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);\r
-        return $ArrayString;\r
-    }\r
-\r
-    public function End()\r
-    {\r
-        $this->log_message("*** someone kill me ***");\r
-        $this->kill_me=true;\r
-    }\r
-    private function IsIgnoreMail($Email)\r
-    {        \r
-        if($this->IgnoreList==false) return false;\r
-        foreach($this->IgnoreList as $Pattern)\r
-        {\r
-            if(preg_match($Pattern,$Email)) return true;\r
-        }\r
-        return false;\r
-    }\r
+    \r
     public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)\r
     {\r
         $this->user = $Configs['user'];\r
         $this->password = $Configs['password'];\r
         $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';\r
         $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';\r
-        $my_add_function = isset($Configs['add_user_function']) ? $Configs['add_user_function'] : false;\r
-        $my_rem_function = isset($Configs['remove_user_function']) ? $Configs['remove_user_function'] : false;\r
         $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;\r
         $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;\r
         $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;\r
         $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;\r
         $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;\r
-        $this->IgnoreList=isset($Configs['IgnoreList'])?$Configs['IgnoreList']:false;\r
         if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)\r
         {\r
             foreach($this->Emotions as $EmotionFilePath)\r
-            $this->MsnObj($EmotionFilePath,$Type=2);\r
+                $this->MsnObj($EmotionFilePath,$Type=2);\r
         }        \r
         $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;\r
         $this->timeout = $timeout;\r
@@ -213,19 +162,51 @@ class MSN {
          = 0x7000800C;\r
          */\r
         $this->clientid = $client_id;\r
-        $this->windows =(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');\r
         $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
+            }\r
+            if($Key{0}=='!')\r
+            {\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
+                }\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
+        }\r
+        if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);\r
+        return $ArrayString;\r
+    }\r
+    \r
     private function get_passport_ticket($url = '')\r
     {\r
         $user = $this->user;\r
         $password = htmlspecialchars($this->password);\r
 \r
         if ($url === '')\r
-        $passport_url = $this->passport_url;\r
+            $passport_url = $this->passport_url;\r
         else\r
-        $passport_url = $url;\r
+            $passport_url = $url;\r
 \r
         $XML = '<?xml version="1.0" encoding="UTF-8"?>\r
 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"\r
@@ -449,78 +430,81 @@ class MSN {
         $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
+            '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
-        file_put_contents('/tmp/STTicket.txt',htmlspecialchars($this->ticket['storage_ticket']));\r
-        //$this->debug_message("StorageTicket:\n",htmlspecialchars($this->ticket['storage_ticket']));\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
-  );\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
-      foreach($Result->ABFindAllResult->contacts->Contact as $Contact)\r
-      $this->Contacts[$Contact->contactInfo->passportName]=$Contact;\r
-  }\r
-  catch(Exception $e)\r
-  {\r
-      $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
-  }\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
+            )\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
+            foreach($Result->ABFindAllResult->contacts->Contact as $Contact)\r
+                $this->Contacts[$Contact->contactInfo->passportName]=$Contact;\r
+        }\r
+        catch(Exception $e)\r
+        {\r
+            $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
+            return false;\r
+        }\r
+        return true;\r
     }\r
-    protected function addContact($email, $network, $display = '', $sendADL = false)\r
+    \r
+    private function addContact($email, $network, $display = '', $sendADL = false)\r
     {\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
-        )\r
-        )\r
-        )\r
-        ),\r
-    'options'=>array(\r
-     'EnableAllowListManagement'=>true\r
-        )\r
-        )\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
+                )\r
+            )\r
         );\r
         $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');\r
         try\r
@@ -531,6 +515,7 @@ class MSN {
         catch(Exception $e)\r
         {\r
             $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
+            return false;\r
         }\r
         if ($sendADL && !feof($this->NSfp)) {\r
             @list($u_name, $u_domain) = @explode('@', $email);\r
@@ -552,7 +537,7 @@ class MSN {
         $user = $email;\r
         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
         if ($network == 1)\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+            $XML = '<?xml version="1.0" encoding="utf-8"?>\r
 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
@@ -591,7 +576,7 @@ class MSN {
 </soap:Body>\r
 </soap:Envelope>';\r
         else\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+            $XML = '<?xml version="1.0" encoding="utf-8"?>\r
 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
@@ -634,41 +619,41 @@ class MSN {
             'SOAPAction: '.$this->delmember_soap,\r
             'Content-Type: text/xml; charset=utf-8',\r
             '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
-            $curl = curl_init();\r
-            curl_setopt($curl, CURLOPT_URL, $this->delmember_url);\r
-            curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-            if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-            curl_setopt($curl, CURLOPT_POST, 1);\r
-            curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-            $data = curl_exec($curl);\r
-            $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-            curl_close($curl);\r
-            $this->debug_message("*** Get Result:\n$data");\r
-\r
-            if ($http_code != 200) {\r
-                preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
-                if (count($matches) == 0) {\r
-                    $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");\r
-                    return false;\r
-                }\r
-                $faultcode = trim($matches[1]);\r
-                $faultstring = trim($matches[2]);\r
-                if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {\r
-                    $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");\r
-                    return false;\r
-                }\r
-                $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
-                return true;\r
+        );\r
+\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
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+\r
+        if ($http_code != 200) {\r
+            preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
+            if (count($matches) == 0) {\r
+                $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");\r
+                return false;\r
+            }\r
+            $faultcode = trim($matches[1]);\r
+            $faultstring = trim($matches[2]);\r
+            if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {\r
+                $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");\r
+                return false;\r
             }\r
-            $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");\r
+            $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
             return true;\r
+        }\r
+        $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");\r
+        return true;\r
     }\r
 \r
     function addMemberToList($email, $network, $list) {\r
@@ -677,7 +662,7 @@ class MSN {
         $user = $email;\r
 \r
         if ($network == 1)\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+            $XML = '<?xml version="1.0" encoding="utf-8"?>\r
 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
@@ -716,7 +701,7 @@ class MSN {
 </soap:Body>\r
 </soap:Envelope>';\r
         else\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+            $XML = '<?xml version="1.0" encoding="utf-8"?>\r
 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
@@ -764,41 +749,41 @@ class MSN {
             'SOAPAction: '.$this->addmember_soap,\r
             'Content-Type: text/xml; charset=utf-8',\r
             '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
-            $curl = curl_init();\r
-            curl_setopt($curl, CURLOPT_URL, $this->addmember_url);\r
-            curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-            if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-            curl_setopt($curl, CURLOPT_POST, 1);\r
-            curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-            $data = curl_exec($curl);\r
-            $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-            curl_close($curl);\r
-            $this->debug_message("*** Get Result:\n$data");\r
-\r
-            if ($http_code != 200) {\r
-                preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
-                if (count($matches) == 0) {\r
-                    $this->log_message("*** can't add member (network: $network) $email to $list");\r
-                    return false;\r
-                }\r
-                $faultcode = trim($matches[1]);\r
-                $faultstring = trim($matches[2]);\r
-                if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {\r
-                    $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");\r
-                    return false;\r
-                }\r
-                $this->log_message("*** add member (network: $network) $email to $list, already exist!");\r
-                return true;\r
+        );\r
+\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
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+\r
+        if ($http_code != 200) {\r
+            preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
+            if (count($matches) == 0) {\r
+                $this->log_message("*** can't add member (network: $network) $email to $list");\r
+                return false;\r
+            }\r
+            $faultcode = trim($matches[1]);\r
+            $faultstring = trim($matches[2]);\r
+            if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {\r
+                $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");\r
+                return false;\r
             }\r
-            $this->log_message("*** add member (network: $network) $email to $list");\r
+            $this->log_message("*** add member (network: $network) $email to $list, already exist!");\r
             return true;\r
+        }\r
+        $this->log_message("*** add member (network: $network) $email to $list");\r
+        return true;\r
     }\r
 \r
     function getMembershipList($returnData=false) {\r
@@ -837,102 +822,109 @@ class MSN {
             'SOAPAction: '.$this->membership_soap,\r
             '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
-            $curl = curl_init();\r
-            curl_setopt($curl, CURLOPT_URL, $this->membership_url);\r
-            curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-            if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-            curl_setopt($curl, CURLOPT_POST, 1);\r
-            curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-            $data = curl_exec($curl);\r
-            $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-            curl_close($curl);\r
-            $this->debug_message("*** Get Result:\n$data");\r
-            if($http_code != 200) return array();\r
-            $p = $data;\r
-            $aMemberships = array();\r
+        );\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
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+        if($http_code != 200) return false;\r
+        $p = $data;\r
+        $aMemberships = array();\r
+        while (1) {\r
+            //$this->debug_message("search p = $p");\r
+            $start = strpos($p, '<Membership>');\r
+            $end = strpos($p, '</Membership>');\r
+            if ($start === false || $end === false || $start > $end) break;\r
+            //$this->debug_message("start = $start, end = $end");\r
+            $end += 13;\r
+            $sMembership = substr($p, $start, $end - $start);\r
+            $aMemberships[] = $sMembership;\r
+            //$this->debug_message("add sMembership = $sMembership");\r
+            $p = substr($p, $end);\r
+        }\r
+        //$this->debug_message("aMemberships = ".var_export($aMemberships, true));\r
+\r
+        $aContactList = array();\r
+        foreach ($aMemberships as $sMembership) {\r
+            //$this->debug_message("sMembership = $sMembership");\r
+            if (isset($matches)) unset($matches);\r
+            preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);\r
+            if (count($matches) == 0) continue;\r
+            $sMemberRole = $matches[1];\r
+            //$this->debug_message("MemberRole = $sMemberRole");\r
+            if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;\r
+            $p = $sMembership;\r
+            if (isset($aMembers)) unset($aMembers);\r
+            $aMembers = array();\r
             while (1) {\r
                 //$this->debug_message("search p = $p");\r
-                $start = strpos($p, '<Membership>');\r
-                $end = strpos($p, '</Membership>');\r
+                $start = strpos($p, '<Member xsi:type="');\r
+                $end = strpos($p, '</Member>');\r
                 if ($start === false || $end === false || $start > $end) break;\r
                 //$this->debug_message("start = $start, end = $end");\r
-                $end += 13;\r
-                $sMembership = substr($p, $start, $end - $start);\r
-                $aMemberships[] = $sMembership;\r
-                //$this->debug_message("add sMembership = $sMembership");\r
+                $end += 9;\r
+                $sMember = substr($p, $start, $end - $start);\r
+                $aMembers[] = $sMember;\r
+                //$this->debug_message("add sMember = $sMember");\r
                 $p = substr($p, $end);\r
             }\r
-            //$this->debug_message("aMemberships = ".var_export($aMemberships, true));\r
-\r
-            $aContactList = array();\r
-            foreach ($aMemberships as $sMembership) {\r
-                //$this->debug_message("sMembership = $sMembership");\r
+            //$this->debug_message("aMembers = ".var_export($aMembers, true));\r
+            foreach ($aMembers as $sMember) {\r
+                //$this->debug_message("sMember = $sMember");\r
                 if (isset($matches)) unset($matches);\r
-                preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);\r
+                preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);\r
+                if (count($matches) == 0) continue;\r
+                $sMemberType = $matches[1];\r
+                //$this->debug_message("MemberType = $sMemberType");\r
+                $network = -1;\r
+                preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);\r
                 if (count($matches) == 0) continue;\r
-                $sMemberRole = $matches[1];\r
-                //$this->debug_message("MemberRole = $sMemberRole");\r
-                if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;\r
-                $p = $sMembership;\r
-                if (isset($aMembers)) unset($aMembers);\r
-                $aMembers = array();\r
-                while (1) {\r
-                    //$this->debug_message("search p = $p");\r
-                    $start = strpos($p, '<Member xsi:type="');\r
-                    $end = strpos($p, '</Member>');\r
-                    if ($start === false || $end === false || $start > $end) break;\r
-                    //$this->debug_message("start = $start, end = $end");\r
-                    $end += 9;\r
-                    $sMember = substr($p, $start, $end - $start);\r
-                    $aMembers[] = $sMember;\r
-                    //$this->debug_message("add sMember = $sMember");\r
-                    $p = substr($p, $end);\r
+                $id = $matches[1];\r
+                if ($sMemberType == 'PassportMember') {\r
+                    if (strpos($sMember, '<Type>Passport</Type>') === false) continue;\r
+                    $network = 1;\r
+                    preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);\r
                 }\r
-                //$this->debug_message("aMembers = ".var_export($aMembers, true));\r
-                foreach ($aMembers as $sMember) {\r
-                    //$this->debug_message("sMember = $sMember");\r
-                    if (isset($matches)) unset($matches);\r
-                    preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);\r
+                else if ($sMemberType == 'EmailMember') {\r
+                    if (strpos($sMember, '<Type>Email</Type>') === false) continue;\r
+                    // Value is 32: or 32:YAHOO\r
+                    preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);\r
                     if (count($matches) == 0) continue;\r
-                    $sMemberType = $matches[1];\r
-                    //$this->debug_message("MemberType = $sMemberType");\r
-                    $network = -1;\r
-                    preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);\r
-                    if (count($matches) == 0) continue;\r
-                    $id = $matches[1];\r
-                    if ($sMemberType == 'PassportMember') {\r
-                        if (strpos($sMember, '<Type>Passport</Type>') === false) continue;\r
-                        $network = 1;\r
-                        preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);\r
-                    }\r
-                    else if ($sMemberType == 'EmailMember') {\r
-                        if (strpos($sMember, '<Type>Email</Type>') === false) continue;\r
-                        // Value is 32: or 32:YAHOO\r
-                        preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);\r
-                        if (count($matches) == 0) continue;\r
-                        if ($matches[1] != 32) continue;\r
-                        $network = 32;\r
-                        preg_match('#<Email>(.*)</Email>#', $sMember, $matches);\r
-                    }\r
-                    if ($network == -1) continue;\r
-                    if (count($matches) > 0) {\r
-                        $email = $matches[1];\r
-                        @list($u_name, $u_domain) = @explode('@', $email);\r
-                        if ($u_domain == NULL) continue;\r
-                        $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;\r
-                        $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
-                    }\r
+                    if ($matches[1] != 32) continue;\r
+                    $network = 32;\r
+                    preg_match('#<Email>(.*)</Email>#', $sMember, $matches);\r
+                }\r
+                if ($network == -1) continue;\r
+                if (count($matches) > 0) {\r
+                    $email = $matches[1];\r
+                    @list($u_name, $u_domain) = @explode('@', $email);\r
+                    if ($u_domain == NULL) continue;\r
+                    $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;\r
+                    $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
                 }\r
             }\r
-            return $aContactList;\r
+        }\r
+        return $aContactList;\r
     }\r
 \r
+    /**\r
+     * Connect to the NS server\r
+     * @param $user Username\r
+     * @param $password Password\r
+     * @param $redirect_server Redirect server\r
+     * @param $redirect_port Redirect port\r
+     */\r
     private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {\r
         $this->id = 1;\r
         if ($redirect_server === '') {\r
@@ -1092,106 +1084,233 @@ class MSN {
         // never goto here\r
     }\r
 \r
-    function derive_key($key, $magic) {\r
-        $hash1 = mhash(MHASH_SHA1, $magic, $key);\r
-        $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);\r
-        $hash3 = mhash(MHASH_SHA1, $hash1, $key);\r
-        $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);\r
-        return $hash2.substr($hash4, 0, 4);\r
-    }\r
-\r
-    function generateLoginBLOB($key, $challenge) {\r
-        $key1 = base64_decode($key);\r
-        $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');\r
-        $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');\r
-\r
-        // get hash of challenge using key2\r
-        $hash = mhash(MHASH_SHA1, $challenge, $key2);\r
-\r
-        // get 8 bytes random data\r
-        $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);\r
-\r
-        $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);\r
-\r
-        $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);\r
-        $blob .= $iv;\r
-        $blob .= $hash;\r
-        $blob .= $cipher;\r
-\r
-        return base64_encode($blob);\r
-    }\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
-            return false;\r
+    /**\r
+     * Sign onto the NS server and retrieve the address book\r
+     */\r
+    public function signon() {\r
+        $this->log_message("*** try to connect to MSN network");\r
+        while(!$this->connect($this->user, $this->password))\r
+        {\r
+            $this->signonFailed("!!! Can't connect to server: $this->error");\r
         }\r
-        $t = htmlspecialchars($matches[1]);\r
-        $p = htmlspecialchars($matches[2]);\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
-               xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
-               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
-<soap:Header>\r
-  <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
-    <t>'.$t.'</t>\r
-    <p>'.$p.'</p>\r
-  </PassportCookie>\r
-</soap:Header>\r
-<soap:Body>\r
-  <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />\r
-</soap:Body>\r
-</soap:Envelope>';\r
-\r
-        $header_array = array(\r
-            'SOAPAction: '.$this->oim_maildata_soap,\r
-            'Content-Type: text/xml; charset=utf-8',\r
-            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
-            );\r
-\r
-            $this->debug_message("*** URL: $this->oim_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
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-            if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-            curl_setopt($curl, CURLOPT_POST, 1);\r
-            curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-            $data = curl_exec($curl);\r
-            $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-            curl_close($curl);\r
-            $this->debug_message("*** Get Result:\n$data");\r
-\r
-            if ($http_code != 200) {\r
-                $this->debug_message("*** Can't get OIM 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
-            }\r
-            return $matches[2];\r
-    }\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
-            return false;\r
+        if(!$this->UpdateContacts()) {\r
+            $this->signonFailed('!!! Could not update contacts');\r
+            return $this->signon();\r
         }\r
-        $t = htmlspecialchars($matches[1]);\r
-        $p = htmlspecialchars($matches[2]);\r
-\r
-        // read OIM\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+        $this->LastPing=time();\r
+        $this->log_message("*** connected, wait for command");\r
+        $start_tm = time();\r
+        $ping_tm = time();\r
+        if(($this->aContactList = $this->getMembershipList()) === false) {\r
+            $this->signonFailed('!!! Could not get Membership List');\r
+            return $this->signon();\r
+        }\r
+        if ($this->update_pending) {\r
+            if (is_array($this->aContactList)) {\r
+                $pending = 'Pending';\r
+                foreach ($this->aContactList as $u_domain => $aUserList) {\r
+                    foreach ($aUserList as $u_name => $aNetworks) {\r
+                        foreach ($aNetworks as $network => $aData) {\r
+                            if (isset($aData[$pending])) {\r
+                                // pending list\r
+                                $cnt = 0;\r
+                                foreach (array('Allow', 'Reverse') as $list) {\r
+                                    if (isset($aData[$list]))\r
+                                        $cnt++;\r
+                                    else {\r
+                                        if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
+                                            $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
+                                            $cnt++;\r
+                                        }\r
+                                    }\r
+                                }\r
+                                if ($cnt >= 2) {\r
+                                    $id = $aData[$pending];\r
+                                    // we can delete it from pending now\r
+                                    if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
+                                        unset($this->aContactList[$u_domain][$u_name][$network][$pending]);\r
+                                }\r
+                            }\r
+                            else {\r
+                                // sync list\r
+                                foreach (array('Allow', 'Reverse') as $list) {\r
+                                    if (!isset($aData[$list])) {\r
+                                        if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
+                                            $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        $n = 0;\r
+        $sList = '';\r
+        $len = 0;\r
+        if (is_array($this->aContactList)) {\r
+            foreach ($this->aContactList as $u_domain => $aUserList) {\r
+                $str = '<d n="'.$u_domain.'">';\r
+                $len += strlen($str);\r
+                if ($len > 7400) {\r
+                    $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+                    $n++;\r
+                    $sList = '';\r
+                    $len = strlen($str);\r
+                }\r
+                $sList .= $str;\r
+                foreach ($aUserList as $u_name => $aNetworks) {\r
+                    foreach ($aNetworks as $network => $status) {\r
+                        $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
+                        $len += strlen($str);\r
+                        // max: 7500, but <ml l="1"></d></ml> is 19,\r
+                        // so we use 7475\r
+                        if ($len > 7475) {\r
+                            $sList .= '</d>';\r
+                            $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+                            $n++;\r
+                            $sList = '<d n="'.$u_domain.'">'.$str;\r
+                            $len = strlen($sList);\r
+                        }\r
+                        else\r
+                            $sList .= $str;\r
+                    }\r
+                }\r
+                $sList .= '</d>';\r
+            }\r
+        }\r
+        $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+        // NS: >>> BLP {id} BL\r
+        $this->ns_writeln("BLP $this->id BL");\r
+        foreach ($this->aADL as $str) {\r
+            $len = strlen($str);\r
+            // NS: >>> ADL {id} {size}\r
+            $this->ns_writeln("ADL $this->id $len");\r
+            $this->ns_writedata($str);\r
+        }\r
+        // NS: >>> PRP {id} MFN name\r
+        if ($this->alias == '') $this->alias = $user;\r
+        $aliasname = rawurlencode($this->alias);\r
+        $this->ns_writeln("PRP $this->id MFN $aliasname");\r
+        //設定個人大頭貼\r
+        //$MsnObj=$this->PhotoStckObj();\r
+        // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
+        $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
+        if($this->PhotoStickerFile!==false)\r
+            $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+        // NS: >>> UUX {id} length\r
+        $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
+        $len = strlen($str);\r
+        $this->ns_writeln("UUX $this->id $len");\r
+        $this->ns_writedata($str);\r
+    }\r
+    \r
+    private function signonFailed($message) {\r
+        $this->log_message($message);\r
+        $this->callHandler('ConnectFailed', NULL);\r
+        $this->NSRetryWait($this->retry_wait);\r
+    }\r
+    \r
+    function derive_key($key, $magic) {\r
+        $hash1 = mhash(MHASH_SHA1, $magic, $key);\r
+        $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);\r
+        $hash3 = mhash(MHASH_SHA1, $hash1, $key);\r
+        $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);\r
+        return $hash2.substr($hash4, 0, 4);\r
+    }\r
+\r
+    function generateLoginBLOB($key, $challenge) {\r
+        $key1 = base64_decode($key);\r
+        $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');\r
+        $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');\r
+\r
+        // get hash of challenge using key2\r
+        $hash = mhash(MHASH_SHA1, $challenge, $key2);\r
+\r
+        // get 8 bytes random data\r
+        $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);\r
+\r
+        $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);\r
+\r
+        $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);\r
+        $blob .= $iv;\r
+        $blob .= $hash;\r
+        $blob .= $cipher;\r
+\r
+        return base64_encode($blob);\r
+    }\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
+            return false;\r
+        }\r
+        $t = htmlspecialchars($matches[1]);\r
+        $p = htmlspecialchars($matches[2]);\r
+        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+               xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
+<soap:Header>\r
+  <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+    <t>'.$t.'</t>\r
+    <p>'.$p.'</p>\r
+  </PassportCookie>\r
+</soap:Header>\r
+<soap:Body>\r
+  <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+        $header_array = array(\r
+            'SOAPAction: '.$this->oim_maildata_soap,\r
+            'Content-Type: text/xml; charset=utf-8',\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+        );\r
+\r
+        $this->debug_message("*** URL: $this->oim_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
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+\r
+        if ($http_code != 200) {\r
+            $this->debug_message("*** Can't get OIM 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
+        }\r
+        return $matches[2];\r
+    }\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
+            return false;\r
+        }\r
+        $t = htmlspecialchars($matches[1]);\r
+        $p = htmlspecialchars($matches[2]);\r
+\r
+        // read OIM\r
+        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
                xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
 <soap:Header>\r
@@ -1212,60 +1331,60 @@ class MSN {
             'SOAPAction: '.$this->oim_read_soap,\r
             'Content-Type: text/xml; charset=utf-8',\r
             'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
-            );\r
-\r
-            $this->debug_message("*** URL: $this->oim_read_url");\r
-            $this->debug_message("*** Sending SOAP:\n$XML");\r
-            $curl = curl_init();\r
-            curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);\r
-            curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-            if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-            curl_setopt($curl, CURLOPT_POST, 1);\r
-            curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-            $data = curl_exec($curl);\r
-            $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-            curl_close($curl);\r
-            $this->debug_message("*** Get Result:\n$data");\r
-\r
-            if ($http_code != 200) {\r
-                $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");\r
-                return false;\r
-            }\r
+        );\r
 \r
-            // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?\r
-            // multi-lines?\r
-            $start = strpos($data, '<GetMessageResult>');\r
-            $end = strpos($data, '</GetMessageResult>');\r
-            if ($start === false || $end === false || $start > $end) {\r
-                $this->debug_message("*** Can't get OIM: $msgid");\r
-                return false;\r
-            }\r
-            $lines = substr($data, $start + 18, $end - $start);\r
-            $aLines = @explode("\n", $lines);\r
-            $header = true;\r
-            $ignore = false;\r
-            $sOIM = '';\r
-            foreach ($aLines as $line) {\r
-                $line = rtrim($line);\r
-                if ($header) {\r
-                    if ($line === '') {\r
-                        $header = false;\r
-                        continue;\r
-                    }\r
+        $this->debug_message("*** URL: $this->oim_read_url");\r
+        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        $curl = curl_init();\r
+        curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);\r
+        curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+\r
+        if ($http_code != 200) {\r
+            $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");\r
+            return false;\r
+        }\r
+\r
+        // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?\r
+        // multi-lines?\r
+        $start = strpos($data, '<GetMessageResult>');\r
+        $end = strpos($data, '</GetMessageResult>');\r
+        if ($start === false || $end === false || $start > $end) {\r
+            $this->debug_message("*** Can't get OIM: $msgid");\r
+            return false;\r
+        }\r
+        $lines = substr($data, $start + 18, $end - $start);\r
+        $aLines = @explode("\n", $lines);\r
+        $header = true;\r
+        $ignore = false;\r
+        $sOIM = '';\r
+        foreach ($aLines as $line) {\r
+            $line = rtrim($line);\r
+            if ($header) {\r
+                if ($line === '') {\r
+                    $header = false;\r
                     continue;\r
                 }\r
-                // stop at empty lines\r
-                if ($line === '') break;\r
-                $sOIM .= $line;\r
+                continue;\r
             }\r
-            $sMsg = base64_decode($sOIM);\r
-            $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
+            // stop at empty lines\r
+            if ($line === '') break;\r
+            $sOIM .= $line;\r
+        }\r
+        $sMsg = base64_decode($sOIM);\r
+        $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
 \r
-            // delete OIM\r
-            $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+        // delete OIM\r
+        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
                xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
@@ -1284,33 +1403,33 @@ class MSN {
 </soap:Body>\r
 </soap:Envelope>';\r
 \r
-            $header_array = array(\r
+        $header_array = array(\r
             'SOAPAction: '.$this->oim_del_soap,\r
             'Content-Type: text/xml; charset=utf-8',\r
             'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
-            );\r
-\r
-            $this->debug_message("*** URL: $this->oim_del_url");\r
-            $this->debug_message("*** Sending SOAP:\n$XML");\r
-            $curl = curl_init();\r
-            curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);\r
-            curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-            if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-            curl_setopt($curl, CURLOPT_POST, 1);\r
-            curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-            $data = curl_exec($curl);\r
-            $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-            curl_close($curl);\r
-            $this->debug_message("*** Get Result:\n$data");\r
-\r
-            if ($http_code != 200)\r
+        );\r
+\r
+        $this->debug_message("*** URL: $this->oim_del_url");\r
+        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        $curl = curl_init();\r
+        curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);\r
+        curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+\r
+        if ($http_code != 200)\r
             $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");\r
-            else\r
+        else\r
             $this->debug_message("*** OIM ($msgid) deleted");\r
-            return $sMsg;\r
+        return $sMsg;\r
     }\r
     private function NSLogout() {\r
         if (is_resource($this->NSfp) && !feof($this->NSfp)) {\r
@@ -1395,7 +1514,7 @@ class MSN {
                             }\r
                             if ($oim_result === false || $oim_result['auth_policy'] !== false)\r
                             {\r
-                                if ($re_login)\r
+                                if ($this->re_login)\r
                                 {\r
                                     $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");\r
                                     break;\r
@@ -1480,7 +1599,6 @@ class MSN {
                 $this->log_message("*** connected, wait for command");\r
                 $start_tm = time();\r
                 $ping_tm = time();\r
-                stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
                     $aContactList = $this->getMembershipList();\r
                     if ($this->update_pending) {\r
                         if (is_array($aContactList)) {\r
@@ -1659,13 +1777,13 @@ class MSN {
                             $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
                             else\r
                             {\r
-                                $re_login = false;\r
+                                $this->re_login = false;\r
                                 $cnt = 0;\r
                                 foreach (array('Allow', 'Reverse') as $list)\r
                                 {\r
                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
                                     {\r
-                                        if ($re_login) {\r
+                                        if ($this->re_login) {\r
                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
                                             continue;\r
                                         }\r
@@ -1676,7 +1794,7 @@ class MSN {
                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
                                             continue;\r
                                         }\r
-                                        $re_login = true;\r
+                                        $this->re_login = true;\r
                                         $this->ticket = $aTickets;\r
                                         $this->log_message("**** get new ticket, try it again");\r
                                         if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
@@ -1770,7 +1888,7 @@ class MSN {
                             $this->log_message("*** ingnore MSG not for OIM");\r
                             break;\r
                         }\r
-                        $re_login = false;\r
+                        $this->re_login = false;\r
                         if (strcasecmp($maildata, 'too-large') == 0) {\r
                             $this->log_message("*** large mail-data, need to get the data via SOAP");\r
                             $maildata = $this->getOIM_maildata();\r
@@ -1783,7 +1901,7 @@ class MSN {
                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
                                     break;\r
                                 }\r
-                                $re_login = true;\r
+                                $this->re_login = true;\r
                                 $this->ticket = $aTickets;\r
                                 $this->log_message("**** get new ticket, try it again");\r
                                 $maildata = $this->getOIM_maildata();\r
@@ -1849,7 +1967,7 @@ class MSN {
                             $sMsg = $this->getOIM_message($oim_msgid);\r
                             if ($sMsg === false) {\r
                                 $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
-                                if ($re_login) {\r
+                                if ($this->re_login) {\r
                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
                                     continue;\r
                                 }\r
@@ -1859,7 +1977,7 @@ class MSN {
                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
                                     continue;\r
                                 }\r
-                                $re_login = true;\r
+                                $this->re_login = true;\r
                                 $this->ticket = $aTickets;\r
                                 $this->log_message("**** get new ticket, try it again");\r
                                 $sMsg = $this->getOIM_message($oim_msgid);\r
@@ -2069,30 +2187,6 @@ class MSN {
         return $this->NsLogout();\r
     }\r
 \r
-    /*public function SendMessage($Message, $To)\r
-    {\r
-        $FileName = MSN_CLASS_SPOOL_DIR.'/'.strftime('%Y%m%d%H%M%S',time()).'_'.posix_getpid().'_sendMessage.msn';\r
-        if(!is_array($To))\r
-        $To=array($To);\r
-        $Receiver='';\r
-        foreach($To as $Email)\r
-        {\r
-            list($name,$host,$network)=explode('@',$Email);\r
-            $network=$network==''?1:$network;\r
-            if($network==1 && $this->SwitchBoardProcess && $this->SwitchBoardSessionUser=="$name@$host" )\r
-            {\r
-                $this->debug_message("*** SendMessage to $Receiver use SB message queue.");\r
-                array_push($this->SwitchBoardMessageQueue,$Message);\r
-                continue;\r
-            }\r
-            $Receiver.="$name@$host@$network,";\r
-        }\r
-        if($Receiver=='') return;\r
-        $Receiver=substr($Receiver,0,-1);\r
-        $this->debug_message("*** SendMessage to $Receiver use File queue.");\r
-        file_put_contents($FileName,"TO: $Receiver\n$Message\n");\r
-    }*/\r
-\r
     function getChallenge($code)\r
     {\r
         // MSNP15\r
@@ -2190,9 +2284,9 @@ class MSN {
     {\r
         $SessionEnd=false;\r
         $Joined=false;\r
-        $id=1;\r
+        $this->id=1;\r
         $LastActive=time();\r
-        stream_set_timeout($this->SBFp, $this->SBTimeout);\r
+        stream_set_timeout($this->SBfp, $this->SBStreamTimeout);\r
         switch($Action)\r
         {\r
             case 'Active':\r
@@ -2200,8 +2294,7 @@ class MSN {
                 $user=$Param['user'];\r
                 $this->SwitchBoardMessageQueue=$Param['Msg'];\r
                 // SB: >>> USR {id} {user} {cki}\r
-                $this->SB_writeln("USR $id $this->user $cki_code");\r
-                $id++;\r
+                $this->SB_writeln("USR $this->id $this->user $cki_code");\r
                 $this->SwitchBoardSessionUser=$user;\r
                 break;\r
             case 'Passive':\r
@@ -2209,14 +2302,13 @@ class MSN {
                 $sid=$Param['sid'];\r
                 $user=$Param['user'];\r
                 // SB: >>> ANS {id} {user} {ticket} {session_id}\r
-                $this->SB_writeln("ANS $id $this->user $ticket $sid");\r
-                $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
+        while((!feof($this->SBfp))&&(!$SessionEnd))\r
         {\r
             $data = $this->SB_readln();\r
             if($this->kill_me)\r
@@ -2242,14 +2334,12 @@ class MSN {
                     {\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 $id N $len");\r
-                        $id++;\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 $id N $len");\r
-                    $id++;\r
+                    $this->SB_writeln("MSG $this->id N $len");\r
                     $this->SB_writedata($aMessage);\r
                 }\r
                 $this->SwitchBoardMessageQueue=array();\r
@@ -2274,8 +2364,7 @@ class MSN {
                     // 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 $id $user");\r
-                    $id++;\r
+                    $this->SB_writeln("CAL $this->id $user");\r
                     break;\r
                 case 'CAL':\r
                     // SB: <<< CAL {id} RINGING {?}\r
@@ -2431,8 +2520,7 @@ class MSN {
                             $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_writeln("MSG $this->id D $len");\r
                             $this->SB_writedata($message);\r
                             $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");\r
                             $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));                            \r
@@ -2468,8 +2556,7 @@ class MSN {
                                 "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_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
@@ -2494,8 +2581,7 @@ class MSN {
                                 "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 $id D ".strlen($message));\r
-                            $id++;\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
@@ -2528,8 +2614,7 @@ class MSN {
                                         "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 $id D ".strlen($message));\r
-                                    $id++;\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
@@ -2651,22 +2736,22 @@ class MSN {
             }\r
             if(!$this->IsIgnoreMail($user)) $LastActive = time();\r
         }\r
-        if (feof($this->SBFp))\r
+        if (feof($this->SBfp))\r
         {\r
             // lost connection? error? try OIM later\r
-            @fclose($this->SBFp);\r
+            @fclose($this->SBfp);\r
             return false;\r
         }\r
         $this->SB_writeln("OUT");\r
-        @fclose($this->SBFp);\r
+        @fclose($this->SBfp);\r
         return true;\r
     }\r
-    private function switchboard_control($ip, $port, $cki_code, $user, $Messages)\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
+        $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
@@ -2677,119 +2762,14 @@ class MSN {
     {\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
+        $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
-    private function sendOIM($to, $sMessage, $lockkey)\r
-    {\r
-        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
-               xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
-               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
-<soap:Header>\r
-  <From memberName="'.$this->user.'"\r
-        friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="\r
-        xml:lang="zh-TW"\r
-        proxy="MSNMSGR"\r
-        xmlns="http://messenger.msn.com/ws/2004/09/oim/"\r
-        msnpVer="'.$this->protocol.'"\r
-        buildVer="'.$this->buildver.'"/>\r
-  <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
-  <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"\r
-          appid="'.$this->prod_id.'"\r
-          lockkey="'.$lockkey.'"\r
-          xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
-  <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">\r
-    <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>\r
-    <MessageNumber>1</MessageNumber>\r
-  </Sequence>\r
-</soap:Header>\r
-<soap:Body>\r
-  <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>\r
-  <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0\r
-Content-Type: text/plain; charset=UTF-8\r
-Content-Transfer-Encoding: base64\r
-X-OIM-Message-Type: OfflineMessage\r
-X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}\r
-X-OIM-Sequence-Num: 1\r
-\r
-'.chunk_split(base64_encode($sMessage)).'\r
-  </Content>\r
-</soap:Body>\r
-</soap:Envelope>';\r
-\r
-        $header_array = array(\r
-            'SOAPAction: '.$this->oim_send_soap,\r
-            'Content-Type: text/xml',\r
-            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
-            );\r
-\r
-            $this->debug_message("*** URL: $this->oim_send_url");\r
-            $this->debug_message("*** Sending SOAP:\n$XML");\r
-            $curl = curl_init();\r
-            curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);\r
-            curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
-            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
-            if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
-            curl_setopt($curl, CURLOPT_POST, 1);\r
-            curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
-            $data = curl_exec($curl);\r
-            $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
-            curl_close($curl);\r
-            $this->debug_message("*** Get Result:\n$data");\r
-\r
-            if ($http_code == 200) {\r
-                $this->debug_message("*** OIM sent for $to");\r
-                return true;\r
-            }\r
-\r
-            $challenge = false;\r
-            $auth_policy = false;\r
-            // the lockkey is invalid, authenticated fail, we need challenge it again\r
-            // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>\r
-            preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);\r
-            if (count($matches) != 0) {\r
-                // yes, we get new LockKeyChallenge\r
-                $challenge = $matches[2];\r
-                $this->debug_message("*** OIM need new challenge ($challenge) for $to");\r
-            }\r
-            // auth policy error\r
-            // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>\r
-            preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);\r
-            if (count($matches) != 0) {\r
-                $auth_policy = $matches[2];\r
-                $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");\r
-            }\r
-            if ($auth_policy === false && $challenge === false) {\r
-                //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>\r
-                preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);\r
-                if (count($matches) == 0) {\r
-                    // no error, we assume the OIM is sent\r
-                    $this->debug_message("*** OIM sent for $to");\r
-                    return true;\r
-                }\r
-                $err_code = $matches[2];\r
-                //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>\r
-                preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);\r
-                if (count($matches) > 0)\r
-                $err_msg = $matches[1];\r
-                else\r
-                $err_msg = '';\r
-                $this->debug_message("*** OIM failed for $to");\r
-                $this->debug_message("*** OIM Error code: $err_code");\r
-                $this->debug_message("*** OIM Error Message: $err_msg");\r
-                return false;\r
-            }\r
-            return array('challenge' => $challenge, 'auth_policy' => $auth_policy);\r
-    }\r
+    }*/\r
 \r
     // read data for specified size\r
     private function ns_readdata($size) {\r
@@ -2831,11 +2811,11 @@ X-OIM-Sequence-Num: 1
     }\r
 \r
     // read data for specified size for SB\r
-    private function sb_readdata($size) {\r
+    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($this->SBfp)) {\r
+            $buf = @fread($this->SBfp, $size - $count);\r
             $data .= $buf;\r
             $count += strlen($buf);\r
             if ($count >= $size) break;\r
@@ -2845,8 +2825,8 @@ X-OIM-Sequence-Num: 1
     }\r
 \r
     // read one line for SB\r
-    private function sb_readln() {\r
-        $data = @fgets($this->SBFp, 4096);\r
+    private function sb_readln($socket) {\r
+        $data = @fgets($socket, 4096);\r
         if ($data !== false) {\r
             $data = trim($data);\r
             $this->debug_message("SB: <<< $data");\r
@@ -2856,16 +2836,16 @@ X-OIM-Sequence-Num: 1
 \r
     // write to server for SB, append \r\n, also increase id\r
     // switchboard server only accept \r\n, it will lost connection if just \n only\r
-    private function sb_writeln($data) {\r
-        @fwrite($this->SBFp, $data."\r\n");\r
+    private function sb_writeln($socket, &$id, $data) {\r
+        @fwrite($socket, $data."\r\n");\r
         $this->debug_message("SB: >>> $data");\r
-        $this->id++;\r
+        $id++;\r
         return;\r
     }\r
 \r
     // write data to server\r
-    private function sb_writedata($data) {\r
-        @fwrite($this->SBFp, $data);\r
+    private function sb_writedata($socket, $data) {\r
+        @fwrite($socket, $data);\r
         $this->debug_message("SB: >>> $data");\r
         return;\r
     }\r
@@ -2943,6 +2923,7 @@ X-OIM-Sequence-Num: 1
         $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
@@ -2953,6 +2934,7 @@ X-OIM-Sequence-Num: 1
         }\r
         return $Data;\r
     }\r
+    \r
     private function GetPictureFilePath($Context)\r
     {\r
         $MsnObj=base64_decode($Context);\r
@@ -2963,6 +2945,7 @@ X-OIM-Sequence-Num: 1
         return $this->MsnObjArray[$location];\r
         return false;\r
     }\r
+    \r
     private function GetMsnObjDefine($Message)\r
     {\r
         $DefineString='';\r
@@ -2974,154 +2957,16 @@ X-OIM-Sequence-Num: 1
         }\r
         return $DefineString;\r
     }\r
+    \r
     /**\r
-     * Receive Message Overload Function\r
-     * @param $Sender\r
-     * @param $Message\r
-     * @param $Network   1 => msn , 32 =>yahoo\r
-     * @param $IsOIM\r
-     * @return unknown_type\r
-     */\r
-    protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}\r
-    /**\r
-     * Remove Us From Member List Overload Function\r
-     * @param $User\r
-     * @param $Message\r
-     * @param $Network   1 => msn , 32 =>yahoo\r
-     * @return unknown_type\r
-     */\r
-    protected function RemoveUsFromMemberList($User,$Network){}\r
-    /**\r
-     * Add Us to Member List Overload Function\r
-     * @param $User\r
-     * @param $Message\r
-     * @param $Network   1 => msn , 32 =>yahoo\r
-     * @return unknown_type\r
+     * Read and handle incoming command from NS\r
      */\r
-    protected function AddUsToMemberList($User,$Network){}\r
-    \r
-    public function signon() {\r
-        $this->log_message("*** try to connect to MSN network");\r
-        while(!$this->connect($this->user, $this->password))\r
-        {\r
-            $this->log_message("!!! Can't connect to server: $this->error");\r
-            $this->callHandler('ConnectFailed', NULL);\r
-            $this->NSRetryWait($this->retry_wait);\r
-        }\r
-        $this->UpdateContacts();\r
-        $this->LastPing=time();\r
-        $this->log_message("*** connected, wait for command");\r
-        $start_tm = time();\r
-        $ping_tm = time();\r
-        stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
-        $this->aContactList = $this->getMembershipList();\r
-        if ($this->update_pending) {\r
-            if (is_array($this->aContactList)) {\r
-                $pending = 'Pending';\r
-                foreach ($this->aContactList as $u_domain => $aUserList) {\r
-                    foreach ($aUserList as $u_name => $aNetworks) {\r
-                        foreach ($aNetworks as $network => $aData) {\r
-                            if (isset($aData[$pending])) {\r
-                                // pending list\r
-                                $cnt = 0;\r
-                                foreach (array('Allow', 'Reverse') as $list) {\r
-                                    if (isset($aData[$list]))\r
-                                    $cnt++;\r
-                                    else {\r
-                                        if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
-                                            $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
-                                            $cnt++;\r
-                                        }\r
-                                    }\r
-                                }\r
-                                if ($cnt >= 2) {\r
-                                    $id = $aData[$pending];\r
-                                    // we can delete it from pending now\r
-                                    if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
-                                    unset($this->aContactList[$u_domain][$u_name][$network][$pending]);\r
-                                }\r
-                            }\r
-                            else {\r
-                                // sync list\r
-                                foreach (array('Allow', 'Reverse') as $list) {\r
-                                    if (!isset($aData[$list])) {\r
-                                        if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
-                                        $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
-                                    }\r
-                                }\r
-                            }\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-        $n = 0;\r
-        $sList = '';\r
-        $len = 0;\r
-        if (is_array($this->aContactList)) {\r
-            foreach ($this->aContactList as $u_domain => $aUserList) {\r
-                $str = '<d n="'.$u_domain.'">';\r
-                $len += strlen($str);\r
-                if ($len > 7400) {\r
-                    $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
-                    $n++;\r
-                    $sList = '';\r
-                    $len = strlen($str);\r
-                }\r
-                $sList .= $str;\r
-                foreach ($aUserList as $u_name => $aNetworks) {\r
-                    foreach ($aNetworks as $network => $status) {\r
-                        $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
-                        $len += strlen($str);\r
-                        // max: 7500, but <ml l="1"></d></ml> is 19,\r
-                        // so we use 7475\r
-                        if ($len > 7475) {\r
-                            $sList .= '</d>';\r
-                            $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
-                            $n++;\r
-                            $sList = '<d n="'.$u_domain.'">'.$str;\r
-                            $len = strlen($sList);\r
-                        }\r
-                        else\r
-                        $sList .= $str;\r
-                    }\r
-                }\r
-                $sList .= '</d>';\r
-            }\r
-        }\r
-        $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
-        // NS: >>> BLP {id} BL\r
-        $this->ns_writeln("BLP $this->id BL");\r
-        foreach ($this->aADL as $str) {\r
-            $len = strlen($str);\r
-            // NS: >>> ADL {id} {size}\r
-            $this->ns_writeln("ADL $this->id $len");\r
-            $this->ns_writedata($str);\r
-        }\r
-        // NS: >>> PRP {id} MFN name\r
-        if ($this->alias == '') $this->alias = $user;\r
-        $aliasname = rawurlencode($this->alias);\r
-        $this->ns_writeln("PRP $this->id MFN $aliasname");\r
-        //設定個人大頭貼\r
-        //$MsnObj=$this->PhotoStckObj();\r
-        // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
-        $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
-        if($this->PhotoStickerFile!==false)\r
-            $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
-        // NS: >>> UUX {id} length\r
-        $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
-        $len = strlen($str);\r
-        $this->ns_writeln("UUX $this->id $len");\r
-        $this->ns_writedata($str);\r
-    }\r
-    \r
-    public function NSreceive() {\r
-        $this->log_message("*** startup ***");\r
-        \r
+    public function nsReceive() {\r
         // Sign in again if not signed in or socket failed\r
         if (!is_resource($this->NSfp) || feof($this->NSfp)) {\r
             $this->callHandler('Reconnect', NULL);\r
             $this->signon();\r
+            return;\r
         }\r
         \r
         $data = $this->ns_readln();\r
@@ -3178,13 +3023,13 @@ X-OIM-Sequence-Num: 1
                             $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
                             else\r
                             {\r
-                                $re_login = false;\r
+                                $this->re_login = false;\r
                                 $cnt = 0;\r
                                 foreach (array('Allow', 'Reverse') as $list)\r
                                 {\r
                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
                                     {\r
-                                        if ($re_login) {\r
+                                        if ($this->re_login) {\r
                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
                                             continue;\r
                                         }\r
@@ -3195,7 +3040,7 @@ X-OIM-Sequence-Num: 1
                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
                                             continue;\r
                                         }\r
-                                        $re_login = true;\r
+                                        $this->re_login = true;\r
                                         $this->ticket = $aTickets;\r
                                         $this->log_message("**** get new ticket, try it again");\r
                                         if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
@@ -3289,7 +3134,7 @@ X-OIM-Sequence-Num: 1
                             $this->log_message("*** ingnore MSG not for OIM");\r
                             break;\r
                         }\r
-                        $re_login = false;\r
+                        $this->re_login = false;\r
                         if (strcasecmp($maildata, 'too-large') == 0) {\r
                             $this->log_message("*** large mail-data, need to get the data via SOAP");\r
                             $maildata = $this->getOIM_maildata();\r
@@ -3302,7 +3147,7 @@ X-OIM-Sequence-Num: 1
                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
                                     break;\r
                                 }\r
-                                $re_login = true;\r
+                                $this->re_login = true;\r
                                 $this->ticket = $aTickets;\r
                                 $this->log_message("**** get new ticket, try it again");\r
                                 $maildata = $this->getOIM_maildata();\r
@@ -3368,7 +3213,7 @@ X-OIM-Sequence-Num: 1
                             $sMsg = $this->getOIM_message($oim_msgid);\r
                             if ($sMsg === false) {\r
                                 $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
-                                if ($re_login) {\r
+                                if ($this->re_login) {\r
                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
                                     continue;\r
                                 }\r
@@ -3378,7 +3223,7 @@ X-OIM-Sequence-Num: 1
                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
                                     continue;\r
                                 }\r
-                                $re_login = true;\r
+                                $this->re_login = true;\r
                                 $this->ticket = $aTickets;\r
                                 $this->log_message("**** get new ticket, try it again");\r
                                 $sMsg = $this->getOIM_message($oim_msgid);\r
@@ -3590,24 +3435,88 @@ X-OIM-Sequence-Num: 1
         }\r
     }\r
     \r
-    public function sendMessageViaSB($message, $to) {\r
+    /**\r
+     * Read and handle incoming command/message from\r
+     * a switchboard session socket\r
+     */\r
+    public function sbReceive() {\r
+        \r
+    }\r
+\r
+    /**\r
+     * Send a request for a switchboard session\r
+     * @param $to Target email for switchboard session\r
+     */\r
+    private function reqSBSession($to) {\r
+        $this->log_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->waitingForXFR[] = &$this->switchBoardSessions[$to];\r
+    }\r
+    \r
+    /**\r
+     * Following an XFR or RNG, connect to the switchboard session\r
+     * @param $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case or RNG)\r
+     * @param $ip IP of Switchboard\r
+     * @param $port Port of Switchboard\r
+     * @param $to User on other end of Switchboard\r
+     * @param $param Array of parameters - 'cki', 'ticket', 'sid'\r
+     * @return Whether successful\r
+     */\r
+    private function connectToSBSession($mode, $ip, $port, $to, $param) {\r
+        $this->debug_message("*** SB: try 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
-        $lastActive = $this->switchBoardSessions[$to]['lastActive'];\r
-        $joined = $this->switchBoardSessions[$to]['joined'];\r
+        if(!$socket) {\r
+            $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
+            return false;\r
+        }\r
+        $this->switchBoardSockets[$socket] = $socket;\r
         \r
-        //FIXME Probably not needed (we're not running in a loop anymore)\r
-        /*if($this->kill_me)\r
-        {\r
-            $this->log_message("*** SB Okay, kill me now!");\r
-            endSBSession($socket);\r
-        }*/\r
+        stream_set_timeout($socket, $this->SBStreamTimeout);\r
+        \r
+        $id = &$this->switchBoardSessions[$to]['id'];\r
+        \r
+        if($mode == 'Active') {\r
+            $cki_code = $param['cki'];\r
+            \r
+            // SB: >>> USR {id} {user} {cki}\r
+            $this->sb_writeln($socket, $id, "USR $id $this->user $cki_code");\r
+        } else {\r
+            // Passive\r
+            $ticket = $param['ticket'];\r
+            $sid = $param['sid'];\r
+            \r
+            // 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
+     * Send a message via an existing SB session\r
+     * @param $message Message\r
+     * @param $to Recipient for message\r
+     * @return Whether successful\r
+     */\r
+    private function sendMessageViaSB($message, $to) {\r
+        if(socketcheck($this->switchBoardSessions[$to]['socket'])) {\r
+            $this->reqSBSession($to);\r
+            return false;\r
+        }\r
         \r
-        if(!$Joined) {\r
+        if(!$this->switchBoardSessions[$to]['joined']) {\r
             // If our participant has not joined the session yet we can't message them!\r
-            //TODO Check the behaviour of the queue runner when we return false\r
             return false;\r
         }\r
         \r
+        $id = &$this->switchBoardSessions[$to]['id'];\r
+        $socket = $this->switchBoardSessions[$to]['socket'];\r
+        \r
         $aMessage = $this->getMessage($Message);\r
         //CheckEmotion...\r
         $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
@@ -3615,79 +3524,255 @@ X-OIM-Sequence-Num: 1
         {\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 $id N $len");\r
-            $id++;\r
-            $this->SB_writedata($SendString);\r
-            $this->id++;\r
+            // TODO handle failure during write to socket\r
+            $this->sb_writeln($socket, $id, "MSG $id N $len");\r
+            $this->sb_writedata($socket, $SendString);\r
         }\r
         $len = strlen($aMessage);\r
-        $this->SB_writeln("MSG $id N $len");\r
-        \r
-        // Increment the trID\r
-        $this->switchBoardSessions[$to]['id']++;\r
-        \r
-        $this->SB_writedata($aMessage);\r
+        // TODO handle failure during write to socket\r
+        $this->sb_writeln($socket, $id, "MSG $id N $len");\r
+        $this->sb_writedata($socket, $aMessage);\r
         \r
         // Don't close the SB session, we might as well leave it open\r
         \r
         return true;\r
     }\r
     \r
-    //FIXME Not sure if this is needed?\r
-    private function endSBSession($socket) {\r
-        if (feof($this->SBFp))\r
-        {\r
-            // lost connection? error? try OIM later\r
-            @fclose($this->SBFp);\r
+    /**\r
+     * \r
+     * @param $to\r
+     * @param $sMessage\r
+     * @param $lockkey\r
+     */\r
+    private function sendOIM($to, $sMessage, $lockkey) {\r
+        $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+               xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
+<soap:Header>\r
+  <From memberName="'.$this->user.'"\r
+        friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="\r
+        xml:lang="zh-TW"\r
+        proxy="MSNMSGR"\r
+        xmlns="http://messenger.msn.com/ws/2004/09/oim/"\r
+        msnpVer="'.$this->protocol.'"\r
+        buildVer="'.$this->buildver.'"/>\r
+  <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
+  <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"\r
+          appid="'.$this->prod_id.'"\r
+          lockkey="'.$lockkey.'"\r
+          xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
+  <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">\r
+    <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>\r
+    <MessageNumber>1</MessageNumber>\r
+  </Sequence>\r
+</soap:Header>\r
+<soap:Body>\r
+  <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>\r
+  <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: base64\r
+X-OIM-Message-Type: OfflineMessage\r
+X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}\r
+X-OIM-Sequence-Num: 1\r
+\r
+'.chunk_split(base64_encode($sMessage)).'\r
+  </Content>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+        $header_array = array(\r
+            'SOAPAction: '.$this->oim_send_soap,\r
+            'Content-Type: text/xml',\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+        );\r
+\r
+        $this->debug_message("*** URL: $this->oim_send_url");\r
+        $this->debug_message("*** Sending SOAP:\n$XML");\r
+        $curl = curl_init();\r
+        curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);\r
+        curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+        if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+        curl_setopt($curl, CURLOPT_POST, 1);\r
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+        $data = curl_exec($curl);\r
+        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+        curl_close($curl);\r
+        $this->debug_message("*** Get Result:\n$data");\r
+\r
+        if ($http_code == 200) {\r
+            $this->debug_message("*** OIM sent for $to");\r
+            return true;\r
+        }\r
+\r
+        $challenge = false;\r
+        $auth_policy = false;\r
+        // the lockkey is invalid, authenticated fail, we need challenge it again\r
+        // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>\r
+        preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);\r
+        if (count($matches) != 0) {\r
+            // yes, we get new LockKeyChallenge\r
+            $challenge = $matches[2];\r
+            $this->debug_message("*** OIM need new challenge ($challenge) for $to");\r
+        }\r
+        // auth policy error\r
+        // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>\r
+        preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);\r
+        if (count($matches) != 0) {\r
+            $auth_policy = $matches[2];\r
+            $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");\r
+        }\r
+        if ($auth_policy === false && $challenge === false) {\r
+            //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>\r
+            preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);\r
+            if (count($matches) == 0) {\r
+                // no error, we assume the OIM is sent\r
+                $this->debug_message("*** OIM sent for $to");\r
+                return true;\r
+            }\r
+            $err_code = $matches[2];\r
+            //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>\r
+            preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);\r
+            if (count($matches) > 0)\r
+            $err_msg = $matches[1];\r
+            else\r
+            $err_msg = '';\r
+            $this->debug_message("*** OIM failed for $to");\r
+            $this->debug_message("*** OIM Error code: $err_code");\r
+            $this->debug_message("*** OIM Error Message: $err_msg");\r
             return false;\r
         }\r
-        $this->SB_writeln("OUT");\r
-        @fclose($this->SBFp);\r
-        return true;\r
+        return array('challenge' => $challenge, 'auth_policy' => $auth_policy);\r
     }\r
     \r
-    private function getSBSession($to) {\r
-        \r
+    /**\r
+     * Send a message to a user on another network\r
+     * @param $message Message\r
+     * @param $to Intended recipient\r
+     * @param $network Network\r
+     */\r
+    private function sendOtherNetworkMessage($message, $to, $network) {\r
+        $message=$this->getMessage($nessage, $network);\r
+        $len = strlen($message);\r
+        $this->ns_writeln("UUM $this->id $to $network 1 $len");\r
+        $this->ns_writedata($Message);\r
+        $this->log_message("*** sent to $to (network: $network):\n$Message");\r
     }\r
     \r
+    /**\r
+     * Send a message\r
+     * @param $message Message\r
+     * @param $to To address in form user@host.com@network\r
+     *            where network is 1 for MSN, 32 for Yahoo\r
+     *            and 'Offline' for offline messages\r
+     */\r
     public function sendMessage($message, $to) {\r
         if($message != '') {\r
             list($name,$host,$network)=explode('@',$to);\r
             $network=$network==''?1:$network;\r
             \r
-            if($network === 1 && isset($this->switchBoardSessions[$to])) {\r
+            if($network === 1 && $this->switchBoardSessions[$to]['socket'] != NULL && time()-$this->switchBoardSessions[$to]['lastActive'] < $this->SBIdleTimeout) {\r
                 $recipient = $name . $host;\r
                 $this->debug_message("*** Sending Message to $recipient using existing SB session");\r
                 return $this->sendMessageViaSB($message, $recipient);\r
+            } elseif($network == 'Offline') {\r
+                //Send OIM\r
+                //FIXME: 修正Send OIM\r
+                $lockkey='';\r
+                for ($i = 0; $i < $this->oim_try; $i++)\r
+                {\r
+                    if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;\r
+                    if (is_array($oim_result) && $oim_result['challenge'] !== false) {\r
+                        // need challenge lockkey\r
+                        $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);\r
+                        $lockkey = $this->getChallenge($oim_result['challenge']);\r
+                        continue;\r
+                    }\r
+                    if ($oim_result === false || $oim_result['auth_policy'] !== false)\r
+                    {\r
+                        if ($this->re_login)\r
+                        {\r
+                            $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");\r
+                            break;\r
+                        }\r
+                        $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");\r
+                        // maybe we need to re-login again\r
+                        if(!$this->get_passport_ticket())\r
+                        {\r
+                            $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+                            break;\r
+                        }\r
+                        $this->log_message("**** get new ticket, try it again");\r
+                        continue;\r
+                    }\r
+                }\r
             } else {\r
                 $this->debug_message("*** Not MSN network or no existing SB session");\r
-                //TODO implement creation of SB session etc\r
+                $this->reqSBSession($to);\r
+                return false;\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
+        {\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
      * Should be called about every 50 seconds\r
      */\r
-    public function send_ping() {\r
+    public function sendPing() {\r
         // NS: >>> PNG\r
         $this->ns_writeln("PNG");\r
     }\r
     \r
+    /**\r
+     * Get the NS socket\r
+     */\r
     public function getNSSocket() {\r
         return $this->NSfp;\r
     }\r
     \r
-    // TODO Allow for multiple SB session sockets\r
-    public function getSBSocket() {\r
-        return $this->SBfp;\r
+    /**\r
+     * Get the Switchboard sockets currently in use\r
+     */\r
+    public function getSBSockets() {\r
+        return $this->switchBoardSockets;\r
     }\r
     \r
+    /**\r
+     * Get all the sockets currently in use\r
+     */\r
     public function getSockets() {\r
-        return array($this->NSfp, $this->SBfp);\r
+        return array_merge($this->NSfp, $this->switchBoardSockets);\r
+    }\r
+    \r
+    /** \r
+     * Checks socket for end of file\r
+     *\r
+     * @access public\r
+     * @param Resource $socket Socket to check\r
+     * @return boolean true if end of file (socket) \r
+     */\r
+    private static function socketcheck($socket){\r
+        $info = stream_get_meta_data($socket);\r
+        return $info['eof'];\r
     }\r
     \r
     /**\r
index 354ed0f3ef45cc597533fff17a1048d4d1434199..b0540c46e5284f59d0ba3a62943fad4694cbebde 100644 (file)
@@ -115,7 +115,7 @@ class MsnManager extends ImManager
         
         $now = time();
         
-        $this->conn->send_ping();
+        $this->conn->sendPing();
         $this->lastping = $now;
         return true;
     }