/**
* 基于 版
* 去除大段注释,以及无用代码
* 仅使用发消息及获取好友列表功能
* 2008-12-15 李勇
*/classMSN{//dispatch服务器地址及端口private$server='messenger.hotmail.com';
private$port=1863;
private$passport_url='';
private$prod_key='PK}_A_0N_K%O?A9S';
private$prod_id='PROD0114ES4Z%Q5W';
private$clientid='0x7000800C';
private$id;
private$fp=false;
private$error='';
private$authed=false;
private$user='';
private$password='';
private$passport_policy='';
private$oim_try=3;
private$oim_ticket='';
private$contact_ticket='';
private$debug=true;
private$timeout=15;
private$stream_timeout=2;
private$sb;
private$font_fn='Arial';
private$font_co='333333';
private$font_ef='';
private$max_msn_message_len=1664;
private$max_yahoo_message_len=518;/**
* 构造函数,需要以下PHP扩展的支持curl,prece,mhash,mcrypt,bcmath
*
* @return MSN
*/public functionMSN(){
if (!function_exists('curl_init')) die("We need curl module!\n");
if (!function_exists('preg_match')) die("We need pcre module!\n");
if (!function_exists('mhash'))die("We need mhash module !\n");
if (!function_exists('mcrypt_cbc')) die("We need mcrypt module !\n");
if (!function_exists('bcmod'))die("We need bcmath module !\n");
return;
}/**
* 登录指定MSN账号
*
* @param string $user
* @param string $password
* @return bool
* @uses
* var
* id
* fp
* =>error
* =>authed
* timeout
* =>passport_policy
* =>oim_ticket
* =>contact_ticket
* =>user
* =>password
* function
* readln
* writeln
* readdata
* debug_message
* get_passport_ticket
* generateLoginBLOB
*/public functionconnect($user,$password){$this->id=1;$server='messenger.hotmail.com';$port=1863;$errno=0;$errstr='';$stream_timeout=2;$this->fp= @fsockopen($server,$port,$errno,$errstr,5);
if (!$this->fp) {$this->error="Can't connect to $server:$port, error => $errno, $errstr";
returnfalse;
}stream_set_timeout($this->fp,$stream_timeout);$this->authed=false;$this->writeln("VER $this->id MSNP15 CVR0");$start_tm=time();
while (!feof($this->fp)) {$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) {// logout now
// NS: >>> OUT$this->writeln("OUT");fclose($this->fp);$this->error='Timeout, maybe protocol changed!';$this->debug_message("*** $this->error");
returnfalse;
}
}
continue;
}$code=substr($data,0,3);$start_tm=time();
switch ($code) {
case'VER':$this->writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs $user");
break;
case'CVR':$this->writeln("USR $this->id SSO I $user");
break;
case'USR':
if ($this->authed) returntrue;// max. 16 digits for passwordif (strlen($password) >16)$password=substr($password,0,16);$this->user=$user;$this->password=$password;// NS: <passport_policy=$policy;$aTickets=$this->get_passport_ticket();
if (!$aTickets|| !is_array($aTickets)) {// logout now
// NS: >>> OUT$this->writeln("OUT");fclose($this->fp);$this->error='Passport authenticated fail!';$this->debug_message("*** $this->error");
returnfalse;
}$this->oim_ticket=$aTickets['oim_ticket'];$this->contact_ticket=$aTickets['contact_ticket'];// NS: >>> USR {id} SSO S {ticket} {login_code}$this->writeln("USR $this->id SSO S ".$aTickets['ticket']." ".$this->generateLoginBLOB($aTickets['secret'],$nonce));$this->authed=true;
break;
case'XFR':
@list(/* XFR */,/* id */,/* NS */,$server,/* ... */) = @explode(' ',$data);
@list($ip,$port) = @explode(':',$server);// this connection will close after XFRfclose($this->fp);$errno=0;$errstr='';$this->fp= @fsockopen($ip,$port,$errno,$errstr,5);
if (!$this->fp) {$this->error="Can't connect to $ip:$port, error => $errno, $errstr";$this->debug_message("*** $this->error");
returnfalse;
}stream_set_timeout($this->fp,$this->stream_timeout);$this->writeln("VER $this->id MSNP15 CVR0");
break;
case'GCF':// return some policy data after 'USR {id} SSO I {user}' command
// NS: <0)$this->readdata($size);
break;
default:// we'll quit if got any errorif (is_numeric($code)) {// logout now
// NS: >>> OUT$this->writeln("OUT");fclose($this->fp);$this->error="Error code: $code, please check the detail information from: ";$this->debug_message("*** $this->error");
returnfalse;
}// unknown response from server, just ignore itbreak;
}
}// never goto herereturnfalse;
}/**
* 获取通行证的令牌
*
* @param string $url
* @return string
* @uses
* var
* user<=
* password<=
* passport_url<=
* passport_policy<=
* function
*/private functionget_passport_ticket($url=''){$user=$this->user;$password=htmlspecialchars($this->password);
if ($url==='')$passport_url=$this->passport_url;
else$passport_url=$url;$XML='
xmlns:wsse=""
xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
xmlns:wsp=""
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsa=""
xmlns:wssc=""
xmlns:wst="">
{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}
4
1
AQAAAAIAAABsYwQAAAAxMDMz
'.$user.'
'.$password.'
http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue
http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue
messengerclear.live.com
.$this->passport_policy.'">
http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue
messenger.msn.com
http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue
contacts.msn.com
http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue
messengersecure.live.com
http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue
spaces.live.com
http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue
storage.msn.com
';$curl=curl_init();curl_setopt($curl,CURLOPT_URL,$passport_url);
if ($this->debug)curl_setopt($curl,CURLOPT_HEADER,1);curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);curl_setopt($curl,CURLOPT_FOLLOWLOCATION,1);curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,0);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);
if ($http_code!=200) {// sometimes, redirect to another URL
// MSNP15
//psf:Redirect
Authentication Failureif (strpos($data,'psf:Redirect') ===false) {$this->debug_message("*** Can't get passport ticket! http code = $http_code");
returnfalse;
}preg_match("#(.*)#",$data,$matches);
if (count($matches) ==0) {$this->debug_message("*** redirect, but can't get redirect URL!");
returnfalse;
}$redirect_url=$matches[1];
if ($redirect_url==$passport_url) {$this->debug_message("*** redirect, but redirect to same URL!");
returnfalse;
}$this->debug_message("*** redirect to $redirect_url");
return$this->get_passport_ticket($redirect_url);
}// sometimes, rediret to another URL, also return 200
// MSNP15
//psf:Redirect
Authentication Failureif (strpos($data,'psf:Redirect') !==false) {preg_match("#(.*)#",$data,$matches);
if (count($matches) !=0) {$redirect_url=$matches[1];
if ($redirect_url==$passport_url) {$this->debug_message("*** redirect, but redirect to same URL!");
returnfalse;
}$this->debug_message("*** redirect to $redirect_url");
return$this->get_passport_ticket($redirect_url);
}
}preg_match("#"."(.*)(.*)"."(.*)(.*)"."(.*)(.*)"."(.*)(.*)"."(.*)(.*)"."(.*)(.*)"."(.*)(.*)"."#",$data,$matches);// no ticket found!if (count($matches) ==0) {$this->debug_message("*** Can't get passport ticket!");
returnfalse;
}// yes, we get ticket$aTickets= array('ticket'=>html_entity_decode($matches[1]),'secret'=>html_entity_decode($matches[3]),'contact_ticket'=>html_entity_decode($matches[7]),'oim_ticket'=>html_entity_decode($matches[9]),
);
return$aTickets;
}