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