4 MSN class ver 2.0 by Tommy Wu, Ricky Su
\r
7 You can find MSN protocol from this site: http://msnpiki.msnfanatic.com/index.php/Main_Page
\r
9 This class support MSNP15 for send message. The PHP module needed:
\r
11 MSNP15: curl pcre mhash mcrypt bcmath
\r
13 Usually, this class will try to use MSNP15 if your system can support it, if your system can't support it,
\r
14 it will switch to use MSNP9. But if you use MSNP9, it won't support OIM (Offline Messages).
\r
20 private $protocol = 'MSNP15';
\r
21 private $passport_url = 'https://login.live.com/RST.srf';
\r
22 private $buildver = '8.1.0178';
\r
23 private $prod_key = 'PK}_A_0N_K%O?A9S';
\r
24 private $prod_id = 'PROD0114ES4Z%Q5W';
\r
25 private $login_method = 'SSO';
\r
26 private $oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';
\r
27 private $oim_send_soap = 'http://messenger.live.com/ws/2006/09/oim/Store2';
\r
31 private $password = '';
\r
32 private $NSfp=false;
\r
33 private $passport_policy = '';
\r
36 private $retry_wait;
\r
37 private $update_pending;
\r
38 private $PhotoStickerFile=false;
\r
39 private $Emotions=false;
\r
40 private $ReqSBXFRTimeout=60;
\r
42 private $ping_wait=50;
\r
43 private $SBIdleTimeout=10;
\r
44 private $SBStreamTimeout=2;
\r
45 private $MsnObjArray=array();
\r
46 private $MsnObjMap=array();
\r
47 private $ABAuthHeader;
\r
50 private $IgnoreList;
\r
52 private $server = 'messenger.hotmail.com';
\r
53 private $port = 1863;
\r
56 public $clientid = '';
\r
58 public $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
59 public $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
\r
60 public $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
61 public $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
\r
62 public $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
63 public $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
\r
65 public $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
66 public $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';
\r
68 public $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
69 public $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';
\r
71 public $addcontact_url = 'https://contacts.msn.com/abservice/abservice.asmx';
\r
72 public $addcontact_soap = 'http://www.msn.com/webservices/AddressBook/ABContactAdd';
\r
74 public $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
75 public $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
\r
80 public $authed = false;
\r
82 public $oim_try = 3;
\r
84 public $log_file = '';
\r
86 public $log_path = false;
\r
88 public $font_fn = 'Arial';
\r
89 public $font_co = '333333';
\r
90 public $font_ef = '';
\r
93 // the message length (include header) is limited (maybe since WLM 8.5 released)
\r
94 // for WLM: 1664 bytes
\r
95 // for YIM: 518 bytes
\r
96 public $max_msn_message_len = 1664;
\r
97 public $max_yahoo_message_len = 518;
\r
99 // Begin added for StatusNet
\r
101 private $aContactList = array();
\r
102 private $aADL = array();
\r
103 private $switchBoardSessions = array();
\r
104 private $switchBoardSockets = array();
\r
105 private $waitingForXFR = array();
\r
108 * Event Handler Functions
\r
110 private $myEventHandlers = array();
\r
112 // End added for StatusNet
\r
115 * Constructor method
\r
117 * @param array $Configs Array of configuration options
\r
118 * 'user' - Username
\r
119 * 'password' - Password
\r
120 * 'alias' - Bot nickname
\r
121 * 'psm' - Bot personal status message
\r
122 * 'retry_wait' - Time to wait before trying to reconnect
\r
123 * 'update_pending' - Whether to update pending contacts
\r
124 * 'PhotoSticker' - Photo file to use (?)
\r
125 * 'debug' - Enable/Disable debugging mode
\r
126 * @param integer $timeout Connection timeout
\r
127 * @param integer $client_id Client id (hexadecimal)
\r
130 public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)
\r
132 $this->user = $Configs['user'];
\r
133 $this->password = $Configs['password'];
\r
134 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
135 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
136 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
137 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
138 $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;
\r
140 if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false) {
\r
141 foreach($this->Emotions as $EmotionFilePath)
\r
142 $this->MsnObj($EmotionFilePath,$Type=2);
\r
144 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
145 $this->timeout = $timeout;
\r
148 if (!function_exists('curl_init')) throw new Exception("curl module not found!\n");
\r
149 if (!function_exists('preg_match')) throw new Exception("pcre module not found!\n");
\r
150 if (!function_exists('mhash')) throw new Exception("mhash module not found!\n");
\r
151 if (!function_exists('mcrypt_cbc')) throw new Exception("mcrypt module not found!\n");
\r
152 if (!function_exists('bcmod')) throw new Exception("bcmath module not found!\n");
\r
155 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
157 normal MSN 8.1 clientid is:
\r
158 01110110 01001100 11000000 00101100
\r
161 we just use following:
\r
162 * 0x04: Your client can send/receive Ink (GIF format)
\r
163 * 0x08: Your client can send/recieve Ink (ISF format)
\r
164 * 0x8000: This means you support Winks receiving (If not set the official Client will warn with 'contact has an older client and is not capable of receiving Winks')
\r
165 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
168 $this->clientid = $client_id;
\r
169 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
172 private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)
\r
175 foreach($Array as $Key => $Val)
\r
177 if($Key{0}==':') continue;
\r
179 if(is_array($Val[':']))
\r
181 foreach($Val[':'] as $AttribName => $AttribVal)
\r
182 $Attrib.=" $AttribName='$AttribVal'";
\r
187 $Key=substr($Key,1);
\r
188 foreach($Val as $ListKey => $ListVal)
\r
190 if($ListKey{0}==':') continue;
\r
191 if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);
\r
192 elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';
\r
193 $ArrayString.="<$Key$Attrib>$ListVal</$Key>";
\r
197 if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);
\r
198 elseif(is_bool($Val)) $Val=$Val?'true':'false';
\r
199 $ArrayString.="<$Key$Attrib>$Val</$Key>";
\r
201 if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);
\r
202 return $ArrayString;
\r
206 * Get Passport ticket
\r
208 * @param string $url URL string (Optional)
\r
209 * @return mixed Array of tickets or false on failure
\r
211 private function get_passport_ticket($url = '')
\r
213 $user = $this->user;
\r
214 $password = htmlspecialchars($this->password);
\r
217 $passport_url = $this->passport_url;
\r
219 $passport_url = $url;
\r
221 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
222 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
223 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
224 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
225 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
226 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
227 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
228 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
229 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
231 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
232 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
233 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
234 <ps:UIVersion>1</ps:UIVersion>
\r
235 <ps:Cookies></ps:Cookies>
\r
236 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
239 <wsse:UsernameToken Id="user">
\r
240 <wsse:Username>'.$user.'</wsse:Username>
\r
241 <wsse:Password>'.$password.'</wsse:Password>
\r
242 </wsse:UsernameToken>
\r
246 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
247 <wst:RequestSecurityToken Id="RST0">
\r
248 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
250 <wsa:EndpointReference>
\r
251 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
252 </wsa:EndpointReference>
\r
254 </wst:RequestSecurityToken>
\r
255 <wst:RequestSecurityToken Id="RST1">
\r
256 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
258 <wsa:EndpointReference>
\r
259 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
260 </wsa:EndpointReference>
\r
262 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
263 </wst:RequestSecurityToken>
\r
264 <wst:RequestSecurityToken Id="RST2">
\r
265 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
267 <wsa:EndpointReference>
\r
268 <wsa:Address>messenger.msn.com</wsa:Address>
\r
269 </wsa:EndpointReference>
\r
271 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
272 </wst:RequestSecurityToken>
\r
273 <wst:RequestSecurityToken Id="RST3">
\r
274 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
276 <wsa:EndpointReference>
\r
277 <wsa:Address>contacts.msn.com</wsa:Address>
\r
278 </wsa:EndpointReference>
\r
280 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
281 </wst:RequestSecurityToken>
\r
282 <wst:RequestSecurityToken Id="RST4">
\r
283 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
285 <wsa:EndpointReference>
\r
286 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
287 </wsa:EndpointReference>
\r
289 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
290 </wst:RequestSecurityToken>
\r
291 <wst:RequestSecurityToken Id="RST5">
\r
292 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
294 <wsa:EndpointReference>
\r
295 <wsa:Address>spaces.live.com</wsa:Address>
\r
296 </wsa:EndpointReference>
\r
298 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
299 </wst:RequestSecurityToken>
\r
300 <wst:RequestSecurityToken Id="RST6">
\r
301 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
303 <wsa:EndpointReference>
\r
304 <wsa:Address>storage.msn.com</wsa:Address>
\r
305 </wsa:EndpointReference>
\r
307 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
308 </wst:RequestSecurityToken>
\r
309 </ps:RequestMultipleSecurityTokens>
\r
313 $this->debug_message("*** URL: $passport_url");
\r
314 $this->debug_message("*** Sending SOAP:\n$XML");
\r
315 $curl = curl_init();
\r
316 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
317 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
318 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
319 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
320 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
321 curl_setopt($curl, CURLOPT_POST, 1);
\r
322 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
323 $data = curl_exec($curl);
\r
324 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
326 $this->debug_message("*** Get Result:\n$data");
\r
328 if ($http_code != 200) {
\r
329 // sometimes, rediret to another URL
\r
331 //<faultcode>psf:Redirect</faultcode>
\r
332 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
333 //<faultstring>Authentication Failure</faultstring>
\r
334 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
335 $this->debug_message("*** Can't get passport ticket! http code = $http_code");
\r
338 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
339 if (count($matches) == 0) {
\r
340 $this->debug_message("*** redirect, but can't get redirect URL!");
\r
343 $redirect_url = $matches[1];
\r
344 if ($redirect_url == $passport_url) {
\r
345 $this->debug_message("*** redirect, but redirect to same URL!");
\r
348 $this->debug_message("*** redirect to $redirect_url");
\r
349 return $this->get_passport_ticket($redirect_url);
\r
352 // sometimes, rediret to another URL, also return 200
\r
354 //<faultcode>psf:Redirect</faultcode>
\r
355 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
356 //<faultstring>Authentication Failure</faultstring>
\r
357 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
358 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
359 if (count($matches) != 0) {
\r
360 $redirect_url = $matches[1];
\r
361 if ($redirect_url == $passport_url) {
\r
362 $this->debug_message("*** redirect, but redirect to same URL!");
\r
365 $this->debug_message("*** redirect to $redirect_url");
\r
366 return $this->get_passport_ticket($redirect_url);
\r
370 // no Redurect faultcode or URL
\r
371 // we should get the ticket here
\r
373 // we need ticket and secret code
\r
374 // RST1: messengerclear.live.com
\r
375 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
376 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
377 // RST2: messenger.msn.com
\r
378 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
379 // RST3: contacts.msn.com
\r
380 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
381 // RST4: messengersecure.live.com
\r
382 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
383 // RST5: spaces.live.com
\r
384 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
385 // RST6: storage.msn.com
\r
386 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
388 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
389 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
390 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
391 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
392 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
393 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
394 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
398 // no ticket found!
\r
399 if (count($matches) == 0) {
\r
400 $this->debug_message("*** Can't get passport ticket!");
\r
404 //$this->debug_message(var_export($matches, true));
\r
405 // matches[0]: all data
\r
406 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
408 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
410 // matches[5]: RST2 (messenger.msn.com) ticket
\r
412 // matches[7]: RST3 (contacts.msn.com) ticket
\r
414 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
415 // matches[10]: ...
\r
416 // matches[11]: RST5 (spaces.live.com) ticket
\r
417 // matches[12]: ...
\r
418 // matches[13]: RST6 (storage.live.com) ticket
\r
419 // matches[14]: ...
\r
422 // ticket => $matches[1]
\r
423 // secret => $matches[3]
\r
424 // web_ticket => $matches[5]
\r
425 // contact_ticket => $matches[7]
\r
426 // oim_ticket => $matches[9]
\r
427 // space_ticket => $matches[11]
\r
428 // storage_ticket => $matches[13]
\r
430 // yes, we get ticket
\r
432 'ticket' => html_entity_decode($matches[1]),
\r
433 'secret' => html_entity_decode($matches[3]),
\r
434 'web_ticket' => html_entity_decode($matches[5]),
\r
435 'contact_ticket' => html_entity_decode($matches[7]),
\r
436 'oim_ticket' => html_entity_decode($matches[9]),
\r
437 'space_ticket' => html_entity_decode($matches[11]),
\r
438 'storage_ticket' => html_entity_decode($matches[13])
\r
440 $this->ticket=$aTickets;
\r
441 $this->debug_message(var_export($aTickets, true));
\r
442 $ABAuthHeaderArray=array(
\r
443 'ABAuthHeader'=>array(
\r
444 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
445 'ManagedGroupRequest'=>false,
\r
446 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),
\r
449 $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));
\r
453 private function UpdateContacts()
\r
455 $ABApplicationHeaderArray=array(
\r
456 'ABApplicationHeader'=>array(
\r
457 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
458 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
459 'IsMigration'=>false,
\r
460 'PartnerScenario'=>'ContactSave'
\r
464 $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
465 $ABFindAllArray=array(
\r
466 'ABFindAll'=>array(
\r
467 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
468 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
470 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',
\r
473 $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');
\r
474 $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));
\r
475 $this->Contacts=array();
\r
478 $this->debug_message("*** Update Contacts...");
\r
479 $Result=$this->ABService->ABFindAll($ABFindAll);
\r
480 $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());
\r
481 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
482 $this->Contacts[$Contact->contactInfo->passportName]=$Contact;
\r
484 catch(Exception $e)
\r
486 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
492 private function addContact($email, $network, $display = '', $sendADL = false)
\r
494 if ($network != 1) return true;
\r
495 if(isset($this->Contacts[$email])) return true;
\r
497 $ABContactAddArray=array(
\r
498 'ABContactAdd'=>array(
\r
499 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
500 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
503 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
504 'contactInfo'=>array(
\r
505 'contactType'=>'LivePending',
\r
506 'passportName'=>$email,
\r
507 'isMessengerUser'=>true,
\r
508 'MessengerMemberInfo'=>array(
\r
509 'DisplayName'=>$email
\r
515 'EnableAllowListManagement'=>true
\r
519 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
522 $this->debug_message("*** Add Contacts $email...");
\r
523 $this->ABService->ABContactAdd($ABContactAdd);
\r
525 catch(Exception $e)
\r
527 $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
530 if ($sendADL && !feof($this->NSfp)) {
\r
531 @list($u_name, $u_domain) = @explode('@', $email);
\r
532 foreach (array('1', '2') as $l) {
\r
533 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
534 $len = strlen($str);
\r
535 // NS: >>> ADL {id} {size}
\r
536 $this->ns_writeln("ADL $this->id $len");
\r
537 $this->ns_writedata($str);
\r
540 $this->UpdateContacts();
\r
544 function delMemberFromList($memberID, $email, $network, $list)
\r
546 if ($network != 1 && $network != 32) return true;
\r
547 if ($memberID === false) return true;
\r
549 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
551 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
552 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
553 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
554 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
555 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
557 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
558 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
559 <IsMigration>false</IsMigration>
\r
560 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
561 </ABApplicationHeader>
\r
562 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
563 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
564 <TicketToken>'.$ticket.'</TicketToken>
\r
568 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
571 <Type>Messenger</Type>
\r
572 <ForeignId></ForeignId>
\r
576 <MemberRole>'.$list.'</MemberRole>
\r
578 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
579 <Type>Passport</Type>
\r
580 <MembershipId>'.$memberID.'</MembershipId>
\r
581 <State>Accepted</State>
\r
590 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
591 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
592 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
593 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
594 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
596 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
597 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
598 <IsMigration>false</IsMigration>
\r
599 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
600 </ABApplicationHeader>
\r
601 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
602 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
603 <TicketToken>'.$ticket.'</TicketToken>
\r
607 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
610 <Type>Messenger</Type>
\r
611 <ForeignId></ForeignId>
\r
615 <MemberRole>'.$list.'</MemberRole>
\r
617 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
619 <MembershipId>'.$memberID.'</MembershipId>
\r
620 <State>Accepted</State>
\r
629 $header_array = array(
\r
630 'SOAPAction: '.$this->delmember_soap,
\r
631 'Content-Type: text/xml; charset=utf-8',
\r
632 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
635 $this->debug_message("*** URL: $this->delmember_url");
\r
636 $this->debug_message("*** Sending SOAP:\n$XML");
\r
637 $curl = curl_init();
\r
638 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
639 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
640 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
641 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
642 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
643 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
644 curl_setopt($curl, CURLOPT_POST, 1);
\r
645 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
646 $data = curl_exec($curl);
\r
647 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
649 $this->debug_message("*** Get Result:\n$data");
\r
651 if ($http_code != 200) {
\r
652 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
653 if (count($matches) == 0) {
\r
654 $this->debug_message("*** can't delete member (network: $network) $email ($memberID) to $list");
\r
657 $faultcode = trim($matches[1]);
\r
658 $faultstring = trim($matches[2]);
\r
659 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
660 $this->debug_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
\r
663 $this->debug_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
\r
666 $this->debug_message("*** delete member (network: $network) $email ($memberID) from $list");
\r
670 function addMemberToList($email, $network, $list)
\r
672 if ($network != 1 && $network != 32) return true;
\r
673 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
677 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
678 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
679 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
680 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
681 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
683 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
684 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
685 <IsMigration>false</IsMigration>
\r
686 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
687 </ABApplicationHeader>
\r
688 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
689 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
690 <TicketToken>'.$ticket.'</TicketToken>
\r
694 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
697 <Type>Messenger</Type>
\r
698 <ForeignId></ForeignId>
\r
702 <MemberRole>'.$list.'</MemberRole>
\r
704 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
705 <Type>Passport</Type>
\r
706 <State>Accepted</State>
\r
707 <PassportName>'.$user.'</PassportName>
\r
716 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
717 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
718 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
719 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
720 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
722 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
723 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
724 <IsMigration>false</IsMigration>
\r
725 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
726 </ABApplicationHeader>
\r
727 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
728 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
729 <TicketToken>'.$ticket.'</TicketToken>
\r
733 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
736 <Type>Messenger</Type>
\r
737 <ForeignId></ForeignId>
\r
741 <MemberRole>'.$list.'</MemberRole>
\r
743 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
745 <State>Accepted</State>
\r
746 <Email>'.$user.'</Email>
\r
749 <Name>MSN.IM.BuddyType</Name>
\r
750 <Value>32:YAHOO</Value>
\r
760 $header_array = array(
\r
761 'SOAPAction: '.$this->addmember_soap,
\r
762 'Content-Type: text/xml; charset=utf-8',
\r
763 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
766 $this->debug_message("*** URL: $this->addmember_url");
\r
767 $this->debug_message("*** Sending SOAP:\n$XML");
\r
768 $curl = curl_init();
\r
769 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
770 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
771 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
772 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
773 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
774 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
775 curl_setopt($curl, CURLOPT_POST, 1);
\r
776 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
777 $data = curl_exec($curl);
\r
778 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
780 $this->debug_message("*** Get Result:\n$data");
\r
782 if ($http_code != 200) {
\r
783 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
784 if (count($matches) == 0) {
\r
785 $this->debug_message("*** can't add member (network: $network) $email to $list");
\r
788 $faultcode = trim($matches[1]);
\r
789 $faultstring = trim($matches[2]);
\r
790 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
791 $this->debug_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
\r
794 $this->debug_message("*** add member (network: $network) $email to $list, already exist!");
\r
797 $this->debug_message("*** add member (network: $network) $email to $list");
\r
801 function getMembershipList($returnData=false)
\r
803 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
804 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
805 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
806 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
807 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
808 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
810 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
811 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
812 <IsMigration>false</IsMigration>
\r
813 <PartnerScenario>Initial</PartnerScenario>
\r
814 </ABApplicationHeader>
\r
815 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
816 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
817 <TicketToken>'.$ticket.'</TicketToken>
\r
821 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
824 <ServiceType>Messenger</ServiceType>
\r
825 <ServiceType>Invitation</ServiceType>
\r
826 <ServiceType>SocialNetwork</ServiceType>
\r
827 <ServiceType>Space</ServiceType>
\r
828 <ServiceType>Profile</ServiceType>
\r
834 $header_array = array(
\r
835 'SOAPAction: '.$this->membership_soap,
\r
836 'Content-Type: text/xml; charset=utf-8',
\r
837 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
839 $this->debug_message("*** URL: $this->membership_url");
\r
840 $this->debug_message("*** Sending SOAP:\n$XML");
\r
841 $curl = curl_init();
\r
842 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
843 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
844 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
845 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
846 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
847 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
848 curl_setopt($curl, CURLOPT_POST, 1);
\r
849 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
850 $data = curl_exec($curl);
\r
851 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
853 $this->debug_message("*** Get Result:\n$data");
\r
854 if($http_code != 200) return false;
\r
856 $aMemberships = array();
\r
858 //$this->debug_message("search p = $p");
\r
859 $start = strpos($p, '<Membership>');
\r
860 $end = strpos($p, '</Membership>');
\r
861 if ($start === false || $end === false || $start > $end) break;
\r
862 //$this->debug_message("start = $start, end = $end");
\r
864 $sMembership = substr($p, $start, $end - $start);
\r
865 $aMemberships[] = $sMembership;
\r
866 //$this->debug_message("add sMembership = $sMembership");
\r
867 $p = substr($p, $end);
\r
869 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
871 $aContactList = array();
\r
872 foreach ($aMemberships as $sMembership) {
\r
873 //$this->debug_message("sMembership = $sMembership");
\r
874 if (isset($matches)) unset($matches);
\r
875 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
876 if (count($matches) == 0) continue;
\r
877 $sMemberRole = $matches[1];
\r
878 //$this->debug_message("MemberRole = $sMemberRole");
\r
879 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
881 if (isset($aMembers)) unset($aMembers);
\r
882 $aMembers = array();
\r
884 //$this->debug_message("search p = $p");
\r
885 $start = strpos($p, '<Member xsi:type="');
\r
886 $end = strpos($p, '</Member>');
\r
887 if ($start === false || $end === false || $start > $end) break;
\r
888 //$this->debug_message("start = $start, end = $end");
\r
890 $sMember = substr($p, $start, $end - $start);
\r
891 $aMembers[] = $sMember;
\r
892 //$this->debug_message("add sMember = $sMember");
\r
893 $p = substr($p, $end);
\r
895 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
896 foreach ($aMembers as $sMember) {
\r
897 //$this->debug_message("sMember = $sMember");
\r
898 if (isset($matches)) unset($matches);
\r
899 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
900 if (count($matches) == 0) continue;
\r
901 $sMemberType = $matches[1];
\r
902 //$this->debug_message("MemberType = $sMemberType");
\r
904 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
905 if (count($matches) == 0) continue;
\r
907 if ($sMemberType == 'PassportMember') {
\r
908 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
910 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
912 else if ($sMemberType == 'EmailMember') {
\r
913 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
914 // Value is 32: or 32:YAHOO
\r
915 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
916 if (count($matches) == 0) continue;
\r
917 if ($matches[1] != 32) continue;
\r
919 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
921 if ($network == -1) continue;
\r
922 if (count($matches) > 0) {
\r
923 $email = $matches[1];
\r
924 @list($u_name, $u_domain) = @explode('@', $email);
\r
925 if ($u_domain == NULL) continue;
\r
926 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
927 $this->debug_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
931 return $aContactList;
\r
935 * Connect to the NS server
\r
936 * @param String $user Username
\r
937 * @param String $password Password
\r
938 * @param String $redirect_server Redirect server
\r
939 * @param Integer $redirect_port Redirect port
\r
940 * @return Boolean Returns true if successful
\r
942 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
944 if ($redirect_server === '') {
\r
945 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
\r
946 if (!$this->NSfp) {
\r
947 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
\r
952 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);
\r
953 if (!$this->NSfp) {
\r
954 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
959 stream_set_timeout($this->NSfp, $this->timeout);
\r
960 $this->authed = false;
\r
962 // NS: >> VER {id} MSNP9 CVR0
\r
964 // NS: >>> VER {id} MSNP15 CVR0
\r
965 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
967 $start_tm = time();
\r
968 while (!self::socketcheck($this->NSfp))
\r
970 $data = $this->ns_readln();
\r
972 if ($data === false) {
\r
975 $this->ns_writeln("OUT");
\r
976 @fclose($this->NSfp);
\r
977 $this->error = 'Timeout, maybe protocol changed!';
\r
978 $this->debug_message("*** $this->error");
\r
982 $code = substr($data, 0, 3);
\r
983 $start_tm = time();
\r
988 // NS: <<< VER {id} MSNP9 CVR0
\r
989 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
991 // NS: <<< VER {id} MSNP15 CVR0
\r
992 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
993 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
998 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
999 // NS: >>> USR {id} TWN I {user}
\r
1001 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
1002 // NS: >>> USR {id} SSO I {user}
\r
1003 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
1007 // already login for passport site, finish the login process now.
\r
1008 // NS: <<< USR {id} OK {user} {verify} 0
\r
1009 if ($this->authed) return true;
\r
1010 // max. 16 digits for password
\r
1011 if (strlen($password) > 16)
\r
1012 $password = substr($password, 0, 16);
\r
1014 $this->user = $user;
\r
1015 $this->password = $password;
\r
1016 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1017 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1019 $this->passport_policy = $policy;
\r
1020 $aTickets = $this->get_passport_ticket();
\r
1021 if (!$aTickets || !is_array($aTickets)) {
\r
1024 $this->ns_writeln("OUT");
\r
1025 @fclose($this->NSfp);
\r
1026 $this->error = 'Passport authenticated fail!';
\r
1027 $this->debug_message("*** $this->error");
\r
1031 $ticket = $aTickets['ticket'];
\r
1032 $secret = $aTickets['secret'];
\r
1033 $this->ticket = $aTickets;
\r
1034 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1036 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1037 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1038 $this->authed = true;
\r
1042 // main login server will redirect to anther NS after USR command
\r
1044 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1046 // NS: <<< XFR {id} NS {server} U D
\r
1047 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1048 if($Type!='NS') break;
\r
1049 @list($ip, $port) = @explode(':', $server);
\r
1050 // this connection will close after XFR
\r
1051 @fclose($this->NSfp);
\r
1053 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
1054 if (!$this->NSfp) {
\r
1055 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1056 $this->debug_message("*** $this->error");
\r
1060 stream_set_timeout($this->NSfp, $this->timeout);
\r
1062 // NS: >> VER {id} MSNP9 CVR0
\r
1064 // NS: >>> VER {id} MSNP15 CVR0
\r
1065 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1069 // return some policy data after 'USR {id} SSO I {user}' command
\r
1070 // NS: <<< GCF 0 {size}
\r
1071 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1072 // we don't need the data, just read it and drop
\r
1073 if (is_numeric($size) && $size > 0)
\r
1074 $this->ns_readdata($size);
\r
1078 // we'll quit if got any error
\r
1079 if (is_numeric($code)) {
\r
1082 $this->ns_writeln("OUT");
\r
1083 @fclose($this->NSfp);
\r
1084 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1085 $this->debug_message("*** $this->error");
\r
1088 // unknown response from server, just ignore it
\r
1092 // never goto here
\r
1096 * Sign onto the NS server and retrieve the address book
\r
1100 public function signon() {
\r
1101 $this->debug_message("*** try to connect to MSN network");
\r
1104 while(!$this->connect($this->user, $this->password)) {
\r
1105 $this->signonFailure("!!! Can't connect to server: $this->error");
\r
1107 if($this->UpdateContacts() === false) {
\r
1108 $this->signonFailure('!!! Update contacts failed');
\r
1111 $this->LastPing=time();
\r
1112 $start_tm = time();
\r
1113 $ping_tm = time();
\r
1114 if(($this->aContactList = $this->getMembershipList()) === false) {
\r
1115 $this->signonFailure('!!! Get membership list failed');
\r
1118 if ($this->update_pending) {
\r
1119 if (is_array($this->aContactList)) {
\r
1120 $pending = 'Pending';
\r
1121 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1122 foreach ($aUserList as $u_name => $aNetworks) {
\r
1123 foreach ($aNetworks as $network => $aData) {
\r
1124 if (isset($aData[$pending])) {
\r
1127 foreach (array('Allow', 'Reverse') as $list) {
\r
1128 if (isset($aData[$list]))
\r
1131 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1132 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1138 $id = $aData[$pending];
\r
1139 // we can delete it from pending now
\r
1140 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1141 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
1146 foreach (array('Allow', 'Reverse') as $list) {
\r
1147 if (!isset($aData[$list])) {
\r
1148 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1149 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1161 if (is_array($this->aContactList)) {
\r
1162 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1163 $str = '<d n="'.$u_domain.'">';
\r
1164 $len += strlen($str);
\r
1165 if ($len > 7400) {
\r
1166 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1169 $len = strlen($str);
\r
1172 foreach ($aUserList as $u_name => $aNetworks) {
\r
1173 foreach ($aNetworks as $network => $status) {
\r
1174 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1175 $len += strlen($str);
\r
1176 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1178 if ($len > 7475) {
\r
1180 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1182 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1183 $len = strlen($sList);
\r
1192 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1193 // NS: >>> BLP {id} BL
\r
1194 $this->ns_writeln("BLP $this->id BL");
\r
1195 foreach ($this->aADL as $str) {
\r
1196 $len = strlen($str);
\r
1197 // NS: >>> ADL {id} {size}
\r
1198 $this->ns_writeln("ADL $this->id $len");
\r
1199 $this->ns_writedata($str);
\r
1201 // NS: >>> PRP {id} MFN name
\r
1202 if ($this->alias == '') $this->alias = $user;
\r
1203 $aliasname = rawurlencode($this->alias);
\r
1204 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1206 //$MsnObj=$this->PhotoStckObj();
\r
1207 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1208 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1209 if($this->PhotoStickerFile!==false)
\r
1210 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1211 // NS: >>> UUX {id} length
\r
1212 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1213 $len = strlen($str);
\r
1214 $this->ns_writeln("UUX $this->id $len");
\r
1215 $this->ns_writedata($str);
\r
1216 if(!socketcheck($this->NSfp)) {
\r
1217 $this->debug_message("*** connected, wait for command");
\r
1220 $this->NSRetryWait($this->retry_wait);
\r
1226 * Called if there is an error during signon
\r
1228 * @param string $message Error message to log
\r
1230 private function signonFailure($message) {
\r
1231 $this->debug_message($message);
\r
1232 $this->callHandler('ConnectFailed');
\r
1233 $this->NSRetryWait($this->retry_wait);
\r
1236 function derive_key($key, $magic) {
\r
1237 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1238 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1239 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1240 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1241 return $hash2.substr($hash4, 0, 4);
\r
1244 function generateLoginBLOB($key, $challenge) {
\r
1245 $key1 = base64_decode($key);
\r
1246 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1247 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1249 // get hash of challenge using key2
\r
1250 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1252 // get 8 bytes random data
\r
1253 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1255 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1257 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1262 return base64_encode($blob);
\r
1265 function getOIM_maildata() {
\r
1266 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1267 if (count($matches) == 0) {
\r
1268 $this->debug_message('*** no web ticket?');
\r
1271 $t = htmlspecialchars($matches[1]);
\r
1272 $p = htmlspecialchars($matches[2]);
\r
1273 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1274 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1275 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1276 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1278 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1284 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1286 </soap:Envelope>';
\r
1288 $header_array = array(
\r
1289 'SOAPAction: '.$this->oim_maildata_soap,
\r
1290 'Content-Type: text/xml; charset=utf-8',
\r
1291 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1294 $this->debug_message("*** URL: $this->oim_maildata_url");
\r
1295 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1296 $curl = curl_init();
\r
1297 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1298 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1299 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1300 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1301 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1302 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1303 curl_setopt($curl, CURLOPT_POST, 1);
\r
1304 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1305 $data = curl_exec($curl);
\r
1306 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1307 curl_close($curl);
\r
1308 $this->debug_message("*** Get Result:\n$data");
\r
1310 if ($http_code != 200) {
\r
1311 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
\r
1315 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1316 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1317 if (count($matches) == 0) {
\r
1318 $this->debug_message("*** Can't get OIM maildata");
\r
1321 return $matches[2];
\r
1324 function getOIM_message($msgid) {
\r
1325 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1326 if (count($matches) == 0) {
\r
1327 $this->debug_message('*** no web ticket?');
\r
1330 $t = htmlspecialchars($matches[1]);
\r
1331 $p = htmlspecialchars($matches[2]);
\r
1334 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1335 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1336 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1337 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1339 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1345 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1346 <messageId>'.$msgid.'</messageId>
\r
1347 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1350 </soap:Envelope>';
\r
1352 $header_array = array(
\r
1353 'SOAPAction: '.$this->oim_read_soap,
\r
1354 'Content-Type: text/xml; charset=utf-8',
\r
1355 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1358 $this->debug_message("*** URL: $this->oim_read_url");
\r
1359 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1360 $curl = curl_init();
\r
1361 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1362 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1363 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1364 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1365 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1366 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1367 curl_setopt($curl, CURLOPT_POST, 1);
\r
1368 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1369 $data = curl_exec($curl);
\r
1370 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1371 curl_close($curl);
\r
1372 $this->debug_message("*** Get Result:\n$data");
\r
1374 if ($http_code != 200) {
\r
1375 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1379 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1381 $start = strpos($data, '<GetMessageResult>');
\r
1382 $end = strpos($data, '</GetMessageResult>');
\r
1383 if ($start === false || $end === false || $start > $end) {
\r
1384 $this->debug_message("*** Can't get OIM: $msgid");
\r
1387 $lines = substr($data, $start + 18, $end - $start);
\r
1388 $aLines = @explode("\n", $lines);
\r
1392 foreach ($aLines as $line) {
\r
1393 $line = rtrim($line);
\r
1395 if ($line === '') {
\r
1401 // stop at empty lines
\r
1402 if ($line === '') break;
\r
1405 $sMsg = base64_decode($sOIM);
\r
1406 $this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1409 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1410 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1411 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1412 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1414 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1420 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1422 <messageId>'.$msgid.'</messageId>
\r
1426 </soap:Envelope>';
\r
1428 $header_array = array(
\r
1429 'SOAPAction: '.$this->oim_del_soap,
\r
1430 'Content-Type: text/xml; charset=utf-8',
\r
1431 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1434 $this->debug_message("*** URL: $this->oim_del_url");
\r
1435 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1436 $curl = curl_init();
\r
1437 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1438 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1439 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1440 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1441 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1442 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1443 curl_setopt($curl, CURLOPT_POST, 1);
\r
1444 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1445 $data = curl_exec($curl);
\r
1446 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1447 curl_close($curl);
\r
1448 $this->debug_message("*** Get Result:\n$data");
\r
1450 if ($http_code != 200)
\r
1451 $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
\r
1453 $this->debug_message("*** OIM ($msgid) deleted");
\r
1457 private function NSLogout() {
\r
1458 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1461 $this->ns_writeln("OUT");
\r
1462 fclose($this->NSfp);
\r
1463 $this->NSfp = false;
\r
1464 $this->debug_message("*** logout now!");
\r
1469 private function NSRetryWait($wait) {
\r
1470 $this->debug_message("*** wait for $Wait seconds");
\r
1474 function getChallenge($code)
\r
1477 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
1478 // Step 1: The MD5 Hash
\r
1479 $md5Hash = md5($code.$this->prod_key);
\r
1480 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
1481 for ($i = 0; $i < 4; $i++) {
\r
1482 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
1483 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
1486 // Step 2: A new string
\r
1487 $chl_id = $code.$this->prod_id;
\r
1488 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
1490 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
1491 for ($i = 0; $i < count($aID); $i++) {
\r
1492 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
1493 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
1496 // Step 3: The 64 bit key
\r
1497 $magic_num = 0x0E79A9C1;
\r
1498 $str7f = 0x7FFFFFFF;
\r
1501 for ($i = 0; $i < count($aID); $i += 2) {
\r
1503 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
1504 $temp = bcadd($temp, $high);
\r
1505 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
1506 $temp = bcmod($temp, $str7f);
\r
1508 $high = $aID[$i+1];
\r
1509 $high = bcmod(bcadd($high, $temp), $str7f);
\r
1510 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
1511 $high = bcmod($high, $str7f);
\r
1513 $low = bcadd(bcadd($low, $high), $temp);
\r
1516 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
1517 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
1519 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
1520 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
1521 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
1522 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
1523 // we need integer here
\r
1524 $high = 0+$new_high;
\r
1526 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
1527 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
1528 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
1529 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
1530 // we need integer here
\r
1531 $low = 0+$new_low;
\r
1533 // we just use 32 bits integer, don't need the key, just high/low
\r
1534 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
1536 // Step 4: Using the key
\r
1537 $md5Hash = md5($code.$this->prod_key);
\r
1538 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
1541 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
1542 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
1543 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
1544 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
1549 private function getMessage($sMessage, $network = 1)
\r
1551 $msg_header = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nX-MMS-IM-Format: FN=$this->font_fn; EF=$this->font_ef; CO=$this->font_co; CS=0; PF=22\r\n\r\n";
\r
1552 $msg_header_len = strlen($msg_header);
\r
1553 if ($network == 1)
\r
1554 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
1556 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
1557 $sMessage=str_replace("\r", '', $sMessage);
\r
1558 $msg=substr($sMessage,0,$maxlen);
\r
1559 return $msg_header.$msg;
\r
1563 * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息
\r
1567 private function DoSwitchBoard($Action,$Param)
\r
1569 $SessionEnd=false;
\r
1572 $LastActive=time();
\r
1573 stream_set_timeout($this->SBfp, $this->SBStreamTimeout);
\r
1577 $cki_code=$Param['cki'];
\r
1578 $user=$Param['user'];
\r
1579 $this->SwitchBoardMessageQueue=$Param['Msg'];
\r
1580 // SB: >>> USR {id} {user} {cki}
\r
1581 $this->SB_writeln("USR $this->id $this->user $cki_code");
\r
1582 $this->SwitchBoardSessionUser=$user;
\r
1585 $ticket=$Param['ticket'];
\r
1586 $sid=$Param['sid'];
\r
1587 $user=$Param['user'];
\r
1588 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
1589 $this->SB_writeln("ANS $this->id $this->user $ticket $sid");
\r
1590 $this->SwitchBoardSessionUser=$user;
\r
1595 while((!feof($this->SBfp))&&(!$SessionEnd))
\r
1597 $data = $this->SB_readln();
\r
1598 if($this->kill_me)
\r
1600 $this->debug_message("*** SB Okay, kill me now!");
\r
1603 if($data === false)
\r
1605 if(time()-$LastActive > $this->SBIdleTimeout)
\r
1607 $this->debug_message("*** SB Idle Timeout!");
\r
1610 if(!$Joined) continue;
\r
1611 foreach($this->SwitchBoardMessageQueue as $Message)
\r
1613 if($Message=='') continue;
\r
1614 $aMessage = $this->getMessage($Message);
\r
1616 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
1617 if($MsnObjDefine!=='')
\r
1619 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
1620 $len = strlen($SendString);
\r
1621 $this->SB_writeln("MSG $this->id N $len");
\r
1622 $this->SB_writedata($SendString);
\r
1625 $len = strlen($aMessage);
\r
1626 $this->SB_writeln("MSG $this->id N $len");
\r
1627 $this->SB_writedata($aMessage);
\r
1629 $this->SwitchBoardMessageQueue=array();
\r
1630 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
1633 $code = substr($data, 0, 3);
\r
1637 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
1638 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
1639 $this->debug_message("*** $email join us");
\r
1643 $this->debug_message("*** Quit for BYE");
\r
1647 // SB: <<< USR {id} OK {user} {alias}
\r
1648 // we don't need the data, just ignore it
\r
1649 // request user to join this switchboard
\r
1650 // SB: >>> CAL {id} {user}
\r
1651 $this->SB_writeln("CAL $this->id $user");
\r
1654 // SB: <<< CAL {id} RINGING {?}
\r
1655 // we don't need this, just ignore, and wait for other response
\r
1659 // SB: <<< JOI {user} {alias} {clientid?}
\r
1660 // someone join us
\r
1661 // we don't need the data, just ignore it
\r
1662 // no more user here
\r
1666 // SB: <<< MSG {email} {alias} {len}
\r
1667 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
1668 $len = trim($len);
\r
1669 $data = $this->SB_readdata($len);
\r
1670 $aLines = @explode("\n", $data);
\r
1675 foreach ($aLines as $line)
\r
1677 $line = rtrim($line);
\r
1679 if ($line === '') {
\r
1683 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
1684 // typing notification, just ignore
\r
1688 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
1689 // we don't handle any split message, just ignore
\r
1693 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
1694 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
1696 $p = strstr($data, "\n\n");
\r
1698 if ($p === false) {
\r
1699 $p = strstr($data, "\r\n\r\n");
\r
1701 $sMsg = substr($p, 4);
\r
1704 $sMsg = substr($p, 2);
\r
1707 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
1708 // ignore all application/x-... message
\r
1710 // application/x-ms-ink => ink message
\r
1714 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
1715 // ignore all text/x-... message
\r
1717 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
1718 // text/x-mms-animemoticon => customized animemotion word
\r
1730 $this->debug_message("*** ingnore from $from_email: $line");
\r
1735 // we will ignore any p2p message after sending acknowledgement
\r
1737 $len = strlen($sMsg);
\r
1738 $this->debug_message("*** p2p message from $from_email, size $len");
\r
1739 // header = 48 bytes
\r
1740 // content >= 0 bytes
\r
1741 // footer = 4 bytes
\r
1742 // so it need to >= 52 bytes
\r
1743 /*if ($len < 52) {
\r
1744 $this->debug_message("*** p2p: size error, less than 52!");
\r
1747 $aDwords = @unpack("V12dword", $sMsg);
\r
1748 if (!is_array($aDwords)) {
\r
1749 $this->debug_message("*** p2p: header unpack error!");
\r
1752 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
1753 $hdr_SessionID = $aDwords['dword1'];
\r
1754 $hdr_Identifier = $aDwords['dword2'];
\r
1755 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
1756 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
1757 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
1758 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
1759 $hdr_MessageLength = $aDwords['dword7'];
\r
1760 $hdr_Flag = $aDwords['dword8'];
\r
1761 $hdr_AckID = $aDwords['dword9'];
\r
1762 $hdr_AckUID = $aDwords['dword10'];
\r
1763 $hdr_AckSizeLow = $aDwords['dword11'];
\r
1764 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
1765 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
1766 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
1767 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
1768 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
1769 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
1770 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
1771 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
1772 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
1773 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
1774 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
1775 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
1776 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
1777 if($hdr_Flag==2) {
\r
1778 //This is an ACK from SB ignore....
\r
1779 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
1782 $MsgBody=$this->linetoArray(substr($sMsg,48,-4));
\r
1783 $this->debug_message("*** p2p: body".print_r($MsgBody,true));
\r
1784 if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))
\r
1788 if($this->SB_readln()===false) break;
\r
1790 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));
\r
1791 preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);
\r
1792 $BranchGUID=$Matches[1];
\r
1793 //it's an invite to send a display picture.
\r
1794 $new_id = ~$hdr_Identifier;
\r
1795 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
1798 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1803 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
1804 $footer = pack("L", 0);
\r
1805 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
1806 $len = strlen($message);
\r
1807 $this->SB_writeln("MSG $this->id D $len");
\r
1808 $this->SB_writedata($message);
\r
1809 $this->debug_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
1810 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
1811 $this->SB_readln();//Read ACK;
\r
1812 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
1814 //Send 200 OK message
\r
1815 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
1817 "MSNSLP/1.0 200 OK\r\n".
\r
1818 "To: <msnmsgr:".$from_email.">\r\n".
\r
1819 "From: <msnmsgr:".$this->user.">\r\n".
\r
1820 "Via: ".$MsgBody['Via']."\r\n".
\r
1821 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
1822 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
1823 "Max-Forwards: 0\r\n".
\r
1824 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
1825 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
1827 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
1828 $hdr_TotalDataSizeHigh=0;
\r
1829 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
1832 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1833 strlen($MessagePayload),
\r
1840 "MIME-Version: 1.0\r\n".
\r
1841 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1842 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
1843 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
1844 $this->SB_writedata($message);
\r
1845 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
1846 $this->SB_readln();//Read ACK;
\r
1848 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
1849 //send Data preparation message
\r
1850 //send 4 null bytes as data
\r
1851 $hdr_TotalDataSizeLow=4;
\r
1852 $hdr_TotalDataSizeHigh=0;
\r
1854 $hdr = pack("LLLLLLLLLLLL",
\r
1855 $MsgBody['SessionID'],
\r
1858 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1859 $hdr_TotalDataSizeLow,
\r
1865 "MIME-Version: 1.0\r\n".
\r
1866 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1867 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";
\r
1868 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
1869 $this->SB_writedata($message);
\r
1870 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
1871 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
1872 $this->SB_readln();//Read ACK;
\r
1874 //send Data Content..
\r
1875 $footer=pack('N',1);
\r
1877 $FileSize=filesize($PictureFilePath);
\r
1878 if($hTitle=fopen($PictureFilePath,'rb'))
\r
1882 while(!feof($hTitle))
\r
1884 $FileContent=fread($hTitle,1024);
\r
1885 $FileContentSize=strlen($FileContent);
\r
1886 $hdr = pack("LLLLLLLLLLLL",
\r
1887 $MsgBody['SessionID'],
\r
1898 "MIME-Version: 1.0\r\n".
\r
1899 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1900 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
1901 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
1902 $this->SB_writedata($message);
\r
1903 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
1904 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
1905 //$this->SB_readln();//Read ACK;
\r
1906 $Offset+=$FileContentSize;
\r
1911 $MessageContent="\r\n".pack("C", 0);
\r
1913 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
1914 "To: <msnmsgr:$from_email>\r\n".
\r
1915 "From: <msnmsgr:".$this->user.">\r\n".
\r
1916 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
1918 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
1919 "Max-Forwards: 0\r\n".
\r
1920 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
1921 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
1922 $footer=pack('N',0);
\r
1923 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
1924 $hdr_TotalDataSizeHigh=0;
\r
1926 $hdr = pack("LLLLLLLLLLLL",
\r
1930 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1937 "MIME-Version: 1.0\r\n".
\r
1938 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1939 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
1940 $this->SB_writeln("MSG $id D ".strlen($message));
\r
1942 $this->SB_writedata($message);
\r
1943 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
1948 //if ($hdr_Flag == 2) {
\r
1949 // just send ACK...
\r
1950 // $this->SB_writeln("ACK $id");
\r
1953 if ($hdr_SessionID == 4) {
\r
1955 $this->debug_message("*** p2p: ignore flag 4");
\r
1958 $finished = false;
\r
1959 if ($hdr_TotalDataSizeHigh == 0) {
\r
1960 // only 32 bites size
\r
1961 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
1965 // we won't accept any file transfer
\r
1966 // so I think we won't get any message size need to use 64 bits
\r
1967 // 64 bits size here, can't count directly...
\r
1968 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
1969 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
1970 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
1971 $now_size = bcadd($dataoffset, $messagelength);
\r
1972 if (bccomp($now_size, $totalsize) >= 0)
\r
1976 // ignore not finished split packet
\r
1977 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
1980 //$new_id = ~$hdr_Identifier;
\r
1983 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
1986 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1991 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
1992 $footer = pack("L", 0);
\r
1993 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
1994 $len = strlen($message);
\r
1995 $this->SB_writeln("MSG $id D $len");
\r
1997 $this->SB_writedata($message);
\r
1998 $this->debug_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
1999 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2003 $this->debug_message("*** MSG from $from_email: $sMsg");
\r
2004 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2007 $this->debug_message("*** User $user is offline. Try OIM.");
\r
2008 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2009 $this->SendMessage($Message,"$user@Offline");
\r
2013 if (is_numeric($code))
\r
2015 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2016 $this->debug_message("*** SB: $this->error");
\r
2021 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2023 if (feof($this->SBfp))
\r
2025 // lost connection? error? try OIM later
\r
2026 @fclose($this->SBfp);
\r
2029 $this->SB_writeln("OUT");
\r
2030 @fclose($this->SBfp);
\r
2033 /*private function switchboard_control($ip, $port, $cki_code, $user, $Messages)
\r
2035 $this->SwitchBoardProcess=1;
\r
2036 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2037 $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2040 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2043 return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));
\r
2045 private function switchboard_ring($ip, $port, $sid, $ticket,$user)
\r
2047 $this->SwitchBoardProcess=2;
\r
2048 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2049 $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2052 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2055 return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));
\r
2058 // read data for specified size
\r
2059 private function ns_readdata($size) {
\r
2062 while (!feof($this->NSfp)) {
\r
2063 $buf = @fread($this->NSfp, $size - $count);
\r
2065 $count += strlen($buf);
\r
2066 if ($count >= $size) break;
\r
2068 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2073 private function ns_readln() {
\r
2074 $data = @fgets($this->NSfp, 4096);
\r
2075 if ($data !== false) {
\r
2076 $data = trim($data);
\r
2077 $this->debug_message("NS: <<< $data");
\r
2082 // write to server, append \r\n, also increase id
\r
2083 private function ns_writeln($data) {
\r
2084 @fwrite($this->NSfp, $data."\r\n");
\r
2085 $this->debug_message("NS: >>> $data");
\r
2090 // write data to server
\r
2091 private function ns_writedata($data) {
\r
2092 @fwrite($this->NSfp, $data);
\r
2093 $this->debug_message("NS: >>> $data");
\r
2097 // read data for specified size for SB
\r
2098 private function sb_readdata($socket, $size) {
\r
2101 while (!feof($this->SBfp)) {
\r
2102 $buf = @fread($this->SBfp, $size - $count);
\r
2104 $count += strlen($buf);
\r
2105 if ($count >= $size) break;
\r
2107 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2111 // read one line for SB
\r
2112 private function sb_readln($socket) {
\r
2113 $data = @fgets($socket, 4096);
\r
2114 if ($data !== false) {
\r
2115 $data = trim($data);
\r
2116 $this->debug_message("SB: <<< $data");
\r
2121 // write to server for SB, append \r\n, also increase id
\r
2122 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
2123 private function sb_writeln($socket, &$id, $data) {
\r
2124 @fwrite($socket, $data."\r\n");
\r
2125 $this->debug_message("SB: >>> $data");
\r
2130 // write data to server
\r
2131 private function sb_writedata($socket, $data) {
\r
2132 @fwrite($socket, $data);
\r
2133 $this->debug_message("SB: >>> $data");
\r
2137 // show debug information
\r
2138 function debug_message($str) {
\r
2139 if (!$this->debug) return;
\r
2140 if($this->debug===STDOUT) echo $str."\n";
\r
2141 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
2142 $fp = fopen($fname, 'at');
\r
2144 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2148 // still show debug information, if we can't open log_file
\r
2153 function dump_binary($str) {
\r
2157 $len = strlen($str);
\r
2158 for ($i = 0; $i < $len; $i++) {
\r
2159 if (($i % 16) == 0) {
\r
2160 if ($buf !== '') {
\r
2161 $buf .= "$h_str $a_str\n";
\r
2163 $buf .= sprintf("%04X:", $i);
\r
2167 $ch = ord($str[$i]);
\r
2171 $a_str .= chr($ch);
\r
2172 $h_str .= sprintf(" %02X", $ch);
\r
2174 if ($h_str !== '')
\r
2175 $buf .= "$h_str $a_str\n";
\r
2181 * @param $FilePath 圖檔路徑
\r
2182 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
2185 private function MsnObj($FilePath,$Type=3)
\r
2187 if(!($FileSize=filesize($FilePath))) return '';
\r
2188 $Location=md5($FilePath);
\r
2189 $Friendly=md5($FilePath.$Type);
\r
2190 if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
2191 $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));
\r
2192 $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
2193 $this->MsnObjArray[$Location]=$FilePath;
\r
2194 $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
2195 $this->MsnObjMap[$Location]=$MsnObj;
\r
2196 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
2200 private function linetoArray($lines) {
\r
2201 $lines=str_replace("\r",'',$lines);
\r
2202 $lines=explode("\n",$lines);
\r
2203 foreach($lines as $line) {
\r
2204 if(!isset($line{3})) continue;
\r
2205 list($Key,$Val)=explode(':',$line);
\r
2206 $Data[trim($Key)]=trim($Val);
\r
2211 private function GetPictureFilePath($Context)
\r
2213 $MsnObj=base64_decode($Context);
\r
2214 if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))
\r
2215 $location=$Match[1];
\r
2216 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
2217 if($location&&(isset($this->MsnObjArray[$location])))
\r
2218 return $this->MsnObjArray[$location];
\r
2222 private function GetMsnObjDefine($Message)
\r
2225 if(is_array($this->Emotions))
\r
2226 foreach($this->Emotions as $Pattern => $FilePath)
\r
2228 if(strpos($Message,$Pattern)!==false)
\r
2229 $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";
\r
2231 return $DefineString;
\r
2235 * Read and handle incoming command from NS
\r
2237 private function nsReceive() {
\r
2238 // Sign in again if not signed in or socket failed
\r
2239 if (!is_resource($this->NSfp) || self::socketcheck($this->NSfp)) {
\r
2240 $this->callHandler('Reconnect');
\r
2241 $this->NSRetryWait($this->retry_wait);
\r
2246 $data = $this->ns_readln();
\r
2247 if($data === false) {
\r
2248 // There was no data / an error when reading from the socket so reconnect
\r
2249 $this->callHandler('Reconnect');
\r
2250 $this->NSRetryWait($this->retry_wait);
\r
2254 switch (substr($data,0,3))
\r
2257 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
2258 // NS: <<< SBS 0 null
\r
2263 // NS: <<< RFS ???
\r
2264 // refresh ADL, so we re-send it again
\r
2265 if (is_array($this->aADL)) {
\r
2266 foreach ($this->aADL as $str) {
\r
2267 $len = strlen($str);
\r
2268 // NS: >>> ADL {id} {size}
\r
2269 $this->ns_writeln("ADL $this->id $len");
\r
2270 $this->ns_writedata($str);
\r
2276 // NS: <<< LST {email} {alias} 11 0
\r
2277 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
2278 @list($u_name, $u_domain) = @explode('@', $email);
\r
2279 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
2280 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
2281 $this->debug_message("*** add to our contact list: $u_name@$u_domain");
\r
2286 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
2287 // NS: <<< ADL 0 {size}
\r
2288 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
2289 if (is_numeric($size) && $size > 0)
\r
2291 $data = $this->ns_readdata($size);
\r
2292 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
2293 if (is_array($matches) && count($matches) > 0)
\r
2295 $u_domain = $matches[1];
\r
2296 $u_name = $matches[2];
\r
2297 $network = $matches[4];
\r
2298 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
2299 $this->debug_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
2301 $re_login = false;
\r
2303 foreach (array('Allow', 'Reverse') as $list)
\r
2305 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
2308 $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
2311 $aTickets = $this->get_passport_ticket();
\r
2312 if (!$aTickets || !is_array($aTickets)) {
\r
2313 // failed to login? ignore it
\r
2314 $this->debug_message("*** can't re-login, something wrong here");
\r
2315 $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
2319 $this->ticket = $aTickets;
\r
2320 $this->debug_message("**** get new ticket, try it again");
\r
2321 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
2323 $this->debug_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
2327 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
2330 $this->debug_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
2332 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
2333 $len = strlen($str);
\r
2336 $this->debug_message("*** someone add us to their list: $data");
\r
2337 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
2342 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
2343 // NS: <<< RML 0 {size}
\r
2344 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
2345 if (is_numeric($size) && $size > 0)
\r
2347 $data = $this->ns_readdata($size);
\r
2348 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
2349 if (is_array($matches) && count($matches) > 0)
\r
2351 $u_domain = $matches[1];
\r
2352 $u_name = $matches[2];
\r
2353 $network = $matches[4];
\r
2354 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
2356 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
2357 foreach ($aData as $list => $id)
\r
2358 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
2359 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
2360 $this->debug_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
2363 $this->debug_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
2364 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
2367 $this->debug_message("*** someone remove us from their list: $data");
\r
2372 // randomly, we get MSG notification from server
\r
2373 // NS: <<< MSG Hotmail Hotmail {size}
\r
2374 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
2375 if (is_numeric($size) && $size > 0) {
\r
2376 $data = $this->ns_readdata($size);
\r
2377 $aLines = @explode("\n", $data);
\r
2381 foreach ($aLines as $line) {
\r
2382 $line = rtrim($line);
\r
2384 if ($line === '') {
\r
2388 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
2389 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
2390 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
2391 // we just need text/x-msmsgsinitialmdatanotification
\r
2392 // or text/x-msmsgsoimnotification
\r
2399 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
2400 $maildata = trim(substr($line, 10));
\r
2405 $this->debug_message("*** ingnore MSG for: $line");
\r
2408 if ($maildata == '') {
\r
2409 $this->debug_message("*** ingnore MSG not for OIM");
\r
2412 $re_login = false;
\r
2413 if (strcasecmp($maildata, 'too-large') == 0) {
\r
2414 $this->debug_message("*** large mail-data, need to get the data via SOAP");
\r
2415 $maildata = $this->getOIM_maildata();
\r
2416 if ($maildata === false) {
\r
2417 $this->debug_message("*** can't get mail-data via SOAP");
\r
2418 // maybe we need to re-login again
\r
2419 $aTickets = $this->get_passport_ticket();
\r
2420 if (!$aTickets || !is_array($aTickets)) {
\r
2421 // failed to login? ignore it
\r
2422 $this->debug_message("*** can't re-login, something wrong here, ignore this OIM");
\r
2426 $this->ticket = $aTickets;
\r
2427 $this->debug_message("*** get new ticket, try it again");
\r
2428 $maildata = $this->getOIM_maildata();
\r
2429 if ($maildata === false) {
\r
2430 $this->debug_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
2435 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
2439 $start = strpos($p, '<M>');
\r
2440 $end = strpos($p, '</M>');
\r
2441 if ($start === false || $end === false || $start > $end) break;
\r
2443 $sOIM = substr($p, $start, $end - $start);
\r
2445 $p = substr($p, $end);
\r
2447 if (count($aOIMs) == 0) {
\r
2448 $this->debug_message("*** ingnore empty OIM");
\r
2451 foreach ($aOIMs as $maildata) {
\r
2452 // T: 11 for MSN, 13 for Yahoo
\r
2453 // S: 6 for MSN, 7 for Yahoo
\r
2454 // RT: the datetime received by server
\r
2455 // RS: already read or not
\r
2456 // SZ: size of message
\r
2459 // F: always 00000000-0000-0000-0000-000000000009
\r
2460 // N: sender alias
\r
2461 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
2462 if (count($matches) == 0) {
\r
2463 $this->debug_message("*** ingnore OIM maildata without <T>type</T>");
\r
2466 $oim_type = $matches[1];
\r
2467 if ($oim_type = 13)
\r
2471 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
2472 if (count($matches) == 0) {
\r
2473 $this->debug_message("*** ingnore OIM maildata without <E>sender</E>");
\r
2476 $oim_sender = $matches[1];
\r
2477 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
2478 if (count($matches) == 0) {
\r
2479 $this->debug_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
2482 $oim_msgid = $matches[1];
\r
2483 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
2484 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
2485 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
2486 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
2487 $this->debug_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
2488 $sMsg = $this->getOIM_message($oim_msgid);
\r
2489 if ($sMsg === false) {
\r
2490 $this->debug_message("*** can't get OIM, msgid = $oim_msgid");
\r
2492 $this->debug_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
2495 $aTickets = $this->get_passport_ticket();
\r
2496 if (!$aTickets || !is_array($aTickets)) {
\r
2497 // failed to login? ignore it
\r
2498 $this->debug_message("*** can't re-login, something wrong here, ignore this OIM");
\r
2502 $this->ticket = $aTickets;
\r
2503 $this->debug_message("*** get new ticket, try it again");
\r
2504 $sMsg = $this->getOIM_message($oim_msgid);
\r
2505 if ($sMsg === false) {
\r
2506 $this->debug_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
2510 $this->debug_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
2512 //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
2513 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
2519 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
2520 // NS: <<< UBM {email} $network $type {size}
\r
2521 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
2522 if (is_numeric($size) && $size > 0)
\r
2524 $data = $this->ns_readdata($size);
\r
2525 $aLines = @explode("\n", $data);
\r
2529 foreach ($aLines as $line) {
\r
2530 $line = rtrim($line);
\r
2532 if ($line === '') {
\r
2536 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2542 $aSubLines = @explode("\r", $line);
\r
2543 foreach ($aSubLines as $str) {
\r
2551 $this->debug_message("*** ingnore from $from_email: $line");
\r
2554 $this->debug_message("*** MSG from $from_email (network: $network): $sMsg");
\r
2555 //$this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2556 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
2561 // randomly, we get UBX notification from server
\r
2562 // NS: <<< UBX email {network} {size}
\r
2563 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
2564 // we don't need the notification data, so just ignore it
\r
2565 if (is_numeric($size) && $size > 0)
\r
2566 $this->ns_readdata($size);
\r
2570 // randomly, we'll get challenge from server
\r
2571 // NS: <<< CHL 0 {code}
\r
2572 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
2573 $fingerprint = $this->getChallenge($chl_code);
\r
2574 // NS: >>> QRY {id} {product_id} 32
\r
2575 // NS: >>> fingerprint
\r
2576 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
2577 $this->ns_writedata($fingerprint);
\r
2578 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2579 if($this->PhotoStickerFile!==false)
\r
2580 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2583 // NS: <<< CHG {id} {status} {code}
\r
2585 // change our status to online first
\r
2589 // sometimes, NS will redirect to another NS
\r
2591 // NS: <<< XFR {id} NS {server} 0 {server}
\r
2593 // NS: <<< XFR {id} NS {server} U D
\r
2594 // for normal switchboard XFR
\r
2595 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
2596 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
2597 @list($ip, $port) = @explode(':', $server);
\r
2598 if ($server_type != 'SB') {
\r
2600 // this connection will close after XFR
\r
2601 $this->NSLogout();
\r
2604 if(count($this->MessageQueue))
\r
2606 foreach($this->MessageQueue as $User => $Message)
\r
2608 //$this->ChildProcess[$ChildPid]
\r
2609 $this->debug_message("*** XFR SB $User");
\r
2610 $pid=pcntl_fork();
\r
2614 $this->ChildProcess[$pid]=$User;
\r
2619 $this->debug_message("*** Fork Error $User");
\r
2625 $this->debug_message("*** Child Process Start for $User");
\r
2626 unset($Message['XFRSent']);
\r
2627 unset($Message['ReqTime']);
\r
2628 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
2629 if ($bSBresult === false)
\r
2631 // error for switchboard
\r
2632 $this->debug_message("!!! error for sending message to ".$User);
\r
2637 unset($this->MessageQueue[$User]);
\r
2640 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
2641 if ($bSBresult === false) {
\r
2642 // error for switchboard
\r
2643 $this->debug_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
2644 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
2648 // NS: <<< QNG {time}
\r
2649 @list(/* QNG */, $ping_wait) = @explode(' ', $data);
\r
2650 $this->callHandler('Pong', $ping_wait);
\r
2654 if($this->PhotoStickerFile!==false)
\r
2655 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2657 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2658 // someone is trying to talk to us
\r
2659 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2660 $this->debug_message("NS: <<< RNG $data");
\r
2661 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2662 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2663 if($this->IsIgnoreMail($email))
\r
2665 $this->debug_message("*** Ignore RNG from $email");
\r
2668 $this->debug_message("*** RING from $email, $sb_ip:$sb_port");
\r
2669 $this->addContact($email,1,$email, true);
\r
2670 $pid=pcntl_fork();
\r
2674 $this->ChildProcess[$pid]='RNG';
\r
2679 $this->debug_message("*** Fork Error $User");
\r
2685 $this->debug_message("*** Ring Child Process Start for $User");
\r
2686 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
2691 // force logout from NS
\r
2692 // NS: <<< OUT xxx
\r
2693 $this->debug_message("*** LOGOUT from NS");
\r
2694 return $this->NsLogout();
\r
2697 $code = substr($data,0,3);
\r
2698 if (is_numeric($code)) {
\r
2699 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2700 $this->debug_message("*** NS: $this->error");
\r
2702 return $this->NsLogout();
\r
2710 * Read and handle incoming command/message from
\r
2711 * a switchboard session socket
\r
2713 private function sbReceive() {
\r
2718 * Checks for new data and calls appropriate methods
\r
2720 * This method is usually called in an infinite loop to keep checking for new data
\r
2724 public function receive() {
\r
2725 //First, get an array of sockets that have data that is ready to be read
\r
2727 $ready = $this->getSockets();
\r
2728 $numrdy = stream_select($ready, $w = NULL, $x = NULL,NULL);
\r
2730 //Now that we've waited for something, go through the $ready
\r
2731 //array and read appropriately
\r
2733 for($i = 0;$i<sizeof($ready);$i++) {
\r
2734 if ($ready[$i] == $this->NSfp) {
\r
2735 $this->nsReceive();
\r
2737 $this->sbReceive($socket);
\r
2743 * Send a request for a switchboard session
\r
2744 * @param String $to Target email for switchboard session
\r
2746 private function reqSBSession($to) {
\r
2747 $this->debug_message("*** Request SB for $to");
\r
2748 $this->ns_writeln("XFR $this->id SB");
\r
2750 // Add to the queue of those waiting for a switchboard session reponse
\r
2751 $this->switchBoardSessions[$to] = array('socket' => NULL, 'id' => 1, 'lastActive' => NULL, 'joined' => false, 'XFRReqTime' => time());
\r
2752 $this->waitingForXFR[] = &$this->switchBoardSessions[$to];
\r
2756 * Following an XFR or RNG, connect to the switchboard session
\r
2758 * @param string $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case or RNG)
\r
2759 * @param string $ip IP of Switchboard
\r
2760 * @param integer $port Port of Switchboard
\r
2761 * @param string $to User on other end of Switchboard
\r
2762 * @param array $param Array of parameters - 'cki', 'ticket', 'sid'
\r
2763 * @return boolean true if successful
\r
2765 private function connectToSBSession($mode, $ip, $port, $to, $param) {
\r
2766 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2768 $this->switchBoardSessions[$to]['socket'] = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2769 $socket = $this->switchBoardSessions[$to]['socket'];
\r
2771 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2774 $this->switchBoardSockets[(int) $socket] = $socket;
\r
2776 stream_set_timeout($socket, $this->SBStreamTimeout);
\r
2778 $id = &$this->switchBoardSessions[$to]['id'];
\r
2780 if($mode == 'Active') {
\r
2781 $cki_code = $param['cki'];
\r
2783 // SB: >>> USR {id} {user} {cki}
\r
2784 $this->sb_writeln($socket, $id, "USR $id $this->user $cki_code");
\r
2787 $ticket = $param['ticket'];
\r
2788 $sid = $param['sid'];
\r
2790 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2791 $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");
\r
2794 $this->switchBoardSessions[$to]['lastActive'] = time();
\r
2798 * Send a message via an existing SB session
\r
2800 * @param string $to Recipient for message
\r
2801 * @param string $message Message
\r
2802 * @return boolean true on success
\r
2804 private function sendMessageViaSB($to, $message) {
\r
2805 if(socketcheck($this->switchBoardSessions[$to]['socket'])) {
\r
2806 $this->reqSBSession($to);
\r
2810 if(!$this->switchBoardSessions[$to]['joined']) {
\r
2811 // If our participant has not joined the session yet we can't message them!
\r
2815 $id = &$this->switchBoardSessions[$to]['id'];
\r
2816 $socket = $this->switchBoardSessions[$to]['socket'];
\r
2818 $aMessage = $this->getMessage($Message);
\r
2820 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2821 if($MsnObjDefine !== '')
\r
2823 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2824 $len = strlen($SendString);
\r
2825 // TODO handle failure during write to socket
\r
2826 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
2827 $this->sb_writedata($socket, $SendString);
\r
2829 $len = strlen($aMessage);
\r
2830 // TODO handle failure during write to socket
\r
2831 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
2832 $this->sb_writedata($socket, $aMessage);
\r
2834 // Don't close the SB session, we might as well leave it open
\r
2840 * Send offline message
\r
2841 * @param string $to Intended recipient
\r
2842 * @param string $sMessage Message
\r
2843 * @param string $lockkey Lock key
\r
2845 private function sendOIM($to, $sMessage, $lockkey) {
\r
2846 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2847 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2848 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2849 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
2851 <From memberName="'.$this->user.'"
\r
2852 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
2855 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
2856 msnpVer="'.$this->protocol.'"
\r
2857 buildVer="'.$this->buildver.'"/>
\r
2858 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2859 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
2860 appid="'.$this->prod_id.'"
\r
2861 lockkey="'.$lockkey.'"
\r
2862 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2863 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
2864 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
2865 <MessageNumber>1</MessageNumber>
\r
2869 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
2870 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
2871 Content-Type: text/plain; charset=UTF-8
\r
2872 Content-Transfer-Encoding: base64
\r
2873 X-OIM-Message-Type: OfflineMessage
\r
2874 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
2875 X-OIM-Sequence-Num: 1
\r
2877 '.chunk_split(base64_encode($sMessage)).'
\r
2880 </soap:Envelope>';
\r
2882 $header_array = array(
\r
2883 'SOAPAction: '.$this->oim_send_soap,
\r
2884 'Content-Type: text/xml',
\r
2885 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
2888 $this->debug_message("*** URL: $this->oim_send_url");
\r
2889 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2890 $curl = curl_init();
\r
2891 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
2892 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2893 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2894 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2895 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2896 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2897 curl_setopt($curl, CURLOPT_POST, 1);
\r
2898 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2899 $data = curl_exec($curl);
\r
2900 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2901 curl_close($curl);
\r
2902 $this->debug_message("*** Get Result:\n$data");
\r
2904 if ($http_code == 200) {
\r
2905 $this->debug_message("*** OIM sent for $to");
\r
2909 $challenge = false;
\r
2910 $auth_policy = false;
\r
2911 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
2912 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
2913 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
2914 if (count($matches) != 0) {
\r
2915 // yes, we get new LockKeyChallenge
\r
2916 $challenge = $matches[2];
\r
2917 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
2919 // auth policy error
\r
2920 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
2921 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
2922 if (count($matches) != 0) {
\r
2923 $auth_policy = $matches[2];
\r
2924 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
2926 if ($auth_policy === false && $challenge === false) {
\r
2927 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
2928 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
2929 if (count($matches) == 0) {
\r
2930 // no error, we assume the OIM is sent
\r
2931 $this->debug_message("*** OIM sent for $to");
\r
2934 $err_code = $matches[2];
\r
2935 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
2936 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
2937 if (count($matches) > 0)
\r
2938 $err_msg = $matches[1];
\r
2941 $this->debug_message("*** OIM failed for $to");
\r
2942 $this->debug_message("*** OIM Error code: $err_code");
\r
2943 $this->debug_message("*** OIM Error Message: $err_msg");
\r
2946 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
2950 * Send a message to a user on another network
\r
2952 * @param $message Message
\r
2953 * @param $to Intended recipient
\r
2954 * @param $network Network
\r
2957 private function sendOtherNetworkMessage($message, $to, $network) {
\r
2958 $message = $this->getMessage($message, $network);
\r
2959 $len = strlen($message);
\r
2960 $this->ns_writeln("UUM $this->id $to $network 1 $len");
\r
2961 $this->ns_writedata($Message);
\r
2962 $this->debug_message("*** Sent to $to (network: $network):\n$Message");
\r
2968 * @param string $to To address in form user@host.com(@network)
\r
2969 * where network is 1 for MSN, 32 for Yahoo
\r
2970 * and 'Offline' for offline messages
\r
2971 * @param string $message Message
\r
2973 public function sendMessage($to, $message) {
\r
2974 if($message != '') {
\r
2975 list($name, $host, $network) = explode('@', $to);
\r
2976 $network = $network == '' ? 1 : $network;
\r
2978 if ($network === 1 && $this->switchBoardSessions[$to]['socket'] !== NULL) {
\r
2979 $recipient = $name . $host;
\r
2980 $this->debug_message("*** Attempting to send message to $recipient using existing SB session");
\r
2982 if ($this->sendMessageViaSB($message, $recipient)) {
\r
2983 $this->debug_message('*** Message sent successfully');
\r
2986 $this->debug_message('*** Message sending failed, requesting new SB session');
\r
2987 $this->reqSBSession($to);
\r
2990 } elseif ($network == 'Offline') {
\r
2992 //FIXME: 修正Send OIM
\r
2994 $re_login = false;
\r
2995 for ($i = 0; $i < $this->oim_try; $i++) {
\r
2996 if (($oim_result = $this->sendOIM($to, $message, $lockkey)) === true) break;
\r
2997 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
2998 // need challenge lockkey
\r
2999 $this->debug_message("*** Need challenge code for ".$oim_result['challenge']);
\r
3000 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
3003 if ($oim_result === false || $oim_result['auth_policy'] !== false) {
\r
3005 $this->debug_message("*** Can't send OIM, but we already re-logged-in again, so ignore this OIM");
\r
3008 $this->debug_message("*** Can't send OIM, maybe ticket expired, trying to login again");
\r
3010 // Maybe we need to re-login again
\r
3011 if (!$this->get_passport_ticket()) {
\r
3012 $this->debug_message("*** Can't re-login, something went wrong here, ignore this OIM");
\r
3015 $this->debug_message("*** Getting new ticket and trying again");
\r
3020 $this->debug_message("*** Not MSN network or no existing SB session");
\r
3021 $this->reqSBSession($to);
\r
3028 //FIXME Not sure if this is needed?
\r
3029 private function endSBSession($socket) {
\r
3030 if (feof($socket)) {
\r
3031 // lost connection? error? try OIM later
\r
3036 $this->sb_writeln($socket, $fake, "OUT");
\r
3042 * Sends a ping command
\r
3044 * Should be called about every 50 seconds
\r
3048 public function sendPing() {
\r
3050 $this->ns_writeln("PNG");
\r
3054 * Methods to return sockets / check socket status
\r
3058 * Get the NS socket
\r
3060 * @return resource NS socket
\r
3062 public function getNSSocket() {
\r
3063 return $this->NSfp;
\r
3067 * Get the Switchboard sockets currently in use
\r
3069 * @return array Array of Switchboard sockets
\r
3071 public function getSBSockets() {
\r
3072 return $this->switchBoardSockets;
\r
3076 * Get all the sockets currently in use
\r
3078 * @return array Array of socket resources
\r
3080 public function getSockets() {
\r
3081 return array_merge($this->NSfp, $this->switchBoardSockets);
\r
3085 * Checks socket for end of file
\r
3087 * @param resource $socket Socket to check
\r
3088 * @return boolean true if end of file (socket)
\r
3090 private static function socketcheck($socket){
\r
3091 $info = stream_get_meta_data($socket);
\r
3092 return $info['eof'];
\r
3096 * Methods to add / call callbacks
\r
3100 * Calls User Handler
\r
3102 * Calls registered handler for a specific event.
\r
3104 * @param string $event Command (event) name (Rvous etc)
\r
3105 * @param array $data Data
\r
3106 * @see registerHandler
\r
3109 private function callHandler($event, $data = NULL) {
\r
3110 if (isset($this->myEventHandlers[$event])) {
\r
3111 if ($data !== NULL) {
\r
3112 call_user_func($this->myEventHandlers[$event], $data);
\r
3114 call_user_func($this->myEventHandlers[$event]);
\r
3120 * Registers a user handler
\r
3123 * IMIn, Pong, ConnectFailed, Reconnect
\r
3125 * @param string $event Event name
\r
3126 * @param string $handler User function to call
\r
3127 * @see callHandler
\r
3128 * @return boolean true if successful
\r
3130 public function registerHandler($event, $handler) {
\r
3131 if (is_callable($handler)) {
\r
3132 $this->myEventHandlers[$event] = $handler;
\r