]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/Msn/extlib/phpmsnclass/msn.class.php
Fix mising require_once (now required here because of rearranging)
[quix0rs-gnu-social.git] / plugins / Msn / extlib / phpmsnclass / msn.class.php
index 1e8d7e0f1f1178e6fea234cc88eec2605f634970..996c5571c20bd2c0a29458b7b1a00ef654f2e97b 100644 (file)
@@ -2,6 +2,7 @@
 /*\r
 \r
 phpmsnclass ver 2.0s\r
+Luke Fitzgerald <lw.fitzgerald@googlemail.com>\r
 \r
 Based on MSN class ver 2.0 by Tommy Wu, Ricky Su\r
 License: GPL\r
@@ -11,7 +12,7 @@ Documentation on the MSN protocol can be found at: http://msnpiki.msnfanatic.com
 This class uses MSNP15.\r
 \r
 In addition to PHP5, the additional php modules required are:\r
-curl pcre mhash mcrypt bcmath\r
+curl pcre mcrypt bcmath\r
 \r
 */\r
 \r
@@ -22,10 +23,10 @@ class MSN {
     const PROD_KEY = 'PK}_A_0N_K%O?A9S';\r
     const PROD_ID = 'PROD0114ES4Z%Q5W';\r
     const LOGIN_METHOD = 'SSO';\r
-    \r
+\r
     const OIM_SEND_URL = 'https://ows.messenger.msn.com/OimWS/oim.asmx';\r
     const OIM_SEND_SOAP = 'http://messenger.live.com/ws/2006/09/oim/Store2';\r
-    \r
+\r
     const OIM_MAILDATA_URL = 'https://rsi.hotmail.com/rsi/rsi.asmx';\r
     const OIM_MAILDATA_SOAP = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';\r
     const OIM_READ_URL = 'https://rsi.hotmail.com/rsi/rsi.asmx';\r
@@ -41,10 +42,16 @@ class MSN {
 \r
     const DELMEMBER_URL = 'https://contacts.msn.com/abservice/SharingService.asmx';\r
     const DELMEMBER_SOAP = 'http://www.msn.com/webservices/AddressBook/DeleteMember';\r
-    \r
+\r
+    // the message length (include header) is limited (maybe since WLM 8.5 released)\r
+    // for WLM: 1664 bytes\r
+    // for YIM: 518 bytes\r
+    const MAX_MSN_MESSAGE_LEN = 1664;\r
+    const MAX_YAHOO_MESSAGE_LEN = 518;\r
+\r
     private $debug;\r
     private $timeout;\r
-    \r
+\r
     private $id;\r
     private $ticket;\r
     private $user = '';\r
@@ -69,7 +76,7 @@ class MSN {
     private $port = 1863;\r
 \r
     private $clientid = '';\r
-    \r
+\r
     private $error = '';\r
 \r
     private $authed = false;\r
@@ -80,13 +87,6 @@ class MSN {
     private $font_co = '333333';\r
     private $font_ef = '';\r
 \r
-\r
-    // the message length (include header) is limited (maybe since WLM 8.5 released)\r
-    // for WLM: 1664 bytes\r
-    // for YIM: 518 bytes\r
-    const max_msn_message_len = 1664;\r
-    const max_yahoo_message_len = 518;\r
-\r
     // Begin added for StatusNet\r
 \r
     private $aContactList = array();\r
@@ -156,7 +156,6 @@ class MSN {
         // Check support\r
         if (!function_exists('curl_init')) throw new Exception("curl module not found!\n");\r
         if (!function_exists('preg_match')) throw new Exception("pcre module not found!\n");\r
-        if (!function_exists('mhash')) throw new Exception("mhash module not found!\n");\r
         if (!function_exists('mcrypt_cbc')) throw new Exception("mcrypt module not found!\n");\r
         if (!function_exists('bcmod')) throw new Exception("bcmath module not found!\n");\r
 \r
@@ -181,7 +180,7 @@ class MSN {
     /**\r
      * Signon methods\r
      */\r
-    \r
+\r
     /**\r
      * Connect to the NS server\r
      *\r
@@ -212,10 +211,10 @@ class MSN {
         // NS: >> VER {id} MSNP9 CVR0\r
         // MSNP15\r
         // NS: >>> VER {id} MSNP15 CVR0\r
-        $this->ns_writeln("VER $this->id ".PROTOCOL.' CVR0');\r
+        $this->ns_writeln("VER $this->id ".self::PROTOCOL.' CVR0');\r
 \r
         $start_tm = time();\r
-        while (!socketcheck($this->NSfp)) {\r
+        while (!self::socketcheck($this->NSfp)) {\r
             $data = $this->ns_readln();\r
             // no data?\r
             if ($data === false) {\r
@@ -238,7 +237,7 @@ class MSN {
                     // MSNP15\r
                     // NS: <<< VER {id} MSNP15 CVR0\r
                     // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}\r
-                    $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS ".BUILDVER." msmsgs $user");\r
+                    $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS ".self::BUILDVER." msmsgs $user");\r
                     break;\r
 \r
                 case 'CVR':\r
@@ -248,7 +247,7 @@ class MSN {
                     // MSNP15\r
                     // NS: <<< CVR {id} {ver_list} {download_serve} ....\r
                     // NS: >>> USR {id} SSO I {user}\r
-                    $this->ns_writeln("USR $this->id ".LOGIN_METHOD." I $user");\r
+                    $this->ns_writeln("USR $this->id ".self::LOGIN_METHOD." I $user");\r
                     break;\r
 \r
                 case 'USR':\r
@@ -262,7 +261,7 @@ class MSN {
                     $this->user = $user;\r
                     $this->password = $password;\r
                     // NS: <<< USR {id} SSO S {policy} {nonce}\r
-                    @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);\r
+                    @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce) = @explode(' ', $data);\r
 \r
                     $this->passport_policy = $policy;\r
                     $aTickets = $this->get_passport_ticket();\r
@@ -271,7 +270,7 @@ class MSN {
                         // NS: >>> OUT\r
                         $this->ns_writeln("OUT");\r
                         @fclose($this->NSfp);\r
-                        $this->error = 'Passport authenticated fail!';\r
+                        $this->error = 'Passport authentication failed!';\r
                         return false;\r
                     }\r
 \r
@@ -281,7 +280,7 @@ class MSN {
                     $login_code = $this->generateLoginBLOB($secret, $nonce);\r
 \r
                     // NS: >>> USR {id} SSO S {ticket} {login_code}\r
-                    $this->ns_writeln("USR $this->id ".LOGIN_METHOD." S $ticket $login_code");\r
+                    $this->ns_writeln("USR $this->id ".self::LOGIN_METHOD." S $ticket $login_code");\r
                     $this->authed = true;\r
                     break;\r
 \r
@@ -291,7 +290,7 @@ class MSN {
                     // NS: <<< XFR {id} NS {server} 0 {server}\r
                     // MSNP15\r
                     // NS: <<< XFR {id} NS {server} U D\r
-                    @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);\r
+                    @list(/* XFR */, /* id */, $Type, $server) = @explode(' ', $data);\r
                     if ($Type!='NS') break;\r
                     @list($ip, $port) = @explode(':', $server);\r
                     // this connection will close after XFR\r
@@ -307,13 +306,13 @@ class MSN {
                     // NS: >> VER {id} MSNP9 CVR0\r
                     // MSNP15\r
                     // NS: >>> VER {id} MSNP15 CVR0\r
-                    $this->ns_writeln("VER $this->id ".PROTOCOL.' CVR0');\r
+                    $this->ns_writeln("VER $this->id ".self::PROTOCOL.' CVR0');\r
                     break;\r
 \r
                 case 'GCF':\r
                     // return some policy data after 'USR {id} SSO I {user}' command\r
                     // NS: <<< GCF 0 {size}\r
-                    @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);\r
+                    @list(/* GCF */, /* 0 */, $size) = @explode(' ', $data);\r
                     // we don't need the data, just read it and drop\r
                     if (is_numeric($size) && $size > 0)\r
                         $this->ns_readdata($size);\r
@@ -346,6 +345,10 @@ class MSN {
         *        the queue handler! */\r
         $this->debug_message('*** Trying to connect to MSN network');\r
 \r
+        // Remove any remaining switchboard sessions\r
+        $this->switchBoardSessions = array();\r
+        $this->switchBoardSessionLookup = array();\r
+\r
         while (true) {\r
             // Connect\r
             if (!$this->connect($this->user, $this->password)) {\r
@@ -354,7 +357,10 @@ class MSN {
             }\r
 \r
             // Update contacts\r
-            if ($this->UpdateContacts() === false) continue;\r
+            if ($this->UpdateContacts() === false) {\r
+                $this->signonFailure('');\r
+                continue;\r
+            }\r
 \r
             // Get membership lists\r
             if (($this->aContactList = $this->getMembershipList()) === false) {\r
@@ -460,7 +466,7 @@ class MSN {
             $len = strlen($str);\r
             $this->ns_writeln("UUX $this->id $len");\r
             $this->ns_writedata($str);\r
-            if (!socketcheck($this->NSfp)) {\r
+            if (!self::socketcheck($this->NSfp)) {\r
                 $this->debug_message('*** Connected, waiting for commands');\r
                 break;\r
             } else {\r
@@ -476,8 +482,10 @@ class MSN {
     * @return void\r
     */\r
     private function signonFailure($message) {\r
-        $this->debug_message($message);\r
-        $this->callHandler('ConnectFailed');\r
+        if(!empty($message)) {\r
+            $this->debug_message($message);\r
+        }\r
+        $this->callHandler('ConnectFailed', $message);\r
         $this->NSRetryWait($this->retry_wait);\r
     }\r
 \r
@@ -500,7 +508,7 @@ class MSN {
     /**\r
      * NS and SB command handling methods\r
      */\r
-    \r
+\r
     /**\r
      * Read and handle incoming command from NS\r
      *\r
@@ -508,7 +516,7 @@ class MSN {
      */\r
     private function nsReceive() {\r
         // Sign in again if not signed in or socket failed\r
-        if (!is_resource($this->NSfp) || socketcheck($this->NSfp)) {\r
+        if (!is_resource($this->NSfp) || self::socketcheck($this->NSfp)) {\r
             $this->callHandler('Reconnect');\r
             $this->NSRetryWait($this->retry_wait);\r
             $this->signon();\r
@@ -522,395 +530,395 @@ class MSN {
             $this->NSRetryWait($this->retry_wait);\r
             $this->signon();\r
             return;\r
-        } else {\r
-            switch (substr($data, 0, 3)) {\r
-                case 'SBS':\r
-                    // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
-                    // NS: <<< SBS 0 null\r
-                    break;\r
+        }\r
 \r
-                case 'RFS':\r
-                    // FIXME:\r
-                    // NS: <<< RFS ???\r
-                    // refresh ADL, so we re-send it again\r
-                    if (is_array($this->aADL)) {\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
-                    }\r
-                    break;\r
+        switch (substr($data, 0, 3)) {\r
+            case 'SBS':\r
+                // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
+                // NS: <<< SBS 0 null\r
+                break;\r
 \r
-                case 'LST':\r
-                    // NS: <<< LST {email} {alias} 11 0\r
-                    @list(/* LST */, $email, /* alias */,) = @explode(' ', $data);\r
-                    @list($u_name, $u_domain) = @explode('@', $email);\r
-                    if (!isset($this->aContactList[$u_domain][$u_name][1])) {\r
-                        $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
-                        $this->debug_message("*** Added to contact list: $u_name@$u_domain");\r
+            case 'RFS':\r
+                // FIXME:\r
+                // NS: <<< RFS ???\r
+                // refresh ADL, so we re-send it again\r
+                if (is_array($this->aADL)) {\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
-                    break;\r
+                }\r
+                break;\r
+\r
+            case 'LST':\r
+                // NS: <<< LST {email} {alias} 11 0\r
+                @list(/* LST */, $email) = @explode(' ', $data);\r
+                @list($u_name, $u_domain) = @explode('@', $email);\r
+                if (!isset($this->aContactList[$u_domain][$u_name][1])) {\r
+                    $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
+                    $this->debug_message("*** Added to contact list: $u_name@$u_domain");\r
+                }\r
+                break;\r
 \r
-                case 'ADL':\r
-                    // randomly, we get ADL command, someone add us to their contact list for MSNP15\r
-                    // NS: <<< ADL 0 {size}\r
-                    @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0) {\r
-                        $data = $this->ns_readdata($size);\r
-                        preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
-                        if (is_array($matches) && count($matches) > 0) {\r
-                            $u_domain = $matches[1];\r
-                            $u_name = $matches[2];\r
-                            $network = $matches[4];\r
-                            if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
-                                $this->debug_message("*** Someone (network: $network) added us to their list (but already in our list): $u_name@$u_domain");\r
-                            else {\r
-                                $re_login = false;\r
-                                $cnt = 0;\r
-                                foreach (array('Allow', 'Reverse') as $list) {\r
+            case 'ADL':\r
+                // randomly, we get ADL command, someone add us to their contact list for MSNP15\r
+                // NS: <<< ADL 0 {size}\r
+                @list(/* ADL */, /* 0 */, $size) = @explode(' ', $data);\r
+                if (is_numeric($size) && $size > 0) {\r
+                    $data = $this->ns_readdata($size);\r
+                    preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
+                    if (is_array($matches) && count($matches) > 0) {\r
+                        $u_domain = $matches[1];\r
+                        $u_name = $matches[2];\r
+                        $network = $matches[4];\r
+                        if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
+                            $this->debug_message("*** Someone (network: $network) added us to their list (but already in our list): $u_name@$u_domain");\r
+                        else {\r
+                            $re_login = false;\r
+                            $cnt = 0;\r
+                            foreach (array('Allow', 'Reverse') as $list) {\r
+                                if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
+                                    if ($re_login) {\r
+                                        $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
+                                        continue;\r
+                                    }\r
+                                    $aTickets = $this->get_passport_ticket();\r
+                                    if (!$aTickets || !is_array($aTickets)) {\r
+                                        // failed to login? ignore it\r
+                                        $this->debug_message("*** Could not re-login, something wrong here");\r
+                                        $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
+                                        continue;\r
+                                    }\r
+                                    $re_login = true;\r
+                                    $this->ticket = $aTickets;\r
+                                    $this->debug_message("**** Got new ticket, trying again");\r
                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
-                                        if ($re_login) {\r
-                                            $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
-                                            continue;\r
-                                        }\r
-                                        $aTickets = $this->get_passport_ticket();\r
-                                        if (!$aTickets || !is_array($aTickets)) {\r
-                                            // failed to login? ignore it\r
-                                            $this->debug_message("*** Could not re-login, something wrong here");\r
-                                            $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
-                                            continue;\r
-                                        }\r
-                                        $re_login = true;\r
-                                        $this->ticket = $aTickets;\r
-                                        $this->debug_message("**** Got new ticket, trying again");\r
-                                        if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
-                                            $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
-                                            continue;\r
-                                        }\r
+                                        $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");\r
+                                        continue;\r
                                     }\r
-                                    $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
-                                    $cnt++;\r
                                 }\r
-                                $this->debug_message("*** Someone (network: $network) added us to their list: $u_name@$u_domain");\r
+                                $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
+                                $cnt++;\r
                             }\r
-                            $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
-                            $len = strlen($str);\r
-\r
-                            $this->callHandler('AddedToList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));\r
+                            $this->debug_message("*** Someone (network: $network) added us to their list: $u_name@$u_domain");\r
                         }\r
-                        else\r
-                            $this->debug_message("*** Someone added us to their list: $data");\r
-                    }\r
-                    break;\r
+                        $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
+                        $len = strlen($str);\r
 \r
-                case 'RML':\r
-                    // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
-                    // NS: <<< RML 0 {size}\r
-                    @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0) {\r
-                        $data = $this->ns_readdata($size);\r
-                        preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
-                        if (is_array($matches) && count($matches) > 0) {\r
-                            $u_domain = $matches[1];\r
-                            $u_name = $matches[2];\r
-                            $network = $matches[4];\r
-                            if (isset($this->aContactList[$u_domain][$u_name][$network])) {\r
-                                $aData = $this->aContactList[$u_domain][$u_name][$network];\r
-\r
-                                foreach ($aData as $list => $id)\r
-                                    $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
-\r
-                                unset($this->aContactList[$u_domain][$u_name][$network]);\r
-                                $this->debug_message("*** Someone (network: $network) removed us from their list: $u_name@$u_domain");\r
-                            }\r
-                            else\r
-                                $this->debug_message("*** Someone (network: $network) removed us from their list (but not in our list): $u_name@$u_domain");\r
+                        $this->callHandler('AddedToList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));\r
+                    }\r
+                    else\r
+                        $this->debug_message("*** Someone added us to their list: $data");\r
+                }\r
+                break;\r
 \r
-                            $this->callHandler('RemovedFromList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));\r
+            case 'RML':\r
+                // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
+                // NS: <<< RML 0 {size}\r
+                @list(/* RML */, /* 0 */, $size) = @explode(' ', $data);\r
+                if (is_numeric($size) && $size > 0) {\r
+                    $data = $this->ns_readdata($size);\r
+                    preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
+                    if (is_array($matches) && count($matches) > 0) {\r
+                        $u_domain = $matches[1];\r
+                        $u_name = $matches[2];\r
+                        $network = $matches[4];\r
+                        if (isset($this->aContactList[$u_domain][$u_name][$network])) {\r
+                            $aData = $this->aContactList[$u_domain][$u_name][$network];\r
+\r
+                            foreach ($aData as $list => $id)\r
+                                $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
+\r
+                            unset($this->aContactList[$u_domain][$u_name][$network]);\r
+                            $this->debug_message("*** Someone (network: $network) removed us from their list: $u_name@$u_domain");\r
                         }\r
                         else\r
-                            $this->debug_message("*** Someone removed us from their list: $data");\r
+                            $this->debug_message("*** Someone (network: $network) removed us from their list (but not in our list): $u_name@$u_domain");\r
+\r
+                        $this->callHandler('RemovedFromList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));\r
                     }\r
-                    break;\r
+                    else\r
+                        $this->debug_message("*** Someone removed us from their list: $data");\r
+                }\r
+                break;\r
 \r
-                case 'MSG':\r
-                    // randomly, we get MSG notification from server\r
-                    // NS: <<< MSG Hotmail Hotmail {size}\r
-                    @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0) {\r
-                        $data = $this->ns_readdata($size);\r
-                        $aLines = @explode("\n", $data);\r
-                        $header = true;\r
-                        $ignore = false;\r
-                        $maildata = '';\r
-                        foreach ($aLines as $line) {\r
-                            $line = rtrim($line);\r
-                            if ($header) {\r
-                                if ($line === '') {\r
-                                    $header = false;\r
-                                    continue;\r
-                                }\r
-                                if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
-                                    if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false && strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
-                                        // we just need text/x-msmsgsinitialmdatanotification\r
-                                        // or text/x-msmsgsoimnotification\r
-                                        $ignore = true;\r
-                                        break;\r
-                                    }\r
-                                }\r
+            case 'MSG':\r
+                // randomly, we get MSG notification from server\r
+                // NS: <<< MSG Hotmail Hotmail {size}\r
+                @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size) = @explode(' ', $data);\r
+                if (is_numeric($size) && $size > 0) {\r
+                    $data = $this->ns_readdata($size);\r
+                    $aLines = @explode("\n", $data);\r
+                    $header = true;\r
+                    $ignore = false;\r
+                    $maildata = '';\r
+                    foreach ($aLines as $line) {\r
+                        $line = rtrim($line);\r
+                        if ($header) {\r
+                            if ($line === '') {\r
+                                $header = false;\r
                                 continue;\r
                             }\r
-                            if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
-                                $maildata = trim(substr($line, 10));\r
-                                break;\r
+                            if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
+                                if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false && strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
+                                    // we just need text/x-msmsgsinitialmdatanotification\r
+                                    // or text/x-msmsgsoimnotification\r
+                                    $ignore = true;\r
+                                    break;\r
+                                }\r
                             }\r
+                            continue;\r
                         }\r
-                        if ($ignore) {\r
-                            $this->debug_message("*** Ignoring MSG for: $line");\r
-                            break;\r
-                        }\r
-                        if ($maildata == '') {\r
-                            $this->debug_message("*** Ignoring MSG not for OIM");\r
+                        if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
+                            $maildata = trim(substr($line, 10));\r
                             break;\r
                         }\r
-                        $re_login = false;\r
-                        if (strcasecmp($maildata, 'too-large') == 0) {\r
-                            $this->debug_message("*** Large mail-data, need to get the data via SOAP");\r
+                    }\r
+                    if ($ignore) {\r
+                        $this->debug_message("*** Ignoring MSG for: $line");\r
+                        break;\r
+                    }\r
+                    if ($maildata == '') {\r
+                        $this->debug_message("*** Ignoring MSG not for OIM");\r
+                        break;\r
+                    }\r
+                    $re_login = false;\r
+                    if (strcasecmp($maildata, 'too-large') == 0) {\r
+                        $this->debug_message("*** Large mail-data, need to get the data via SOAP");\r
+                        $maildata = $this->getOIM_maildata();\r
+                        if ($maildata === false) {\r
+                            $this->debug_message("*** Could not get mail-data via SOAP");\r
+\r
+                            // maybe we need to re-login again\r
+                            $aTickets = $this->get_passport_ticket();\r
+                            if (!$aTickets || !is_array($aTickets)) {\r
+                                // failed to login? ignore it\r
+                                $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");\r
+                                break;\r
+                            }\r
+                            $re_login = true;\r
+                            $this->ticket = $aTickets;\r
+                            $this->debug_message("*** Got new ticket, trying again");\r
                             $maildata = $this->getOIM_maildata();\r
                             if ($maildata === false) {\r
-                                $this->debug_message("*** Could not get mail-data via SOAP");\r
-\r
-                                // maybe we need to re-login again\r
-                                $aTickets = $this->get_passport_ticket();\r
-                                if (!$aTickets || !is_array($aTickets)) {\r
-                                    // failed to login? ignore it\r
-                                    $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");\r
-                                    break;\r
-                                }\r
-                                $re_login = true;\r
-                                $this->ticket = $aTickets;\r
-                                $this->debug_message("*** Got new ticket, trying again");\r
-                                $maildata = $this->getOIM_maildata();\r
-                                if ($maildata === false) {\r
-                                    $this->debug_message("*** Could not get mail-data via SOAP, and re-login already attempted, ignoring this OIM");\r
-                                    break;\r
-                                }\r
+                                $this->debug_message("*** Could not get mail-data via SOAP, and re-login already attempted, ignoring this OIM");\r
+                                break;\r
                             }\r
                         }\r
-                        // could be a lots of <M>...</M>, so we can't use preg_match here\r
-                        $p = $maildata;\r
-                        $aOIMs = array();\r
-                        while (1) {\r
-                            $start = strpos($p, '<M>');\r
-                            $end = strpos($p, '</M>');\r
-                            if ($start === false || $end === false || $start > $end) break;\r
-                            $end += 4;\r
-                            $sOIM = substr($p, $start, $end - $start);\r
-                            $aOIMs[] = $sOIM;\r
-                            $p = substr($p, $end);\r
+                    }\r
+                    // could be a lots of <M>...</M>, so we can't use preg_match here\r
+                    $p = $maildata;\r
+                    $aOIMs = array();\r
+                    while (1) {\r
+                        $start = strpos($p, '<M>');\r
+                        $end = strpos($p, '</M>');\r
+                        if ($start === false || $end === false || $start > $end) break;\r
+                        $end += 4;\r
+                        $sOIM = substr($p, $start, $end - $start);\r
+                        $aOIMs[] = $sOIM;\r
+                        $p = substr($p, $end);\r
+                    }\r
+                    if (count($aOIMs) == 0) {\r
+                        $this->debug_message("*** Ignoring empty OIM");\r
+                        break;\r
+                    }\r
+                    foreach ($aOIMs as $maildata) {\r
+                        // T: 11 for MSN, 13 for Yahoo\r
+                        // S: 6 for MSN, 7 for Yahoo\r
+                        // RT: the datetime received by server\r
+                        // RS: already read or not\r
+                        // SZ: size of message\r
+                        // E: sender\r
+                        // I: msgid\r
+                        // F: always 00000000-0000-0000-0000-000000000009\r
+                        // N: sender alias\r
+                        preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
+                        if (count($matches) == 0) {\r
+                            $this->debug_message("*** Ignoring OIM maildata without <T>type</T>");\r
+                            continue;\r
                         }\r
-                        if (count($aOIMs) == 0) {\r
-                            $this->debug_message("*** Ignoring empty OIM");\r
-                            break;\r
+                        $oim_type = $matches[1];\r
+                        if ($oim_type = 13)\r
+                            $network = 32;\r
+                        else\r
+                            $network = 1;\r
+                        preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
+                        if (count($matches) == 0) {\r
+                            $this->debug_message("*** Ignoring OIM maildata without <E>sender</E>");\r
+                            continue;\r
                         }\r
-                        foreach ($aOIMs as $maildata) {\r
-                            // T: 11 for MSN, 13 for Yahoo\r
-                            // S: 6 for MSN, 7 for Yahoo\r
-                            // RT: the datetime received by server\r
-                            // RS: already read or not\r
-                            // SZ: size of message\r
-                            // E: sender\r
-                            // I: msgid\r
-                            // F: always 00000000-0000-0000-0000-000000000009\r
-                            // N: sender alias\r
-                            preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
-                            if (count($matches) == 0) {\r
-                                $this->debug_message("*** Ignoring OIM maildata without <T>type</T>");\r
-                                continue;\r
-                            }\r
-                            $oim_type = $matches[1];\r
-                            if ($oim_type = 13)\r
-                                $network = 32;\r
-                            else\r
-                                $network = 1;\r
-                            preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
-                            if (count($matches) == 0) {\r
-                                $this->debug_message("*** Ignoring OIM maildata without <E>sender</E>");\r
+                        $oim_sender = $matches[1];\r
+                        preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
+                        if (count($matches) == 0) {\r
+                            $this->debug_message("*** Ignoring OIM maildata without <I>msgid</I>");\r
+                            continue;\r
+                        }\r
+                        $oim_msgid = $matches[1];\r
+                        preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
+                        $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
+                        preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
+                        $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
+                        $this->debug_message("*** OIM received from $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
+                        $sMsg = $this->getOIM_message($oim_msgid);\r
+                        if ($sMsg === false) {\r
+                            $this->debug_message("*** Could not get OIM, msgid = $oim_msgid");\r
+                            if ($re_login) {\r
+                                $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");\r
                                 continue;\r
                             }\r
-                            $oim_sender = $matches[1];\r
-                            preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
-                            if (count($matches) == 0) {\r
-                                $this->debug_message("*** Ignoring OIM maildata without <I>msgid</I>");\r
+                            $aTickets = $this->get_passport_ticket();\r
+                            if (!$aTickets || !is_array($aTickets)) {\r
+                                // failed to login? ignore it\r
+                                $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");\r
                                 continue;\r
                             }\r
-                            $oim_msgid = $matches[1];\r
-                            preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
-                            $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
-                            preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
-                            $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
-                            $this->debug_message("*** OIM received from $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
+                            $re_login = true;\r
+                            $this->ticket = $aTickets;\r
+                            $this->debug_message("*** get new ticket, try it again");\r
                             $sMsg = $this->getOIM_message($oim_msgid);\r
                             if ($sMsg === false) {\r
-                                $this->debug_message("*** Could not get OIM, msgid = $oim_msgid");\r
-                                if ($re_login) {\r
-                                    $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");\r
-                                    continue;\r
-                                }\r
-                                $aTickets = $this->get_passport_ticket();\r
-                                if (!$aTickets || !is_array($aTickets)) {\r
-                                    // failed to login? ignore it\r
-                                    $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");\r
-                                    continue;\r
-                                }\r
-                                $re_login = true;\r
-                                $this->ticket = $aTickets;\r
-                                $this->debug_message("*** get new ticket, try it again");\r
-                                $sMsg = $this->getOIM_message($oim_msgid);\r
-                                if ($sMsg === false) {\r
-                                    $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");\r
-                                    continue;\r
-                                }\r
+                                $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");\r
+                                continue;\r
                             }\r
-                            $this->debug_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
-                            $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));\r
                         }\r
+                        $this->debug_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
+                        $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));\r
                     }\r
-                    break;\r
+                }\r
+                break;\r
 \r
-                case 'UBM':\r
-                    // randomly, we get UBM, this is the message from other network, like Yahoo!\r
-                    // NS: <<< UBM {email} $network $type {size}\r
-                    @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);\r
-                    if (is_numeric($size) && $size > 0) {\r
-                        $data = $this->ns_readdata($size);\r
-                        $aLines = @explode("\n", $data);\r
-                        $header = true;\r
-                        $ignore = false;\r
-                        $sMsg = '';\r
-                        foreach ($aLines as $line) {\r
-                            $line = rtrim($line);\r
-                            if ($header) {\r
-                                if ($line === '') {\r
-                                    $header = false;\r
-                                    continue;\r
-                                }\r
-                                if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
-                                    $ignore = true;\r
-                                    break;\r
-                                }\r
+            case 'UBM':\r
+                // randomly, we get UBM, this is the message from other network, like Yahoo!\r
+                // NS: <<< UBM {email} $network $type {size}\r
+                @list(/* UBM */, $from_email, $network, $type, $size) = @explode(' ', $data);\r
+                if (is_numeric($size) && $size > 0) {\r
+                    $data = $this->ns_readdata($size);\r
+                    $aLines = @explode("\n", $data);\r
+                    $header = true;\r
+                    $ignore = false;\r
+                    $sMsg = '';\r
+                    foreach ($aLines as $line) {\r
+                        $line = rtrim($line);\r
+                        if ($header) {\r
+                            if ($line === '') {\r
+                                $header = false;\r
                                 continue;\r
                             }\r
-                            $aSubLines = @explode("\r", $line);\r
-                            foreach ($aSubLines as $str) {\r
-                                if ($sMsg !== '')\r
-                                $sMsg .= "\n";\r
-                                $sMsg .= $str;\r
+                            if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
+                                $ignore = true;\r
+                                break;\r
                             }\r
+                            continue;\r
                         }\r
-                        if ($ignore) {\r
-                            $this->debug_message("*** Ignoring message from $from_email: $line");\r
-                            break;\r
+                        $aSubLines = @explode("\r", $line);\r
+                        foreach ($aSubLines as $str) {\r
+                            if ($sMsg !== '')\r
+                            $sMsg .= "\n";\r
+                            $sMsg .= $str;\r
                         }\r
-                        $this->debug_message("*** MSG from $from_email (network: $network): $sMsg");\r
-                        $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));\r
                     }\r
-                    break;\r
+                    if ($ignore) {\r
+                        $this->debug_message("*** Ignoring message from $from_email: $line");\r
+                        break;\r
+                    }\r
+                    $this->debug_message("*** MSG from $from_email (network: $network): $sMsg");\r
+                    $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));\r
+                }\r
+                break;\r
 \r
-                case 'UBX':\r
-                    // randomly, we get UBX notification from server\r
-                    // NS: <<< UBX email {network} {size}\r
-                    @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);\r
-                    // we don't need the notification data, so just ignore it\r
-                    if (is_numeric($size) && $size > 0)\r
-                        $this->ns_readdata($size);\r
-                    break;\r
+            case 'UBX':\r
+                // randomly, we get UBX notification from server\r
+                // NS: <<< UBX email {network} {size}\r
+                @list(/* UBX */, /* email */, /* network */, $size) = @explode(' ', $data);\r
+                // we don't need the notification data, so just ignore it\r
+                if (is_numeric($size) && $size > 0)\r
+                    $this->ns_readdata($size);\r
+                break;\r
 \r
-                case 'CHL':\r
-                    // randomly, we'll get challenge from server\r
-                    // NS: <<< CHL 0 {code}\r
-                    @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);\r
-                    $fingerprint = $this->getChallenge($chl_code);\r
-                    // NS: >>> QRY {id} {product_id} 32\r
-                    // NS: >>> fingerprint\r
-                    $this->ns_writeln("QRY $this->id ".PROD_ID.' 32');\r
-                    $this->ns_writedata($fingerprint);\r
-                    $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
-                    if ($this->PhotoStickerFile !== false)\r
-                        $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
-                    break;\r
-                case 'CHG':\r
-                    // NS: <<< CHG {id} {status} {code}\r
-                    // ignore it\r
-                    // change our status to online first\r
-                    break;\r
+            case 'CHL':\r
+                // randomly, we'll get challenge from server\r
+                // NS: <<< CHL 0 {code}\r
+                @list(/* CHL */, /* 0 */, $chl_code) = @explode(' ', $data);\r
+                $fingerprint = $this->getChallenge($chl_code);\r
+                // NS: >>> QRY {id} {product_id} 32\r
+                // NS: >>> fingerprint\r
+                $this->ns_writeln("QRY $this->id ".self::PROD_ID.' 32');\r
+                $this->ns_writedata($fingerprint);\r
+                $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
+                if ($this->PhotoStickerFile !== false)\r
+                    $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+                break;\r
+            case 'CHG':\r
+                // NS: <<< CHG {id} {status} {code}\r
+                // ignore it\r
+                // change our status to online first\r
+                break;\r
 \r
-                case 'XFR':\r
-                    // sometimes, NS will redirect to another NS\r
-                    // MSNP9\r
-                    // NS: <<< XFR {id} NS {server} 0 {server}\r
-                    // MSNP15\r
-                    // NS: <<< XFR {id} NS {server} U D\r
-                    // for normal switchboard XFR\r
-                    // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
-                    @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);\r
-                    @list($ip, $port) = @explode(':', $server);\r
-                    if ($server_type != 'SB') {\r
-                        // maybe exit?\r
-                        // this connection will close after XFR\r
-                        $this->nsLogout();\r
-                        continue;\r
-                    }\r
+            case 'XFR':\r
+                // sometimes, NS will redirect to another NS\r
+                // MSNP9\r
+                // NS: <<< XFR {id} NS {server} 0 {server}\r
+                // MSNP15\r
+                // NS: <<< XFR {id} NS {server} U D\r
+                // for normal switchboard XFR\r
+                // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
+                @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code) = @explode(' ', $data);\r
+                @list($ip, $port) = @explode(':', $server);\r
+                if ($server_type != 'SB') {\r
+                    // maybe exit?\r
+                    // this connection will close after XFR\r
+                    $this->nsLogout();\r
+                    continue;\r
+                }\r
 \r
-                    $this->debug_message("NS: <<< XFR SB");\r
-                    $user = array_shift($this->waitingForXFR);\r
-                                $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
-                    /*\r
-                     $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
-                     if ($bSBresult === false) {\r
-                     // error for switchboard\r
-                     $this->debug_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
-                     $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
-                     }*/\r
-                    break;\r
-                case 'QNG':\r
-                    // NS: <<< QNG {time}\r
-                    @list(/* QNG */, $ping_wait) = @explode(' ', $data);\r
-                    $this->callHandler('Pong', $ping_wait);\r
-                    break;\r
+                $this->debug_message("NS: <<< XFR SB");\r
+                $session = array_shift($this->waitingForXFR);\r
+                $this->connectToSBSession('Active', $ip, $port, $session['to'], array('cki' => $cki_code));\r
+                break;\r
+            case 'QNG':\r
+                // NS: <<< QNG {time}\r
+                @list(/* QNG */, $ping_wait) = @explode(' ', $data);\r
+                $this->callHandler('Pong', $ping_wait);\r
+                break;\r
 \r
-                case 'RNG':\r
-                    if ($this->PhotoStickerFile !== false)\r
-                        $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
-                    else\r
-                        $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
-                    // someone is trying to talk to us\r
-                    // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
-                    $this->debug_message("NS: <<< RNG $data");\r
-                    @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
-                    @list($sb_ip, $sb_port) = @explode(':', $server);\r
-                    $this->debug_message("*** RING from $email, $sb_ip:$sb_port");\r
-                    $this->addContact($email, 1, $email, true);\r
-                    $this->connectToSBSession('Passive', $sb_ip, $sb_port, $email, array('sid' => $sid, 'ticket' => $ticket));\r
-                    break;\r
-                case 'OUT':\r
-                    // force logout from NS\r
-                    // NS: <<< OUT xxx\r
-                    $this->debug_message("*** LOGOUT from NS");\r
-                    return $this->nsLogout();\r
+            case 'RNG':\r
+                if ($this->PhotoStickerFile !== false)\r
+                    $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+                else\r
+                    $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
+                // someone is trying to talk to us\r
+                // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
+                $this->debug_message("NS: <<< RNG $data");\r
+                @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name) = @explode(' ', $data);\r
+                @list($sb_ip, $sb_port) = @explode(':', $server);\r
+                $this->debug_message("*** RING from $email, $sb_ip:$sb_port");\r
+                $this->addContact($email, 1, $email, true);\r
+                $this->connectToSBSession('Passive', $sb_ip, $sb_port, $email, array('sid' => $sid, 'ticket' => $ticket));\r
+                break;\r
 \r
-                default:\r
-                    $code = substr($data,0,3);\r
-                    if (is_numeric($code)) {\r
-                        $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
-                        $this->debug_message("*** NS: $this->error");\r
+            case 'NLN':\r
+                // NS: <<< NLN {status} {email} {networkid} {nickname} {clientid} {dpobj}\r
+                @list(/* NLN */, $status, $email, $network, $nickname) = @explode(' ', $data);\r
+                $this->callHandler('StatusChange', array('screenname' => $email, 'status' => $status, 'network' => $network, 'nickname' => $nickname));\r
+                break;\r
 \r
-                        return $this->nsLogout();\r
-                    }\r
-                    break;\r
-            }\r
+            case 'OUT':\r
+                // force logout from NS\r
+                // NS: <<< OUT xxx\r
+                $this->debug_message("*** LOGOUT from NS");\r
+                return $this->nsLogout();\r
+\r
+            default:\r
+                $code = substr($data,0,3);\r
+                if (is_numeric($code)) {\r
+                    $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
+                    $this->debug_message("*** NS: $this->error");\r
+\r
+                    return $this->nsLogout();\r
+                }\r
+                break;\r
         }\r
     }\r
 \r
@@ -940,18 +948,21 @@ class MSN {
                 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}\r
                 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);\r
                 $this->debug_message("*** $email joined session");\r
-                $session['joined'] = true;\r
+                if ($email == $session['to']) {\r
+                    $session['joined'] = true;\r
+                    $this->callHandler('SessionReady', array('to' => $email));\r
+                }\r
                 break;\r
             case 'BYE':\r
                 $this->debug_message("*** Quit for BYE");\r
-                $this->endSBSession();\r
+                $this->endSBSession($socket);\r
                 break;\r
             case 'USR':\r
                 // SB: <<< USR {id} OK {user} {alias}\r
                 // we don't need the data, just ignore it\r
                 // request user to join this switchboard\r
                 // SB: >>> CAL {id} {user}\r
-                $this->sb_writeln($socket, $id, "CAL $this->id $user");\r
+                $this->sb_writeln($socket, $id, "CAL $id ".$session['to']);\r
                 break;\r
             case 'CAL':\r
                 // SB: <<< CAL {id} RINGING {?}\r
@@ -961,13 +972,15 @@ class MSN {
             case 'JOI':\r
                 // SB: <<< JOI {user} {alias} {clientid?}\r
                 // someone join us\r
-                // we don't need the data, just ignore it\r
-                // no more user here\r
-                $session['joined'] = true;\r
+                @list(/* JOI */, $email) = @explode(' ', $data);\r
+                if ($email == $session['to']) {\r
+                    $session['joined'] = true;\r
+                    $this->callHandler('SessionReady', array('to' => $email));\r
+                }\r
                 break;\r
             case 'MSG':\r
                 // SB: <<< MSG {email} {alias} {len}\r
-                @list(/* MSG */, $from_email, /* alias */, $len) = @explode(' ', $data);\r
+                @list(/* MSG */, $from_email, /* alias */, $len) = @explode(' ', $data);\r
                 $len = trim($len);\r
                 $data = $this->sb_readdata($socket, $len);\r
                 $aLines = @explode("\n", $data);\r
@@ -1104,7 +1117,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($socket, $id, "MSG $this->id D $len");\r
+                        $this->sb_writeln($socket, $id, "MSG $id D $len");\r
                         $this->sb_writedata($socket, $message);\r
                         $this->debug_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");\r
                         $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));\r
@@ -1142,7 +1155,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($socket, $id, "MSG $this->id D ".strlen($message));\r
+                        $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));\r
                         $this->sb_writedata($socket, $message);\r
                         $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));\r
                         $this->sb_readln($socket); // Read ACK;\r
@@ -1169,7 +1182,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($socket, $id, "MSG $this->id D ".strlen($message));\r
+                        $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));\r
                         $this->sb_writedata($socket, $message);\r
                         $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));\r
                         $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));\r
@@ -1201,7 +1214,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($socket, $id, "MSG $this->id D ".strlen($message));\r
+                                $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));\r
                                 $this->sb_writedata($socket, $message);\r
                                 $this->debug_message("*** p2p: dump send Data Content message  $Offset / $FileSize :\n".$this->dump_binary($message));\r
                                 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));\r
@@ -1304,17 +1317,16 @@ class MSN {
                     break;\r
                 }\r
                 $this->debug_message("*** MSG from $from_email: $sMsg");\r
-                $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));\r
+                $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => 1, 'offline' => false));\r
                 break;\r
             case '217':\r
-                $this->debug_message("*** User $user is offline. Trying OIM.");\r
+                $this->debug_message('*** User '.$session['to'].' is offline. Trying OIM.');\r
                 $session['offline'] = true;\r
                 break;\r
             default:\r
                 if (is_numeric($code)) {\r
                     $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
                     $this->debug_message("*** SB: $this->error");\r
-                    $sessionEnd=true;\r
                 }\r
                 break;\r
         }\r
@@ -1348,7 +1360,7 @@ class MSN {
     /**\r
      * Switchboard related methods\r
      */\r
-    \r
+\r
     /**\r
      * Send a request for a switchboard session\r
      *\r
@@ -1367,7 +1379,7 @@ class MSN {
             'offline' => false,\r
             'XFRReqTime' => time()\r
         );\r
-        $this->waitingForXFR[] = &$this->switchBoardSessions[$to];\r
+        $this->waitingForXFR[$to] = &$this->switchBoardSessions[$to];\r
     }\r
 \r
     /**\r
@@ -1393,7 +1405,6 @@ class MSN {
         $this->switchBoardSessionLookup[$to] = $socket;\r
 \r
         // Store the socket in the sessions array\r
-        $intsocket = (int) $socket;\r
         $this->switchBoardSessions[$to] = array(\r
             'to' => $to,\r
             'socket' => $socket,\r
@@ -1404,6 +1415,7 @@ class MSN {
         );\r
 \r
         // Change the index of the session to the socket\r
+        $intsocket = (int) $socket;\r
         $this->switchBoardSessions[$intsocket] = $this->switchBoardSessions[$to];\r
         unset($this->switchBoardSessions[$to]);\r
 \r
@@ -1423,7 +1435,7 @@ class MSN {
             $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");\r
         }\r
     }\r
-    \r
+\r
     /**\r
     * Called when we want to end a switchboard session\r
     * or a switchboard session ends\r
@@ -1432,8 +1444,8 @@ class MSN {
     * @param boolean $killsession Whether to delete the session\r
     * @return void\r
     */\r
-    private function endSBSession($socket, $killsession = false) {\r
-        if (!socketcheck($socket)) {\r
+    private function endSBSession($socket) {\r
+        if (!self::socketcheck($socket)) {\r
             $this->sb_writeln($socket, $fake = 0, 'OUT');\r
         }\r
         @fclose($socket);\r
@@ -1455,35 +1467,34 @@ class MSN {
      */\r
     private function sendMessageViaSB($to, $message) {\r
         $socket = $this->switchBoardSessionLookup[$to];\r
-        if (socketcheck($socket)) {\r
-            return false;\r
-        }\r
-\r
-        if (!$this->switchBoardSessions[$to]['joined']) {\r
-            // If our participant has not joined the session yet we can't message them!\r
+        if (self::socketcheck($socket)) {\r
             return false;\r
         }\r
 \r
-        $intsocket = (int) $socket;\r
-        $id = &$this->switchBoardSessions[$intsocket]['id'];\r
+        $id = &$this->switchBoardSessions[(int) $socket]['id'];\r
 \r
-        $aMessage = $this->getMessage($Message);\r
-        //CheckEmotion...\r
-        $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
+        $aMessage = $this->getMessage($message);\r
+        // CheckEmotion...\r
+        $MsnObjDefine = $this->GetMsnObjDefine($aMessage);\r
         if ($MsnObjDefine !== '') {\r
-            $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";\r
+            $SendString = "MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";\r
             $len = strlen($SendString);\r
-            // TODO handle failure during write to socket\r
-            $this->sb_writeln($socket, $id, "MSG $id N $len");\r
-            $this->sb_writedata($socket, $SendString);\r
+\r
+            if ($this->sb_writeln($socket, $id, "MSG $id N $len") === false ||\r
+                $this->sb_writedata($socket, $SendString) === false) {\r
+                    $this->endSBSession($socket);\r
+                    return false;\r
+                }\r
         }\r
         $len = strlen($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
+        if ($this->sb_writeln($socket, $id, "MSG $id N $len") === false ||\r
+            $this->sb_writedata($socket, $aMessage) === false) {\r
+                $this->endSBSession($socket);\r
+                return false;\r
+            }\r
 \r
+        // Don't close the SB session, we might as well leave it open\r
         return true;\r
     }\r
 \r
@@ -1498,9 +1509,10 @@ class MSN {
     private function sendOtherNetworkMessage($to, $message, $network) {\r
         $message = $this->getMessage($message, $network);\r
         $len = strlen($message);\r
-        // TODO Introduce error checking for message sending\r
-        $this->ns_writeln("UUM $this->id $to $network 1 $len");\r
-        $this->ns_writedata($Message);\r
+        if ($this->ns_writeln("UUM $this->id $to $network 1 $len") === false ||\r
+            $this->ns_writedata($Message) === false) {\r
+            return false;\r
+        }\r
         $this->debug_message("*** Sent to $to (network: $network):\n$Message");\r
         return true;\r
     }\r
@@ -1512,36 +1524,58 @@ class MSN {
      *                   where network is 1 for MSN, 32 for Yahoo\r
      *                   and 'Offline' for offline messages\r
      * @param string $message Message\r
+     * @param boolean &$waitForSession Boolean passed by reference,\r
+     *                                 if set to true on return, message\r
+     *                                 did not fail to send but is\r
+     *                                 waiting for a valid session\r
+     *\r
+     * @return boolean true on success\r
      */\r
-    public function sendMessage($to, $message) {\r
+    public function sendMessage($to, $message, &$waitForSession) {\r
         if ($message != '') {\r
-            list($name, $host, $network) = explode('@', $to);\r
-            $network = $network == '' ? 1 : $network;\r
-            $recipient = $name.$host;\r
+            $toParts = explode('@', $to);\r
+            if(count($toParts) < 3) {\r
+                list($name, $host) = $toParts;\r
+                $network = 1;\r
+            } else {\r
+                list($name, $host, $network) = $toParts;\r
+            }\r
+\r
+            $recipient = $name.'@'.$host;\r
 \r
             if ($network === 1) {\r
-                if (!isset($this->switchBoardSessionLookup[$recipient]) && (!isset($this->switchBoardSessions[$recipient])\r
-                    || time() - $this->switchBoardSessions[$recipient]['XFRReqTime'] > $this->XFRReqTimeout)) {\r
-                    $this->debug_message("*** No existing SB session or request has timed out");\r
-                    $this->reqSBSession($recipient);\r
+                if (!isset($this->switchBoardSessionLookup[$recipient])) {\r
+                    if (!isset($this->switchBoardSessions[$recipient]) || time() - $this->switchBoardSessions[$recipient]['XFRReqTime'] > $this->XFRReqTimeout) {\r
+                        $this->debug_message("*** No existing SB session or request has timed out");\r
+                        $this->reqSBSession($recipient);\r
+                    }\r
+\r
+                    $waitForSession = true;\r
                     return false;\r
                 } else {\r
-                    $socket = $this->switchBoardSessionLookup[$to];\r
-                    if ($this->switchBoardSessions[(int) $socket]['offline']) {\r
+                    $socket = $this->switchBoardSessionLookup[$recipient];\r
+                    $intsocket = (int) $socket;\r
+                    if ($this->switchBoardSessions[$intsocket]['offline']) {\r
                         $this->debug_message("*** Contact ($recipient) offline, sending OIM");\r
                         $this->endSBSession($socket);\r
+                        $waitForSession = false;\r
                         return $this->sendMessage($recipient.'@Offline', $message);\r
                     } else {\r
+                        if ($this->switchBoardSessions[$intsocket]['joined'] !== true) {\r
+                            $this->debug_message("*** Recipient has not joined session, returning false");\r
+                            $waitForSession = true;\r
+                            return false;\r
+                        }\r
+\r
                         $this->debug_message("*** Attempting to send message to $recipient using existing SB session");\r
 \r
                         if ($this->sendMessageViaSB($recipient, $message)) {\r
                             $this->debug_message('*** Message sent successfully');\r
                             return true;\r
-                        } else {\r
-                            $this->debug_message('*** Message sending failed, requesting new SB session');\r
-                            $this->reqSBSession($to);\r
-                            return false;\r
                         }\r
+\r
+                        $waitForSession = false;\r
+                        return false;\r
                     }\r
                 }\r
             } elseif ($network == 'Offline') {\r
@@ -1573,6 +1607,7 @@ class MSN {
                         continue;\r
                     }\r
                 }\r
+                return true;\r
             } else {\r
                 // Other network\r
                 return $this->sendOtherNetworkMessage($recipient, $message, $network);\r
@@ -1584,7 +1619,7 @@ class MSN {
     /**\r
      * OIM methods\r
      */\r
-    \r
+\r
     /**\r
     * Get OIM mail data\r
     *\r
@@ -1614,15 +1649,15 @@ class MSN {
 </soap:Envelope>';\r
 \r
         $header_array = array(\r
-            'SOAPAction: '.OIM_MAILDATA_SOAP,\r
+            'SOAPAction: '.self::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 '.BUILDVER.')'\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'\r
         );\r
 \r
-        $this->debug_message('*** URL: '.OIM_MAILDATA_URL);\r
+        $this->debug_message('*** URL: '.self::OIM_MAILDATA_URL);\r
         $this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, OIM_MAILDATA_URL);\r
+        curl_setopt($curl, CURLOPT_URL, self::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
@@ -1684,15 +1719,15 @@ class MSN {
 </soap:Envelope>';\r
 \r
         $header_array = array(\r
-            'SOAPAction: '.OIM_READ_SOAP,\r
+            'SOAPAction: '.self::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 '.BUILDVER.')'\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'\r
         );\r
 \r
-        $this->debug_message('*** URL: '.OIM_READ_URL);\r
+        $this->debug_message('*** URL: '.self::OIM_READ_URL);\r
         $this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, OIM_READ_URL);\r
+        curl_setopt($curl, CURLOPT_URL, self::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
@@ -1760,15 +1795,15 @@ class MSN {
 </soap:Envelope>';\r
 \r
         $header_array = array(\r
-            'SOAPAction: '.OIM_DEL_SOAP,\r
+            'SOAPAction: '.self::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 '.BUILDVER.')'\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'\r
         );\r
 \r
-        $this->debug_message('*** URL: '.OIM_DEL_URL);\r
+        $this->debug_message('*** URL: '.self::OIM_DEL_URL);\r
         $this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, OIM_DEL_URL);\r
+        curl_setopt($curl, CURLOPT_URL, self::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
@@ -1787,7 +1822,7 @@ class MSN {
             $this->debug_message("*** OIM ($msgid) deleted");\r
         return $sMsg;\r
     }\r
-    \r
+\r
     /**\r
      * Send offline message\r
      *\r
@@ -1807,11 +1842,11 @@ class MSN {
         xml:lang="zh-TW"\r
         proxy="MSNMSGR"\r
         xmlns="http://messenger.msn.com/ws/2004/09/oim/"\r
-        msnpVer="'.PROTOCOL.'"\r
-        buildVer="'.BUILDVER.'"/>\r
+        msnpVer="'.self::PROTOCOL.'"\r
+        buildVer="'.self::BUILDVER.'"/>\r
   <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
   <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"\r
-          appid="'.PROD_ID.'"\r
+          appid="'.self::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
@@ -1834,15 +1869,15 @@ X-OIM-Sequence-Num: 1
 </soap:Envelope>';\r
 \r
         $header_array = array(\r
-            'SOAPAction: '.OIM_SEND_SOAP,\r
+            'SOAPAction: '.self::OIM_SEND_SOAP,\r
             'Content-Type: text/xml',\r
-            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.BUILDVER.')'\r
+            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'\r
         );\r
 \r
-        $this->debug_message('*** URL: '.OIM_SEND_URL);\r
+        $this->debug_message('*** URL: '.self::OIM_SEND_URL);\r
         $this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, OIM_SEND_URL);\r
+        curl_setopt($curl, CURLOPT_URL, self::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
@@ -1889,9 +1924,9 @@ X-OIM-Sequence-Num: 1
             //<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
+                $err_msg = $matches[1];\r
             else\r
-            $err_msg = '';\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
@@ -1899,11 +1934,11 @@ X-OIM-Sequence-Num: 1
         }\r
         return array('challenge' => $challenge, 'auth_policy' => $auth_policy);\r
     }\r
-    \r
+\r
     /**\r
      * Contact / Membership list methods\r
      */\r
-    \r
+\r
     /**\r
     * Fetch contact list\r
     *\r
@@ -1993,7 +2028,6 @@ X-OIM-Sequence-Num: 1
                 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';\r
                 $len = strlen($str);\r
                 // NS: >>> ADL {id} {size}\r
-                //TODO introduce error checking\r
                 $this->ns_writeln("ADL $this->id $len");\r
                 $this->ns_writedata($str);\r
             }\r
@@ -2095,15 +2129,15 @@ X-OIM-Sequence-Num: 1
 </soap:Envelope>';\r
 \r
         $header_array = array(\r
-            'SOAPAction: '.DELMEMBER_SOAP,\r
+            'SOAPAction: '.self::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: '.DELMEMBER_URL);\r
+        $this->debug_message('*** URL: '.self::DELMEMBER_URL);\r
         $this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, DELMEMBER_URL);\r
+        curl_setopt($curl, CURLOPT_URL, self::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
@@ -2232,15 +2266,15 @@ X-OIM-Sequence-Num: 1
 </soap:Body>\r
 </soap:Envelope>';\r
         $header_array = array(\r
-            'SOAPAction: '.ADDMEMBER_SOAP,\r
+            'SOAPAction: '.self::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: '.ADDMEMBER_URL);\r
+        $this->debug_message('*** URL: '.self::ADDMEMBER_URL);\r
         $this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, ADDMEMBER_URL);\r
+        curl_setopt($curl, CURLOPT_URL, self::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
@@ -2310,14 +2344,14 @@ X-OIM-Sequence-Num: 1
 </soap:Body>\r
 </soap:Envelope>';\r
         $header_array = array(\r
-            'SOAPAction: '.MEMBERSHIP_SOAP,\r
+            'SOAPAction: '.self::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: '.MEMBERSHIP_URL);\r
+        $this->debug_message('*** URL: '.self::MEMBERSHIP_URL);\r
         $this->debug_message("*** Sending SOAP:\n$XML");\r
         $curl = curl_init();\r
-        curl_setopt($curl, CURLOPT_URL, MEMBERSHIP_URL);\r
+        curl_setopt($curl, CURLOPT_URL, self::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
@@ -2329,7 +2363,7 @@ X-OIM-Sequence-Num: 1
         $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
         curl_close($curl);\r
         $this->debug_message("*** Get Result:\n$data");\r
-        \r
+\r
         if ($http_code != 200) return false;\r
         $p = $data;\r
         $aMemberships = array();\r
@@ -2409,11 +2443,11 @@ X-OIM-Sequence-Num: 1
         }\r
         return $aContactList;\r
     }\r
-    \r
+\r
     /**\r
      * MsnObj related methods\r
      */\r
-    \r
+\r
     /**\r
      *\r
      * @param $FilePath 圖檔路徑\r
@@ -2453,14 +2487,14 @@ X-OIM-Sequence-Num: 1
             }\r
         return $DefineString;\r
     }\r
-    \r
+\r
     /**\r
      * Socket methods\r
      */\r
-    \r
+\r
     /**\r
      * Read data of specified size from NS socket\r
-     * \r
+     *\r
      * @param integer $size Size to read\r
      * @return string Data read\r
      */\r
@@ -2479,7 +2513,7 @@ X-OIM-Sequence-Num: 1
 \r
     /**\r
      * Read line from the NS socket\r
-     * \r
+     *\r
      * @return string Data read\r
      */\r
     private function ns_readln() {\r
@@ -2493,32 +2527,38 @@ X-OIM-Sequence-Num: 1
 \r
     /**\r
      * Write line to NS socket\r
-     * \r
+     *\r
      * Also increments id\r
-     * \r
+     *\r
      * @param string $data Line to write to socket\r
-     * @return void\r
+     * @return mixed Bytes written or false on failure\r
      */\r
     private function ns_writeln($data) {\r
-        @fwrite($this->NSfp, $data."\r\n");\r
-        $this->debug_message("NS: >>> $data");\r
-        $this->id++;\r
+        $result = @fwrite($this->NSfp, $data."\r\n");\r
+        if ($result !== false) {\r
+            $this->debug_message("NS: >>> $data");\r
+            $this->id++;\r
+        }\r
+        return $result;\r
     }\r
 \r
     /**\r
      * Write data to NS socket\r
-     * \r
+     *\r
      * @param string $data Data to write to socket\r
-     * @return void\r
+     * @return mixed Bytes written or false on failure\r
      */\r
     private function ns_writedata($data) {\r
-        @fwrite($this->NSfp, $data);\r
-        $this->debug_message("NS: >>> $data");\r
+        $result = @fwrite($this->NSfp, $data);\r
+        if ($result !== false) {\r
+            $this->debug_message("NS: >>> $data");\r
+        }\r
+        return $result;\r
     }\r
 \r
     /**\r
      * Read data of specified size from given SB socket\r
-     * \r
+     *\r
      * @param resource $socket SB socket\r
      * @param integer $size Size to read\r
      * @return string Data read\r
@@ -2538,7 +2578,7 @@ X-OIM-Sequence-Num: 1
 \r
     /**\r
      * Read line from given SB socket\r
-     * \r
+     *\r
      * @param resource $socket SB Socket\r
      * @return string Line read\r
      */\r
@@ -2553,30 +2593,36 @@ X-OIM-Sequence-Num: 1
 \r
     /**\r
      * Write line to given SB socket\r
-     * \r
+     *\r
      * Also increments id\r
-     * \r
+     *\r
      * @param resource $socket SB socket\r
      * @param integer $id Reference to SB id\r
      * @param string $data Line to write\r
-     * @return void\r
+     * @return mixed Bytes written or false on error\r
      */\r
     private function sb_writeln($socket, &$id, $data) {\r
-        @fwrite($socket, $data."\r\n");\r
-        $this->debug_message("SB: >>> $data");\r
-        $id++;\r
+        $result = @fwrite($socket, $data."\r\n");\r
+        if ($result !== false) {\r
+            $this->debug_message("SB: >>> $data");\r
+            $id++;\r
+        }\r
+        return $result;\r
     }\r
 \r
     /**\r
      * Write data to given SB socket\r
-     * \r
+     *\r
      * @param resource $socket SB socket\r
      * @param $data Data to write to socket\r
-     * @return void\r
+     * @return mixed Bytes written or false on error\r
      */\r
     private function sb_writedata($socket, $data) {\r
-        @fwrite($socket, $data);\r
-        $this->debug_message("SB: >>> $data");\r
+        $result = @fwrite($socket, $data);\r
+        if ($result !== false) {\r
+            $this->debug_message("SB: >>> $data");\r
+        }\r
+        return $result;\r
     }\r
 \r
     /**\r
@@ -2598,16 +2644,16 @@ X-OIM-Sequence-Num: 1
         $info = stream_get_meta_data($socket);\r
         return $info['eof'];\r
     }\r
-    \r
+\r
     /**\r
      * Key generation methods\r
      */\r
-    \r
+\r
     private 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
+        $hash1 = $this->mhash_sha1($magic, $key);\r
+        $hash2 = $this->mhash_sha1($hash1.$magic, $key);\r
+        $hash3 = $this->mhash_sha1($hash1, $key);\r
+        $hash4 = $this->mhash_sha1($hash3.$magic, $key);\r
         return $hash2.substr($hash4, 0, 4);\r
     }\r
 \r
@@ -2617,7 +2663,7 @@ X-OIM-Sequence-Num: 1
         $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
+        $hash = $this->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
@@ -2631,7 +2677,7 @@ X-OIM-Sequence-Num: 1
 \r
         return base64_encode($blob);\r
     }\r
-    \r
+\r
     /**\r
     * Generate challenge response\r
     *\r
@@ -2642,7 +2688,7 @@ X-OIM-Sequence-Num: 1
         // MSNP15\r
         // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges\r
         // Step 1: The MD5 Hash\r
-        $md5Hash = md5($code.PROD_KEY);\r
+        $md5Hash = md5($code.self::PROD_KEY);\r
         $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));\r
         for ($i = 0; $i < 4; $i++) {\r
             $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));\r
@@ -2650,7 +2696,7 @@ X-OIM-Sequence-Num: 1
         }\r
 \r
         // Step 2: A new string\r
-        $chl_id = $code.PROD_ID;\r
+        $chl_id = $code.self::PROD_ID;\r
         $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));\r
 \r
         $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));\r
@@ -2700,7 +2746,7 @@ X-OIM-Sequence-Num: 1
         // $key = bcadd(bcmul($high, 0x100000000), $low);\r
 \r
         // Step 4: Using the key\r
-        $md5Hash = md5($code.PROD_KEY);\r
+        $md5Hash = md5($code.self::PROD_KEY);\r
         $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));\r
 \r
         $hash = '';\r
@@ -2711,11 +2757,11 @@ X-OIM-Sequence-Num: 1
 \r
         return $hash;\r
     }\r
-    \r
+\r
     /**\r
      * Utility methods\r
      */\r
-    \r
+\r
     private function Array2SoapVar($Array, $ReturnSoapVarObj = true, $TypeName = null, $TypeNameSpace = null) {\r
         $ArrayString = '';\r
         foreach($Array as $Key => $Val) {\r
@@ -2723,7 +2769,7 @@ X-OIM-Sequence-Num: 1
             $Attrib = '';\r
             if (is_array($Val[':'])) {\r
                 foreach ($Val[':'] as $AttribName => $AttribVal)\r
-                $Attrib .= " $AttribName = '$AttribVal'";\r
+                    $Attrib .= " $AttribName = '$AttribVal'";\r
             }\r
             if ($Key{0} == '!') {\r
                 //List Type Define\r
@@ -2743,7 +2789,7 @@ X-OIM-Sequence-Num: 1
         if ($ReturnSoapVarObj) return new SoapVar($ArrayString, XSD_ANYXML, $TypeName, $TypeNameSpace);\r
         return $ArrayString;\r
     }\r
-    \r
+\r
     private function linetoArray($lines) {\r
         $lines = str_replace("\r", '', $lines);\r
         $lines = explode("\n", $lines);\r
@@ -2754,7 +2800,7 @@ X-OIM-Sequence-Num: 1
         }\r
         return $Data;\r
     }\r
-    \r
+\r
     /**\r
     * Get Passport ticket\r
     *\r
@@ -2766,7 +2812,7 @@ X-OIM-Sequence-Num: 1
         $password = htmlspecialchars($this->password);\r
 \r
         if ($url === '')\r
-            $passport_url = PASSPORT_URL;\r
+            $passport_url = self::PASSPORT_URL;\r
         else\r
             $passport_url = $url;\r
 \r
@@ -3001,7 +3047,7 @@ X-OIM-Sequence-Num: 1
         $this->ABAuthHeader = new SoapHeader('http://www.msn.com/webservices/AddressBook', 'ABAuthHeader', $this->Array2SoapVar($ABAuthHeaderArray));\r
         return $aTickets;\r
     }\r
-    \r
+\r
     /**\r
     * Generate the data to send a message\r
     *\r
@@ -3013,14 +3059,14 @@ X-OIM-Sequence-Num: 1
         $msg_header = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nX-MMS-IM-Format: FN=$this->font_fn; EF=$this->font_ef; CO=$this->font_co; CS=0; PF=22\r\n\r\n";\r
         $msg_header_len = strlen($msg_header);\r
         if ($network == 1)\r
-            $maxlen = $this->max_msn_message_len - $msg_header_len;\r
+            $maxlen = self::MAX_MSN_MESSAGE_LEN - $msg_header_len;\r
         else\r
-            $maxlen = $this->max_yahoo_message_len - $msg_header_len;\r
+            $maxlen = self::MAX_YAHOO_MESSAGE_LEN - $msg_header_len;\r
         $sMessage = str_replace("\r", '', $sMessage);\r
         $msg = substr($sMessage, 0, $maxlen);\r
         return $msg_header.$msg;\r
     }\r
-    \r
+\r
     /**\r
     * Sleep for the given number of seconds\r
     *\r
@@ -3030,7 +3076,7 @@ X-OIM-Sequence-Num: 1
         $this->debug_message("*** Sleeping for $wait seconds before retrying");\r
         sleep($wait);\r
     }\r
-    \r
+\r
     /**\r
      * Sends a ping command\r
      *\r
@@ -3042,7 +3088,7 @@ X-OIM-Sequence-Num: 1
         // NS: >>> PNG\r
         $this->ns_writeln("PNG");\r
     }\r
-    \r
+\r
     /**\r
     * Methods to add / call callbacks\r
     */\r
@@ -3071,8 +3117,8 @@ X-OIM-Sequence-Num: 1
      * Registers a user handler\r
      *\r
      * Handler List\r
-     * IMIn, Pong, ConnectFailed, Reconnect,\r
-     * AddedToList, RemovedFromList\r
+     * IMIn, SessionReady, Pong, ConnectFailed, Reconnect,\r
+     * AddedToList, RemovedFromList, StatusChange\r
      *\r
      * @param string $event Event name\r
      * @param string $handler User function to call\r
@@ -3087,24 +3133,24 @@ X-OIM-Sequence-Num: 1
             return false;\r
         }\r
     }\r
-    \r
+\r
     /**\r
      * Debugging methods\r
      */\r
-    \r
+\r
     /**\r
      * Print message if debugging is enabled\r
-     * \r
+     *\r
      * @param string $str Message to print\r
      */\r
     private function debug_message($str) {\r
         if (!$this->debug) return;\r
         echo $str."\n";\r
     }\r
-    \r
+\r
     /**\r
      * Dump binary data\r
-     * \r
+     *\r
      * @param string $str Data string\r
      * @return Binary data\r
      */\r
@@ -3133,4 +3179,32 @@ X-OIM-Sequence-Num: 1
         $buf .= "$h_str $a_str\n";\r
         return $buf;\r
     }\r
+\r
+    function mhash_sha1($data, $key)\r
+    {\r
+        if (extension_loaded("mhash"))\r
+            return mhash(MHASH_SHA1, $data, $key);\r
+\r
+        if (function_exists("hash_hmac"))\r
+            return hash_hmac('sha1', $data, $key, true);\r
+\r
+        // RFC 2104 HMAC implementation for php. Hacked by Lance Rushing\r
+        $b = 64;\r
+        if (strlen($key) > $b)\r
+            $key = pack("H*", sha1($key));\r
+        $key = str_pad($key, $b, chr(0x00));\r
+        $ipad = str_pad("", $b, chr(0x36));\r
+        $opad = str_pad("", $b, chr(0x5c));\r
+        $k_ipad = $key ^ $ipad ;\r
+        $k_opad = $key ^ $opad;\r
+\r
+        $sha1_value = sha1($k_opad . pack("H*", sha1($k_ipad . $data)));\r
+\r
+        $hash_data = '';\r
+        $str = join('',explode('\x', $sha1_value));\r
+        $len = strlen($str);\r
+        for ($i = 0; $i < $len; $i += 2)\r
+            $hash_data .= chr(hexdec(substr($str, $i, 2)));\r
+        return $hash_data;\r
+    }\r
 }\r