- * Remove Us From Member List Overload Function\r
- * @param $User\r
- * @param $Message\r
- * @param $Network 1 => msn , 32 =>yahoo\r
- * @return unknown_type\r
- */\r
- protected function RemoveUsFromMemberList($User,$Network){}\r
- /**\r
- * Add Us to Member List Overload Function\r
- * @param $User\r
- * @param $Message\r
- * @param $Network 1 => msn , 32 =>yahoo\r
- * @return unknown_type\r
- */\r
- protected function AddUsToMemberList($User,$Network){}\r
- \r
- public function signon() {\r
- $this->log_message("*** try to connect to MSN network");\r
- while(!$this->connect($this->user, $this->password))\r
- {\r
- $this->log_message("!!! Can't connect to server: $this->error");\r
- if(!$this->NSRetryWait($this->retry_wait)) return;\r
- }\r
- $this->UpdateContacts();\r
- $this->LastPing=time();\r
- $this->log_message("*** connected, wait for command");\r
- $start_tm = time();\r
- $ping_tm = time();\r
- stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
- $this->aContactList = $this->getMembershipList();\r
- if ($this->update_pending) {\r
- if (is_array($this->aContactList)) {\r
- $pending = 'Pending';\r
- foreach ($this->aContactList as $u_domain => $aUserList) {\r
- foreach ($aUserList as $u_name => $aNetworks) {\r
- foreach ($aNetworks as $network => $aData) {\r
- if (isset($aData[$pending])) {\r
- // pending list\r
- $cnt = 0;\r
- foreach (array('Allow', 'Reverse') as $list) {\r
- if (isset($aData[$list]))\r
- $cnt++;\r
- else {\r
- if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
- $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
- $cnt++;\r
- }\r
- }\r
- }\r
- if ($cnt >= 2) {\r
- $id = $aData[$pending];\r
- // we can delete it from pending now\r
- if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
- unset($this->aContactList[$u_domain][$u_name][$network][$pending]);\r
- }\r
- }\r
- else {\r
- // sync list\r
- foreach (array('Allow', 'Reverse') as $list) {\r
- if (!isset($aData[$list])) {\r
- if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
- $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- $n = 0;\r
- $sList = '';\r
- $len = 0;\r
- if (is_array($this->aContactList)) {\r
- foreach ($this->aContactList as $u_domain => $aUserList) {\r
- $str = '<d n="'.$u_domain.'">';\r
- $len += strlen($str);\r
- if ($len > 7400) {\r
- $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
- $n++;\r
- $sList = '';\r
- $len = strlen($str);\r
- }\r
- $sList .= $str;\r
- foreach ($aUserList as $u_name => $aNetworks) {\r
- foreach ($aNetworks as $network => $status) {\r
- $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
- $len += strlen($str);\r
- // max: 7500, but <ml l="1"></d></ml> is 19,\r
- // so we use 7475\r
- if ($len > 7475) {\r
- $sList .= '</d>';\r
- $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
- $n++;\r
- $sList = '<d n="'.$u_domain.'">'.$str;\r
- $len = strlen($sList);\r
- }\r
- else\r
- $sList .= $str;\r
- }\r
- }\r
- $sList .= '</d>';\r
- }\r
- }\r
- $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
- // NS: >>> BLP {id} BL\r
- $this->ns_writeln("BLP $this->id BL");\r
- foreach ($aADL as $str) {\r
- $len = strlen($str);\r
- // NS: >>> ADL {id} {size}\r
- $this->ns_writeln("ADL $this->id $len");\r
- $this->ns_writedata($str);\r
- }\r
- // NS: >>> PRP {id} MFN name\r
- if ($this->alias == '') $this->alias = $user;\r
- $aliasname = rawurlencode($this->alias);\r
- $this->ns_writeln("PRP $this->id MFN $aliasname");\r
- //設定個人大頭貼\r
- //$MsnObj=$this->PhotoStckObj();\r
- // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
- $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
- if($this->PhotoStickerFile!==false)\r
- $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
- // NS: >>> UUX {id} length\r
- $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
- $len = strlen($str);\r
- $this->ns_writeln("UUX $this->id $len");\r
- $this->ns_writedata($str);\r
- }\r
- \r
- public function NSreceive() {\r
- $this->log_message("*** startup ***");\r
- \r
- $aADL = array();\r
- \r
- // Sign in again if not signed in or socket failed\r
- if (!is_resource($this->NSfp) || feof($this->NSfp)) {\r
- $this->signon();\r
- }\r
- \r
- $data = $this->ns_readln();\r
- if($data === false) {\r
- // There was no data / an error when reading from the socket so reconnect\r
- $this->signon();\r
- } else {\r
- switch (substr($data,0,3))\r
- {\r
- case 'SBS':\r
- // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
- // NS: <<< SBS 0 null\r
- break;\r
- \r
- case 'RFS':\r
- // FIXME:\r
- // NS: <<< RFS ???\r
- // refresh ADL, so we re-send it again\r
- if (is_array($aADL)) {\r
- foreach ($aADL as $str) {\r
- $len = strlen($str);\r
- // NS: >>> ADL {id} {size}\r
- $this->ns_writeln("ADL $this->id $len");\r
- $this->ns_writedata($str);\r
- }\r
- }\r
- break;\r
- \r
- case 'LST':\r
- // NS: <<< LST {email} {alias} 11 0\r
- @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\r
- @list($u_name, $u_domain) = @explode('@', $email);\r
- if (!isset($this->aContactList[$u_domain][$u_name][1])) {\r
- $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
- $this->log_message("*** add to our contact list: $u_name@$u_domain");\r
- }\r
- break;\r
- \r
- case 'ADL':\r
- // randomly, we get ADL command, someome add us to their contact list for MSNP15\r
- // NS: <<< ADL 0 {size}\r
- @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);\r
- if (is_numeric($size) && $size > 0)\r
- {\r
- $data = $this->ns_readdata($size);\r
- preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
- if (is_array($matches) && count($matches) > 0)\r
- {\r
- $u_domain = $matches[1];\r
- $u_name = $matches[2];\r
- $network = $matches[4];\r
- if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
- $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
- else\r
- {\r
- $re_login = false;\r
- $cnt = 0;\r
- foreach (array('Allow', 'Reverse') as $list)\r
- {\r
- if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
- {\r
- if ($re_login) {\r
- $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
- continue;\r
- }\r
- $aTickets = $this->get_passport_ticket();\r
- if (!$aTickets || !is_array($aTickets)) {\r
- // failed to login? ignore it\r
- $this->log_message("*** can't re-login, something wrong here");\r
- $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
- continue;\r
- }\r
- $re_login = true;\r
- $this->ticket = $aTickets;\r
- $this->log_message("**** get new ticket, try it again");\r
- if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
- {\r
- $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
- continue;\r
- }\r
- }\r
- $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
- $cnt++;\r
- }\r
- $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
- }\r
- $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
- $len = strlen($str);\r
- }\r
- else\r
- $this->log_message("*** someone add us to their list: $data");\r
- $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
- }\r
- break;\r
- \r
- case 'RML':\r
- // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
- // NS: <<< RML 0 {size}\r
- @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);\r
- if (is_numeric($size) && $size > 0)\r
- {\r
- $data = $this->ns_readdata($size);\r
- preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
- if (is_array($matches) && count($matches) > 0)\r
- {\r
- $u_domain = $matches[1];\r
- $u_name = $matches[2];\r
- $network = $matches[4];\r
- if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
- {\r
- $aData = $this->aContactList[$u_domain][$u_name][$network];\r
- foreach ($aData as $list => $id)\r
- $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
- unset($this->aContactList[$u_domain][$u_name][$network]);\r
- $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
- }\r
- else\r
- $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
- $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);\r
- }\r
- else\r
- $this->log_message("*** someone remove us from their list: $data");\r
- }\r
- break;\r
- \r
- case 'MSG':\r
- // randomly, we get MSG notification from server\r
- // NS: <<< MSG Hotmail Hotmail {size}\r
- @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);\r
- if (is_numeric($size) && $size > 0) {\r
- $data = $this->ns_readdata($size);\r
- $aLines = @explode("\n", $data);\r
- $header = true;\r
- $ignore = false;\r
- $maildata = '';\r
- foreach ($aLines as $line) {\r
- $line = rtrim($line);\r
- if ($header) {\r
- if ($line === '') {\r
- $header = false;\r
- continue;\r
- }\r
- if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
- if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&\r
- strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
- // we just need text/x-msmsgsinitialmdatanotification\r
- // or text/x-msmsgsoimnotification\r
- $ignore = true;\r
- break;\r
- }\r
- }\r
- continue;\r
- }\r
- if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
- $maildata = trim(substr($line, 10));\r
- break;\r
- }\r
- }\r
- if ($ignore) {\r
- $this->log_message("*** ingnore MSG for: $line");\r
- break;\r
- }\r
- if ($maildata == '') {\r
- $this->log_message("*** ingnore MSG not for OIM");\r
- break;\r
- }\r
- $re_login = false;\r
- if (strcasecmp($maildata, 'too-large') == 0) {\r
- $this->log_message("*** large mail-data, need to get the data via SOAP");\r
- $maildata = $this->getOIM_maildata();\r
- if ($maildata === false) {\r
- $this->log_message("*** can't get mail-data via SOAP");\r
- // maybe we need to re-login again\r
- $aTickets = $this->get_passport_ticket();\r
- if (!$aTickets || !is_array($aTickets)) {\r
- // failed to login? ignore it\r
- $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
- break;\r
- }\r
- $re_login = true;\r
- $this->ticket = $aTickets;\r
- $this->log_message("**** get new ticket, try it again");\r
- $maildata = $this->getOIM_maildata();\r
- if ($maildata === false) {\r
- $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
- break;\r
- }\r
- }\r
- }\r
- // could be a lots of <M>...</M>, so we can't use preg_match here\r
- $p = $maildata;\r
- $aOIMs = array();\r
- while (1) {\r
- $start = strpos($p, '<M>');\r
- $end = strpos($p, '</M>');\r
- if ($start === false || $end === false || $start > $end) break;\r
- $end += 4;\r
- $sOIM = substr($p, $start, $end - $start);\r
- $aOIMs[] = $sOIM;\r
- $p = substr($p, $end);\r
- }\r
- if (count($aOIMs) == 0) {\r
- $this->log_message("*** ingnore empty OIM");\r
- break;\r
- }\r
- foreach ($aOIMs as $maildata) {\r
- // T: 11 for MSN, 13 for Yahoo\r
- // S: 6 for MSN, 7 for Yahoo\r
- // RT: the datetime received by server\r
- // RS: already read or not\r
- // SZ: size of message\r
- // E: sender\r
- // I: msgid\r
- // F: always 00000000-0000-0000-0000-000000000009\r
- // N: sender alias\r
- preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
- if (count($matches) == 0) {\r
- $this->log_message("*** ingnore OIM maildata without <T>type</T>");\r
- continue;\r
- }\r
- $oim_type = $matches[1];\r
- if ($oim_type = 13)\r
- $network = 32;\r
- else\r
- $network = 1;\r
- preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
- if (count($matches) == 0) {\r
- $this->log_message("*** ingnore OIM maildata without <E>sender</E>");\r
- continue;\r
- }\r
- $oim_sender = $matches[1];\r
- preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
- if (count($matches) == 0) {\r
- $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");\r
- continue;\r
- }\r
- $oim_msgid = $matches[1];\r
- preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
- $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
- preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
- $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
- $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
- $sMsg = $this->getOIM_message($oim_msgid);\r
- if ($sMsg === false) {\r
- $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
- if ($re_login) {\r
- $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
- continue;\r
- }\r
- $aTickets = $this->get_passport_ticket();\r
- if (!$aTickets || !is_array($aTickets)) {\r
- // failed to login? ignore it\r
- $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
- continue;\r
- }\r
- $re_login = true;\r
- $this->ticket = $aTickets;\r
- $this->log_message("**** get new ticket, try it again");\r
- $sMsg = $this->getOIM_message($oim_msgid);\r
- if ($sMsg === false) {\r
- $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
- continue;\r
- }\r
- }\r
- $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
- \r
- //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
- $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));\r
- }\r
- }\r
- break;\r
- \r
- case 'UBM':\r
- // randomly, we get UBM, this is the message from other network, like Yahoo!\r
- // NS: <<< UBM {email} $network $type {size}\r
- @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);\r
- if (is_numeric($size) && $size > 0)\r
- {\r
- $data = $this->ns_readdata($size);\r
- $aLines = @explode("\n", $data);\r
- $header = true;\r
- $ignore = false;\r
- $sMsg = '';\r
- foreach ($aLines as $line) {\r
- $line = rtrim($line);\r
- if ($header) {\r
- if ($line === '') {\r
- $header = false;\r
- continue;\r
- }\r
- if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
- $ignore = true;\r
- break;\r
- }\r
- continue;\r
- }\r
- $aSubLines = @explode("\r", $line);\r
- foreach ($aSubLines as $str) {\r
- if ($sMsg !== '')\r
- $sMsg .= "\n";\r
- $sMsg .= $str;\r
- }\r
- }\r
- if($ignore)\r
- {\r
- $this->log_message("*** ingnore from $from_email: $line");\r
- break;\r
- }\r
- $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
- //$this->ReceivedMessage($from_email,$sMsg,$network,false);\r
- $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
- \r
- case 'CHL':\r
- // randomly, we'll get challenge from server\r
- // NS: <<< CHL 0 {code}\r
- @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);\r
- $fingerprint = $this->getChallenge($chl_code);\r
- // NS: >>> QRY {id} {product_id} 32\r
- // NS: >>> fingerprint\r
- $this->ns_writeln("QRY $this->id $this->prod_id 32");\r
- $this->ns_writedata($fingerprint);\r
- $this->ns_writeln("CHG $this->id NLN $this->clientid"); \r
- if($this->PhotoStickerFile!==false)\r
- $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
- break;\r
- case 'CHG':\r
- // NS: <<< CHG {id} {status} {code}\r
- // ignore it\r
- // change our status to online first\r
- break;\r
- \r
- case 'XFR':\r
- // sometimes, NS will redirect to another NS\r
- // MSNP9\r
- // NS: <<< XFR {id} NS {server} 0 {server}\r
- // MSNP15\r
- // NS: <<< XFR {id} NS {server} U D\r
- // for normal switchboard XFR\r
- // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
- @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);\r
- @list($ip, $port) = @explode(':', $server);\r
- if ($server_type != 'SB') {\r
- // maybe exit?\r
- // this connection will close after XFR\r
- $this->NSLogout();\r
- continue;\r
- }\r
- if(count($this->MessageQueue))\r
- {\r
- foreach($this->MessageQueue as $User => $Message)\r
- {\r
- //$this->ChildProcess[$ChildPid]\r
- $this->log_message("*** XFR SB $User");\r
- $pid=pcntl_fork();\r
- if($pid)\r
- {\r
- //Parrent Process\r
- $this->ChildProcess[$pid]=$User;\r
- break;\r
- }\r
- elseif($pid==-1)\r
- {\r
- $this->log_message("*** Fork Error $User");\r
- break;\r
- }\r
- else\r
- {\r
- //Child Process\r
- $this->log_message("*** Child Process Start for $User");\r
- unset($Message['XFRSent']);\r
- unset($Message['ReqTime']);\r
- $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
- if ($bSBresult === false)\r
- {\r
- // error for switchboard\r
- $this->log_message("!!! error for sending message to ".$User);\r
- }\r
- die;\r
- }\r
- }\r
- unset($this->MessageQueue[$User]);\r
- }\r
- /*\r
- $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
- if ($bSBresult === false) {\r
- // error for switchboard\r
- $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
- $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
- }*/\r
- break;\r
- case 'QNG':\r
- // NS: <<< QNG {time}\r
- //@list(/* QNG */, $this->ping_wait) = @explode(' ', $data);\r
- //if ($this->ping_wait == 0) $this->ping_wait = 50;\r
- //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;\r
- //Mod by Ricky Set Online\r
- break;\r
- \r
- case 'RNG':\r
- if($this->PhotoStickerFile!==false)\r
- $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
- else\r
- $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
- // someone is trying to talk to us\r
- // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
- $this->log_message("NS: <<< RNG $data");\r
- @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
- @list($sb_ip, $sb_port) = @explode(':', $server);\r
- if($this->IsIgnoreMail($email))\r
- {\r
- $this->log_message("*** Ignore RNG from $email");\r
- break;\r
- }\r
- $this->log_message("*** RING from $email, $sb_ip:$sb_port");\r
- $this->addContact($email,1,$email, true);\r
- $pid=pcntl_fork();\r
- if($pid)\r
- {\r
- //Parrent Process\r
- $this->ChildProcess[$pid]='RNG';\r
- break;\r
- }\r
- elseif($pid==-1)\r
- {\r
- $this->log_message("*** Fork Error $User");\r
- break;\r
- }\r
- else\r
- {\r
- //Child Process\r
- $this->log_message("*** Ring Child Process Start for $User");\r
- $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
- die;\r
- }\r
- break;\r
- case 'OUT':\r
- // force logout from NS\r
- // NS: <<< OUT xxx\r
- $this->log_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
+ * Add contact to list\r
+ *\r
+ * @param string $email\r
+ * @param integer $network\r
+ * @param string $list\r
+ */\r
+ function addMemberToList($email, $network, $list) {\r
+ if ($network != 1 && $network != 32) return true;\r
+ $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
+ $user = $email;\r
+\r
+ if ($network == 1)\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceHandle>\r
+ <Id>0</Id>\r
+ <Type>Messenger</Type>\r
+ <ForeignId></ForeignId>\r
+ </serviceHandle>\r
+ <memberships>\r
+ <Membership>\r
+ <MemberRole>'.$list.'</MemberRole>\r
+ <Members>\r
+ <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+ <Type>Passport</Type>\r
+ <State>Accepted</State>\r
+ <PassportName>'.$user.'</PassportName>\r
+ </Member>\r
+ </Members>\r
+ </Membership>\r
+ </memberships>\r
+ </AddMember>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+ else\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceHandle>\r
+ <Id>0</Id>\r
+ <Type>Messenger</Type>\r
+ <ForeignId></ForeignId>\r
+ </serviceHandle>\r
+ <memberships>\r
+ <Membership>\r
+ <MemberRole>'.$list.'</MemberRole>\r
+ <Members>\r
+ <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+ <Type>Email</Type>\r
+ <State>Accepted</State>\r
+ <Email>'.$user.'</Email>\r
+ <Annotations>\r
+ <Annotation>\r
+ <Name>MSN.IM.BuddyType</Name>\r
+ <Value>32:YAHOO</Value>\r
+ </Annotation>\r
+ </Annotations>\r
+ </Member>\r
+ </Members>\r
+ </Membership>\r
+ </memberships>\r
+ </AddMember>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+ $header_array = array(\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: '.self::ADDMEMBER_URL);\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\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
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->debug_message("*** Could not add member (network: $network) $email to $list list");\r
+ return false;\r
+ }\r
+ $faultcode = trim($matches[1]);\r
+ $faultstring = trim($matches[2]);\r
+ if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {\r
+ $this->debug_message("*** Could not add member (network: $network) $email to $list list, error code: $faultcode, $faultstring");\r
+ return false;\r
+ }\r
+ $this->debug_message("*** Could not add member (network: $network) $email to $list list, already present");\r
+ return true;\r
+ }\r
+ $this->debug_message("*** Member successfully added (network: $network) $email to $list list");\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Get membership lists\r
+ *\r
+ * @param mixed $returnData Membership list or false on failure\r
+ */\r
+ function getMembershipList($returnData = false) {\r
+ $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>Initial</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceFilter>\r
+ <Types>\r
+ <ServiceType>Messenger</ServiceType>\r
+ <ServiceType>Invitation</ServiceType>\r
+ <ServiceType>SocialNetwork</ServiceType>\r
+ <ServiceType>Space</ServiceType>\r
+ <ServiceType>Profile</ServiceType>\r
+ </Types>\r
+ </serviceFilter>\r
+ </FindMembership>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+ $header_array = array(\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: '.self::MEMBERSHIP_URL);\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\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
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) return false;\r
+ $p = $data;\r
+ $aMemberships = array();\r
+ while (1) {\r
+ //$this->debug_message("search p = $p");\r
+ $start = strpos($p, '<Membership>');\r
+ $end = strpos($p, '</Membership>');\r
+ if ($start === false || $end === false || $start > $end) break;\r
+ //$this->debug_message("start = $start, end = $end");\r
+ $end += 13;\r
+ $sMembership = substr($p, $start, $end - $start);\r
+ $aMemberships[] = $sMembership;\r
+ //$this->debug_message("add sMembership = $sMembership");\r
+ $p = substr($p, $end);\r
+ }\r
+ //$this->debug_message("aMemberships = ".var_export($aMemberships, true));\r
+\r
+ $aContactList = array();\r
+ foreach ($aMemberships as $sMembership) {\r
+ //$this->debug_message("sMembership = $sMembership");\r
+ if (isset($matches)) unset($matches);\r
+ preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);\r
+ if (count($matches) == 0) continue;\r
+ $sMemberRole = $matches[1];\r
+ //$this->debug_message("MemberRole = $sMemberRole");\r
+ if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;\r
+ $p = $sMembership;\r
+ if (isset($aMembers)) unset($aMembers);\r
+ $aMembers = array();\r
+ while (1) {\r
+ //$this->debug_message("search p = $p");\r
+ $start = strpos($p, '<Member xsi:type="');\r
+ $end = strpos($p, '</Member>');\r
+ if ($start === false || $end === false || $start > $end) break;\r
+ //$this->debug_message("start = $start, end = $end");\r
+ $end += 9;\r
+ $sMember = substr($p, $start, $end - $start);\r
+ $aMembers[] = $sMember;\r
+ //$this->debug_message("add sMember = $sMember");\r
+ $p = substr($p, $end);\r
+ }\r
+ //$this->debug_message("aMembers = ".var_export($aMembers, true));\r
+ foreach ($aMembers as $sMember) {\r
+ //$this->debug_message("sMember = $sMember");\r
+ if (isset($matches)) unset($matches);\r
+ preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);\r
+ if (count($matches) == 0) continue;\r
+ $sMemberType = $matches[1];\r
+ //$this->debug_message("MemberType = $sMemberType");\r
+ $network = -1;\r
+ preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);\r
+ if (count($matches) == 0) continue;\r
+ $id = $matches[1];\r
+ if ($sMemberType == 'PassportMember') {\r
+ if (strpos($sMember, '<Type>Passport</Type>') === false) continue;\r
+ $network = 1;\r
+ preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);\r
+ }\r
+ else if ($sMemberType == 'EmailMember') {\r
+ if (strpos($sMember, '<Type>Email</Type>') === false) continue;\r
+ // Value is 32: or 32:YAHOO\r
+ preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);\r
+ if (count($matches) == 0) continue;\r
+ if ($matches[1] != 32) continue;\r
+ $network = 32;\r
+ preg_match('#<Email>(.*)</Email>#', $sMember, $matches);\r
+ }\r
+ if ($network == -1) continue;\r
+ if (count($matches) > 0) {\r
+ $email = $matches[1];\r
+ @list($u_name, $u_domain) = @explode('@', $email);\r
+ if ($u_domain == NULL) continue;\r
+ $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;\r
+ $this->debug_message("*** Adding new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
+ }\r
+ }\r
+ }\r
+ return $aContactList;\r
+ }\r
+\r
+ /**\r
+ * MsnObj related methods\r
+ */\r
+\r
+ /**\r
+ *\r
+ * @param $FilePath 圖檔路徑\r
+ * @param $Type 檔案類型 3=>大頭貼,2表情圖案\r
+ * @return array\r
+ */\r
+ private function MsnObj($FilePath, $Type = 3) {\r
+ if (!($FileSize=filesize($FilePath))) return '';\r
+ $Location = md5($FilePath);\r
+ $Friendly = md5($FilePath.$Type);\r
+ if (isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];\r
+ $sha1d = base64_encode(sha1(file_get_contents($FilePath), true));\r
+ $sha1c = base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d", true));\r
+ $this->MsnObjArray[$Location] = $FilePath;\r
+ $MsnObj = '<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';\r
+ $this->MsnObjMap[$Location] = $MsnObj;\r
+ $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");\r
+ return $MsnObj;\r
+ }\r
+\r
+ private function GetPictureFilePath($Context) {\r
+ $MsnObj = base64_decode($Context);\r
+ if (preg_match('/location="(.*?)"/i', $MsnObj, $Match))\r
+ $location = $Match[1];\r
+ $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");\r
+ if ($location && isset($this->MsnObjArray[$location]))\r
+ return $this->MsnObjArray[$location];\r
+ return false;\r
+ }\r
+\r
+ private function GetMsnObjDefine($Message) {\r
+ $DefineString = '';\r
+ if (is_array($this->Emotions))\r
+ foreach ($this->Emotions as $Pattern => $FilePath) {\r
+ if (strpos($Message, $Pattern) !== false)\r
+ $DefineString .= "$Pattern\t".$this->MsnObj($FilePath, 2)."\t";\r
+ }\r
+ return $DefineString;\r
+ }\r
+\r
+ /**\r
+ * Socket methods\r
+ */\r
+\r
+ /**\r
+ * Read data of specified size from NS socket\r
+ *\r
+ * @param integer $size Size to read\r
+ * @return string Data read\r
+ */\r
+ private function ns_readdata($size) {\r
+ $data = '';\r
+ $count = 0;\r
+ while (!feof($this->NSfp)) {\r
+ $buf = @fread($this->NSfp, $size - $count);\r
+ $data .= $buf;\r
+ $count += strlen($buf);\r
+ if ($count >= $size) break;\r
+ }\r
+ $this->debug_message("NS: data ($size/$count) <<<\n$data");\r
+ return $data;\r
+ }\r
+\r
+ /**\r
+ * Read line from the NS socket\r
+ *\r
+ * @return string Data read\r
+ */\r
+ private function ns_readln() {\r
+ $data = @fgets($this->NSfp, 4096);\r
+ if ($data !== false) {\r
+ $data = trim($data);\r
+ $this->debug_message("NS: <<< $data");\r
+ }\r
+ return $data;\r
+ }\r
+\r
+ /**\r
+ * Write line to NS socket\r
+ *\r
+ * Also increments id\r
+ *\r
+ * @param string $data Line to write to socket\r
+ * @return mixed Bytes written or false on failure\r
+ */\r
+ private function ns_writeln($data) {\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
+ * @param string $data Data to write to socket\r
+ * @return mixed Bytes written or false on failure\r
+ */\r
+ private function ns_writedata($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
+ * @param resource $socket SB socket\r
+ * @param integer $size Size to read\r
+ * @return string Data read\r
+ */\r
+ private function sb_readdata($socket, $size) {\r
+ $data = '';\r
+ $count = 0;\r
+ while (!feof($socket)) {\r
+ $buf = @fread($socket, $size - $count);\r
+ $data .= $buf;\r
+ $count += strlen($buf);\r
+ if ($count >= $size) break;\r
+ }\r
+ $this->debug_message("SB: data ($size/$count) <<<\n$data");\r
+ return $data;\r
+ }\r
+\r
+ /**\r
+ * Read line from given SB socket\r
+ *\r
+ * @param resource $socket SB Socket\r
+ * @return string Line read\r
+ */\r
+ private function sb_readln($socket) {\r
+ $data = @fgets($socket, 4096);\r
+ if ($data !== false) {\r
+ $data = trim($data);\r
+ $this->debug_message("SB: <<< $data");\r
+ }\r
+ return $data;\r
+ }\r
+\r
+ /**\r
+ * Write line to given SB socket\r
+ *\r
+ * Also increments id\r
+ *\r
+ * @param resource $socket SB socket\r
+ * @param integer $id Reference to SB id\r
+ * @param string $data Line to write\r
+ * @return mixed Bytes written or false on error\r
+ */\r
+ private function sb_writeln($socket, &$id, $data) {\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
+ * @param resource $socket SB socket\r
+ * @param $data Data to write to socket\r
+ * @return mixed Bytes written or false on error\r
+ */\r
+ private function sb_writedata($socket, $data) {\r
+ $result = @fwrite($socket, $data);\r
+ if ($result !== false) {\r
+ $this->debug_message("SB: >>> $data");\r
+ }\r
+ return $result;\r
+ }\r
+\r
+ /**\r
+ * Get all the sockets currently in use\r
+ *\r
+ * @return array Array of socket resources\r
+ */\r
+ public function getSockets() {\r
+ return array_merge(array($this->NSfp), $this->switchBoardSessionLookup);\r
+ }\r
+\r
+ /**\r
+ * Checks socket for end of file\r
+ *\r
+ * @param resource $socket Socket to check\r
+ * @return boolean true if end of file (socket)\r
+ */\r
+ private static function socketcheck($socket){\r
+ $info = stream_get_meta_data($socket);\r
+ return $info['eof'];\r
+ }\r
+\r
+ /**\r
+ * Key generation methods\r
+ */\r
+\r
+ private function derive_key($key, $magic) {\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
+ private function generateLoginBLOB($key, $challenge) {\r
+ $key1 = base64_decode($key);\r
+ $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');\r
+ $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');\r
+\r
+ // get hash of challenge using key2\r
+ $hash = $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
+\r
+ $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);\r
+\r
+ $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);\r
+ $blob .= $iv;\r
+ $blob .= $hash;\r
+ $blob .= $cipher;\r
+\r
+ return base64_encode($blob);\r
+ }\r
+\r
+ /**\r
+ * Generate challenge response\r
+ *\r
+ * @param string $code\r
+ * @return string challenge response code\r
+ */\r
+ private function getChallenge($code) {\r
+ // MSNP15\r
+ // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges\r
+ // Step 1: The MD5 Hash\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
+ $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;\r
+ }\r
+\r
+ // Step 2: A new string\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
+ for ($i = 0; $i < count($aID); $i++) {\r
+ $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));\r
+ $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);\r
+ }\r
+\r
+ // Step 3: The 64 bit key\r
+ $magic_num = 0x0E79A9C1;\r
+ $str7f = 0x7FFFFFFF;\r
+ $high = 0;\r
+ $low = 0;\r
+ for ($i = 0; $i < count($aID); $i += 2) {\r
+ $temp = $aID[$i];\r
+ $temp = bcmod(bcmul($magic_num, $temp), $str7f);\r
+ $temp = bcadd($temp, $high);\r
+ $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);\r
+ $temp = bcmod($temp, $str7f);\r
+\r
+ $high = $aID[$i+1];\r
+ $high = bcmod(bcadd($high, $temp), $str7f);\r
+ $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);\r
+ $high = bcmod($high, $str7f);\r
+\r
+ $low = bcadd(bcadd($low, $high), $temp);\r
+ }\r
+\r
+ $high = bcmod(bcadd($high, $aMD5[1]), $str7f);\r
+ $low = bcmod(bcadd($low, $aMD5[3]), $str7f);\r
+\r
+ $new_high = bcmul($high & 0xFF, 0x1000000);\r
+ $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));\r
+ $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));\r
+ $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));\r
+ // we need integer here\r
+ $high = 0+$new_high;\r
+\r
+ $new_low = bcmul($low & 0xFF, 0x1000000);\r
+ $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));\r
+ $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));\r
+ $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));\r
+ // we need integer here\r
+ $low = 0+$new_low;\r
+\r
+ // we just use 32 bits integer, don't need the key, just high/low\r
+ // $key = bcadd(bcmul($high, 0x100000000), $low);\r
+\r
+ // Step 4: Using the key\r
+ $md5Hash = md5($code.self::PROD_KEY);\r
+ $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));\r
+\r
+ $hash = '';\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);\r
+\r
+ return $hash;\r
+ }\r
+\r
+ /**\r
+ * Utility methods\r
+ */\r
+\r
+ private function Array2SoapVar($Array, $ReturnSoapVarObj = true, $TypeName = null, $TypeNameSpace = null) {\r
+ $ArrayString = '';\r
+ foreach($Array as $Key => $Val) {\r
+ if ($Key{0} == ':') continue;\r
+ $Attrib = '';\r
+ if (is_array($Val[':'])) {\r
+ foreach ($Val[':'] as $AttribName => $AttribVal)\r
+ $Attrib .= " $AttribName = '$AttribVal'";\r
+ }\r
+ if ($Key{0} == '!') {\r
+ //List Type Define\r
+ $Key = substr($Key,1);\r
+ foreach ($Val as $ListKey => $ListVal) {\r
+ if ($ListKey{0} == ':') continue;\r
+ if (is_array($ListVal)) $ListVal = $this->Array2SoapVar($ListVal, false);\r
+ elseif (is_bool($ListVal)) $ListVal = $ListVal ? 'true' : 'false';\r
+ $ArrayString .= "<$Key$Attrib>$ListVal</$Key>";\r
+ }\r
+ continue;\r
+ }\r
+ if (is_array($Val)) $Val = $this->Array2SoapVar($Val, false);\r
+ elseif (is_bool($Val)) $Val = $Val ? 'true' : 'false';\r
+ $ArrayString .= "<$Key$Attrib>$Val</$Key>";\r
+ }\r
+ if ($ReturnSoapVarObj) return new SoapVar($ArrayString, XSD_ANYXML, $TypeName, $TypeNameSpace);\r
+ return $ArrayString;\r
+ }\r
+\r
+ private function linetoArray($lines) {\r
+ $lines = str_replace("\r", '', $lines);\r
+ $lines = explode("\n", $lines);\r
+ foreach ($lines as $line) {\r
+ if (!isset($line{3})) continue;\r
+ list($Key, $Val) = explode(':', $line);\r
+ $Data[trim($Key)] = trim($Val);\r
+ }\r
+ return $Data;\r
+ }\r
+\r
+ /**\r
+ * Get Passport ticket\r
+ *\r
+ * @param string $url URL string (Optional)\r
+ * @return mixed Array of tickets or false on failure\r
+ */\r
+ private function get_passport_ticket($url = '') {\r
+ $user = $this->user;\r
+ $password = htmlspecialchars($this->password);\r
+\r
+ if ($url === '')\r
+ $passport_url = self::PASSPORT_URL;\r
+ else\r
+ $passport_url = $url;\r
+\r
+ $XML = '<?xml version="1.0" encoding="UTF-8"?>\r
+<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"\r
+ xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"\r
+ xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"\r
+ xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"\r
+ xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"\r
+ xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"\r
+ xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">\r
+<Header>\r
+ <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">\r
+ <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>\r
+ <ps:BinaryVersion>4</ps:BinaryVersion>\r
+ <ps:UIVersion>1</ps:UIVersion>\r
+ <ps:Cookies></ps:Cookies>\r
+ <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>\r
+ </ps:AuthInfo>\r
+ <wsse:Security>\r
+ <wsse:UsernameToken Id="user">\r
+ <wsse:Username>'.$user.'</wsse:Username>\r
+ <wsse:Password>'.$password.'</wsse:Password>\r
+ </wsse:UsernameToken>\r
+ </wsse:Security>\r
+</Header>\r
+<Body>\r
+ <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">\r
+ <wst:RequestSecurityToken Id="RST0">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>http://Passport.NET/tb</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST1">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>messengerclear.live.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST2">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>messenger.msn.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST3">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>contacts.msn.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST4">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>messengersecure.live.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST5">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>spaces.live.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST6">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>storage.msn.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ </ps:RequestMultipleSecurityTokens>\r
+</Body>\r
+</Envelope>';\r
+\r
+ $this->debug_message("*** URL: $passport_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $passport_url);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ // sometimes, redirect to another URL\r
+ // MSNP15\r
+ //<faultcode>psf:Redirect</faultcode>\r
+ //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>\r
+ //<faultstring>Authentication Failure</faultstring>\r
+ if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {\r
+ $this->debug_message("*** Could not get passport ticket! http code = $http_code");\r
+ return false;\r