分类:
2009-12-30 18:43:56
/**
* 获取MSN联系人包括分组信息
*
* @return array
* email=>array(
* email=>
* name=>
* group=>
* )
* @uses
* var
* contact_ticket
* debug
* function
* debug_message
*/
public function getAddressBook(){
//SOAP请求包
$XML=' xmlns:xsi=\'\'
xmlns:xsd=\'\'
xmlns:soapenc=\'\'>
CFE80F9D-180F-4399-82AB-413F33A1FA11
false
Initial
false
' .htmlspecialchars($this->contact_ticket).'
00000000-0000-0000-0000-000000000000
Full
false
0001-01-01T00:00:00.0000000-08:00
';
$header_array = array(
'SOAPAction: /ABFindAll',
'Content-Type: text/xml; charset=utf-8',
'Cookie: MSPAuth=Removed',
'Host: contacts.msn.com'
);
//发送SOAP请求
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL,'');
curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
$data = curl_exec($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
//$this->debug_message("*** Get Result:\n$data");
if ($http_code != 200) return array();
//将回应包转换为对象
$start = strpos($data, '' );
$end = strpos($data, '');
$start+=17;
$result=substr($data,$start,$end-$start);
//$this->debug_message("*** Get Result:\n$result");
$result=simplexml_load_string("" .$result."");
//获取分组信息
$groups=array();
foreach($result->groups->Group as $group){
$groups[(string)$group->groupId]=(string)$group->groupInfo->name;
}
//获取联系人信息
$contacts=array();
foreach($result->contacts->Contact as $contact){
$info=$contact->contactInfo;
//自己,不加入联系人中
if($info->contactType=='Me')continue;
//获取所属的组
$email=$name=$group='';
if(isset($info->groupIds))if(isset($info->groupIds->guid)){
$group=$groups[(string)$info->groupIds->guid];
}
//获取EMAIL地址
if(isset($info->passportName)){
$email=$info->passportName;
}elseif(isset($info->emails)&&isset($info->emails->ContactEmail)){
if(is_array($info->emails->ContactEmail)){
$email=$info->emails->ContactEmail[0]->email;
}else{
$email=$info->emails->ContactEmail->email;
}
}
//获取显示名
if(isset($info->annotations))if(isset($info->annotations->Annotation)){
if(is_array($info->annotations->Annotation)){
foreach($info->annotations->Annotation as $ann){
if($ann->Name=='AB.NickName'){
$name=$ann->Value;
break;
}
}
}elseif($info->annotations->Annotation->Name=='AB.NickName'){
$name=$info->annotations->Annotation->Value;
}
}
if(!$name)if(isset($info->displayName))$name=$info->displayName;
if(!$name)if(isset($info->quickName))$name=$info->quickName;
if(!$name)$name=$email;
//加入联系人数组中
$contacts[(string)$email]=array('email'=>(string)$email,'name'=>(string)$name,'group'=>$group);
}
return $contacts;
}
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);
return $hash2.substr($hash4, 0, 4);
}
private function generateLoginBLOB($key, $challenge)
{
$key1 = base64_decode($key);
$key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
$key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
// get hash of challenge using key2
$hash = mhash(MHASH_SHA1, $challenge, $key2);
// get 8 bytes random data
$iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
$cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
$blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
$blob .= $iv;
$blob .= $hash;
$blob .= $cipher;
return base64_encode($blob);
}
public function sendMessage($sMessage, $aTo) {
if (!is_array($aTo)) {
if ($aTo === '')
$aTo = array();
else
$aTo = array($aTo);
}
stream_set_timeout($this->fp, $this->stream_timeout);
$quit = false;
$online_cnt = 0;
$offline_cnt = 0;
$other_cnt = 0;
$start_tm = time();
while (!feof($this->fp)) {
if ($quit) break;
$data = $this->readln();
// no data ?
if ($data === false) {
if ($this->timeout > 0) {
$now_tm = time();
$used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
if ($used_time > $this->timeout) {
$this->error = 'Timeout, maybe protocol changed!';
$this->debug_message("*** $this->error");
break;
}
}
continue;
}
$code = substr($data, 0, 3);
$start_tm = time();
switch ($code) {
case 'SBS':
$this->writeln("CHG $this->id NLN");
break;
case 'MSG':
@list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
// we don't need the notification data, so just ignore it
if (is_numeric($size) && $size > 0)
$this->readdata($size);
break;
case 'CHL':
@list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
$fingerprint = $this->getChallenge($chl_code);
// NS: >>> QRY {id} {product_id} 32
// NS: >>> fingerprint
$this->writeln("QRY $this->id $this->prod_id 32");
$this->writedata($fingerprint);
break;
case 'SYN':
break;
case 'CHG':
if (count($aTo) == 0 || $sMessage === '') {
$quit = true;
break;
}
$aMSNUsers = array();
$aOfflineUsers = array();
$aOtherUsers = array();
$nMSNUsers = 0;
foreach ($aTo as $sUser) {
@list($u_user, $u_domain, $u_network) = @explode([email='@']'@'[/email], $sUser);
if ($u_network === '' || $u_network == NULL)
$u_network = 1;
$to_email = trim([email=$u_user.'@'.$u_domain]$u_user.'@'.$u_domain[/email]);
if ($u_network == 1)
$aMSNUsers[$nMSNUsers++] = $to_email;
else
$aOtherUsers[$u_network][] = $to_email;
}
if ($nMSNUsers == 0) {
// no MSN account, only others
// process other network first
foreach ($aOtherUsers as $network => $aNetUsers) {
$other_cnt++;
$aMessage = $this->getMessage($sMessage, $network);
foreach ($aNetUsers as $to) {
foreach ($aMessage as $message) {
$len = strlen($message);
$this->writeln("UUM $this->id $to $network 1 $len");
$this->writedata($message);
}
$this->debug_message("*** sent to $to (network: $network):\n$sMessage");
}
}
$quit = true;
break;
}
$nCurrentUser = 0;
// okay, try to ask a switchboard (SB) for sending message
// NS: >>> XFR {id} SB
$this->writeln("XFR $this->id SB");
break;
case 'XFR':
// NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
@list(/* XFR */, /* {id} */, /* SB */, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
@list($ip, $port) = @explode(':', $server);
$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];
}
else
$online_cnt++;
$nCurrentUser++;
if ($nCurrentUser < $nMSNUsers) {
// for next user
// okay, try to ask a switchboard (SB) for sending message
// NS: >>> XFR {id} SB
$this->writeln("XFR $this->id SB");
continue;
}
// okay, process offline user
$lockkey = '';
$re_login = false;
foreach ($aOfflineUsers as $to) {
$offline_cnt++;
for ($i = 0; $i < $this->oim_try; $i++) {
$oim_result = $this->sendOIM($to, $sMessage, $lockkey);
if ($oim_result === true) {
// finished
break;
}
if (is_array($oim_result) && $oim_result['challenge'] !== false) {
// need challenge lockkey
$lockkey = $this->getChallenge($oim_result['challenge']);
}
if ($oim_result === false || $oim_result['auth_policy'] !== false) {
if ($re_login) {
$this->debug_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
break;
}
$this->debug_message("*** can't send OIM, maybe ticket expired, try to login again");
// 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("*** can't re-login, something wrong here, ignore this OIM");
break;
}
$re_login = true;
$this->oim_ticket = $aTickets['oim_ticket'];
$this->contact_ticket = $aTickets['contact_ticket'];
$this->debug_message("**** get new ticket, try it again");
}
}
}
$quit = true;
break;
case 'LST':
// NS: <<< LST {email} {alias} 11 0
@list(/* LST */, $email, $alias, ) = @explode(' ', $data);
$this->friends[$email]=$alias;
break;
default:
if (is_numeric($code)) {
$this->error = "Error code: $code, please check the detail information from: ";
$this->debug_message("*** NS: $this->error");
}
break;
}
}
// logout now
// NS: >>> OUT
$this->writeln("OUT");
fclose($this->fp);
return array(
'online' => $online_cnt,
'offline' => $offline_cnt,
'others' => $other_cnt
);
}