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
37 private $retry_wait;
\r
38 private $backup_file;
\r
39 private $update_pending;
\r
40 private $PhotoStickerFile=false;
\r
41 private $Emotions=false;
\r
42 private $MessageQueue=array();
\r
43 private $ChildProcess=array();
\r
44 private $MAXChildProcess=3;
\r
45 private $ReqSBXFRTimeout=60;
\r
47 private $ping_wait=50;
\r
48 private $SBIdleTimeout=10;
\r
49 private $SBStreamTimeout=2;
\r
50 private $NSStreamTimeout=2;
\r
51 private $MsnObjArray=array();
\r
52 private $MsnObjMap=array();
\r
53 private $SwitchBoardProcess=false; // false=>Main Process,1 => sb_control_process,2 => sb_ring_process
\r
54 private $SwitchBoardSessionUser=false;
\r
55 private $SwitchBoardMessageQueue=array();
\r
56 private $ABAuthHeader;
\r
59 private $IgnoreList;
\r
61 public $server = 'messenger.hotmail.com';
\r
62 public $port = 1863;
\r
65 public $clientid = '';
\r
67 public $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
68 public $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
\r
69 public $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
70 public $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
\r
71 public $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
72 public $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
\r
74 public $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
75 public $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';
\r
77 public $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
78 public $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';
\r
80 public $addcontact_url = 'https://contacts.msn.com/abservice/abservice.asmx';
\r
81 public $addcontact_soap = 'http://www.msn.com/webservices/AddressBook/ABContactAdd';
\r
83 public $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
84 public $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
\r
89 public $authed = false;
\r
91 public $oim_try = 3;
\r
93 public $log_file = '';
\r
95 public $log_path = false;
\r
97 public $font_fn = 'Arial';
\r
98 public $font_co = '333333';
\r
99 public $font_ef = '';
\r
102 // the message length (include header) is limited (maybe since WLM 8.5 released)
\r
103 // for WLM: 1664 bytes
\r
104 // for YIM: 518 bytes
\r
105 public $max_msn_message_len = 1664;
\r
106 public $max_yahoo_message_len = 518;
\r
108 // Begin added for StatusNet
\r
110 private $aContactList = array();
\r
111 private $aADL = array();
\r
113 private $switchBoardSessions = array();
\r
114 private $switchBoardSockets = array();
\r
115 private $waitingForXFR = array();
\r
118 * Event Handler Functions
\r
120 private $myEventHandlers = array();
\r
122 // End added for StatusNet
\r
124 public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)
\r
126 $this->user = $Configs['user'];
\r
127 $this->password = $Configs['password'];
\r
128 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
129 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
130 $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;
\r
131 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
132 $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;
\r
133 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
134 $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;
\r
135 if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)
\r
137 foreach($this->Emotions as $EmotionFilePath)
\r
138 $this->MsnObj($EmotionFilePath,$Type=2);
\r
140 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
141 $this->timeout = $timeout;
\r
144 if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");
\r
145 if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");
\r
146 if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");
\r
148 if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");
\r
149 if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");
\r
152 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
154 normal MSN 8.1 clientid is:
\r
155 01110110 01001100 11000000 00101100
\r
158 we just use following:
\r
159 * 0x04: Your client can send/receive Ink (GIF format)
\r
160 * 0x08: Your client can send/recieve Ink (ISF format)
\r
161 * 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
162 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
165 $this->clientid = $client_id;
\r
166 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
169 private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)
\r
172 foreach($Array as $Key => $Val)
\r
174 if($Key{0}==':') continue;
\r
176 if(is_array($Val[':']))
\r
178 foreach($Val[':'] as $AttribName => $AttribVal)
\r
179 $Attrib.=" $AttribName='$AttribVal'";
\r
184 $Key=substr($Key,1);
\r
185 foreach($Val as $ListKey => $ListVal)
\r
187 if($ListKey{0}==':') continue;
\r
188 if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);
\r
189 elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';
\r
190 $ArrayString.="<$Key$Attrib>$ListVal</$Key>";
\r
194 if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);
\r
195 elseif(is_bool($Val)) $Val=$Val?'true':'false';
\r
196 $ArrayString.="<$Key$Attrib>$Val</$Key>";
\r
198 if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);
\r
199 return $ArrayString;
\r
202 private function get_passport_ticket($url = '')
\r
204 $user = $this->user;
\r
205 $password = htmlspecialchars($this->password);
\r
208 $passport_url = $this->passport_url;
\r
210 $passport_url = $url;
\r
212 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
213 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
214 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
215 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
216 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
217 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
218 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
219 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
220 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
222 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
223 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
224 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
225 <ps:UIVersion>1</ps:UIVersion>
\r
226 <ps:Cookies></ps:Cookies>
\r
227 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
230 <wsse:UsernameToken Id="user">
\r
231 <wsse:Username>'.$user.'</wsse:Username>
\r
232 <wsse:Password>'.$password.'</wsse:Password>
\r
233 </wsse:UsernameToken>
\r
237 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
238 <wst:RequestSecurityToken Id="RST0">
\r
239 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
241 <wsa:EndpointReference>
\r
242 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
243 </wsa:EndpointReference>
\r
245 </wst:RequestSecurityToken>
\r
246 <wst:RequestSecurityToken Id="RST1">
\r
247 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
249 <wsa:EndpointReference>
\r
250 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
251 </wsa:EndpointReference>
\r
253 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
254 </wst:RequestSecurityToken>
\r
255 <wst:RequestSecurityToken Id="RST2">
\r
256 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
258 <wsa:EndpointReference>
\r
259 <wsa:Address>messenger.msn.com</wsa:Address>
\r
260 </wsa:EndpointReference>
\r
262 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
263 </wst:RequestSecurityToken>
\r
264 <wst:RequestSecurityToken Id="RST3">
\r
265 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
267 <wsa:EndpointReference>
\r
268 <wsa:Address>contacts.msn.com</wsa:Address>
\r
269 </wsa:EndpointReference>
\r
271 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
272 </wst:RequestSecurityToken>
\r
273 <wst:RequestSecurityToken Id="RST4">
\r
274 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
276 <wsa:EndpointReference>
\r
277 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
278 </wsa:EndpointReference>
\r
280 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
281 </wst:RequestSecurityToken>
\r
282 <wst:RequestSecurityToken Id="RST5">
\r
283 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
285 <wsa:EndpointReference>
\r
286 <wsa:Address>spaces.live.com</wsa:Address>
\r
287 </wsa:EndpointReference>
\r
289 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
290 </wst:RequestSecurityToken>
\r
291 <wst:RequestSecurityToken Id="RST6">
\r
292 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
294 <wsa:EndpointReference>
\r
295 <wsa:Address>storage.msn.com</wsa:Address>
\r
296 </wsa:EndpointReference>
\r
298 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
299 </wst:RequestSecurityToken>
\r
300 </ps:RequestMultipleSecurityTokens>
\r
304 $this->debug_message("*** URL: $passport_url");
\r
305 $this->debug_message("*** Sending SOAP:\n$XML");
\r
306 $curl = curl_init();
\r
307 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
308 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
309 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
310 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
311 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
312 curl_setopt($curl, CURLOPT_POST, 1);
\r
313 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
314 $data = curl_exec($curl);
\r
315 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
317 $this->debug_message("*** Get Result:\n$data");
\r
319 if ($http_code != 200) {
\r
320 // sometimes, rediret to another URL
\r
322 //<faultcode>psf:Redirect</faultcode>
\r
323 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
324 //<faultstring>Authentication Failure</faultstring>
\r
325 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
326 $this->debug_message("*** Can't get passport ticket! http code = $http_code");
\r
329 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
330 if (count($matches) == 0) {
\r
331 $this->debug_message("*** redirect, but can't get redirect URL!");
\r
334 $redirect_url = $matches[1];
\r
335 if ($redirect_url == $passport_url) {
\r
336 $this->debug_message("*** redirect, but redirect to same URL!");
\r
339 $this->debug_message("*** redirect to $redirect_url");
\r
340 return $this->get_passport_ticket($redirect_url);
\r
343 // sometimes, rediret to another URL, also return 200
\r
345 //<faultcode>psf:Redirect</faultcode>
\r
346 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
347 //<faultstring>Authentication Failure</faultstring>
\r
348 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
349 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
350 if (count($matches) != 0) {
\r
351 $redirect_url = $matches[1];
\r
352 if ($redirect_url == $passport_url) {
\r
353 $this->debug_message("*** redirect, but redirect to same URL!");
\r
356 $this->debug_message("*** redirect to $redirect_url");
\r
357 return $this->get_passport_ticket($redirect_url);
\r
361 // no Redurect faultcode or URL
\r
362 // we should get the ticket here
\r
364 // we need ticket and secret code
\r
365 // RST1: messengerclear.live.com
\r
366 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
367 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
368 // RST2: messenger.msn.com
\r
369 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
370 // RST3: contacts.msn.com
\r
371 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
372 // RST4: messengersecure.live.com
\r
373 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
374 // RST5: spaces.live.com
\r
375 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
376 // RST6: storage.msn.com
\r
377 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
379 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
380 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
381 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
382 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
383 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
384 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
385 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
389 // no ticket found!
\r
390 if (count($matches) == 0) {
\r
391 $this->debug_message("*** Can't get passport ticket!");
\r
395 //$this->debug_message(var_export($matches, true));
\r
396 // matches[0]: all data
\r
397 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
399 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
401 // matches[5]: RST2 (messenger.msn.com) ticket
\r
403 // matches[7]: RST3 (contacts.msn.com) ticket
\r
405 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
406 // matches[10]: ...
\r
407 // matches[11]: RST5 (spaces.live.com) ticket
\r
408 // matches[12]: ...
\r
409 // matches[13]: RST6 (storage.live.com) ticket
\r
410 // matches[14]: ...
\r
413 // ticket => $matches[1]
\r
414 // secret => $matches[3]
\r
415 // web_ticket => $matches[5]
\r
416 // contact_ticket => $matches[7]
\r
417 // oim_ticket => $matches[9]
\r
418 // space_ticket => $matches[11]
\r
419 // storage_ticket => $matches[13]
\r
421 // yes, we get ticket
\r
423 'ticket' => html_entity_decode($matches[1]),
\r
424 'secret' => html_entity_decode($matches[3]),
\r
425 'web_ticket' => html_entity_decode($matches[5]),
\r
426 'contact_ticket' => html_entity_decode($matches[7]),
\r
427 'oim_ticket' => html_entity_decode($matches[9]),
\r
428 'space_ticket' => html_entity_decode($matches[11]),
\r
429 'storage_ticket' => html_entity_decode($matches[13])
\r
431 $this->ticket=$aTickets;
\r
432 $this->debug_message(var_export($aTickets, true));
\r
433 $ABAuthHeaderArray=array(
\r
434 'ABAuthHeader'=>array(
\r
435 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
436 'ManagedGroupRequest'=>false,
\r
437 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),
\r
440 $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));
\r
444 private function UpdateContacts()
\r
446 $ABApplicationHeaderArray=array(
\r
447 'ABApplicationHeader'=>array(
\r
448 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
449 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
450 'IsMigration'=>false,
\r
451 'PartnerScenario'=>'ContactSave'
\r
455 $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
456 $ABFindAllArray=array(
\r
457 'ABFindAll'=>array(
\r
458 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
459 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
461 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',
\r
464 $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');
\r
465 $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));
\r
466 $this->Contacts=array();
\r
469 $this->debug_message("*** Update Contacts...");
\r
470 $Result=$this->ABService->ABFindAll($ABFindAll);
\r
471 $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());
\r
472 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
473 $this->Contacts[$Contact->contactInfo->passportName]=$Contact;
\r
475 catch(Exception $e)
\r
477 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
483 private function addContact($email, $network, $display = '', $sendADL = false)
\r
485 if ($network != 1) return true;
\r
486 if(isset($this->Contacts[$email])) return true;
\r
488 $ABContactAddArray=array(
\r
489 'ABContactAdd'=>array(
\r
490 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
491 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
494 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
495 'contactInfo'=>array(
\r
496 'contactType'=>'LivePending',
\r
497 'passportName'=>$email,
\r
498 'isMessengerUser'=>true,
\r
499 'MessengerMemberInfo'=>array(
\r
500 'DisplayName'=>$email
\r
506 'EnableAllowListManagement'=>true
\r
510 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
513 $this->debug_message("*** Add Contacts $email...");
\r
514 $this->ABService->ABContactAdd($ABContactAdd);
\r
516 catch(Exception $e)
\r
518 $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
521 if ($sendADL && !feof($this->NSfp)) {
\r
522 @list($u_name, $u_domain) = @explode('@', $email);
\r
523 foreach (array('1', '2') as $l) {
\r
524 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
525 $len = strlen($str);
\r
526 // NS: >>> ADL {id} {size}
\r
527 $this->ns_writeln("ADL $this->id $len");
\r
528 $this->ns_writedata($str);
\r
531 $this->UpdateContacts();
\r
535 function delMemberFromList($memberID, $email, $network, $list) {
\r
536 if ($network != 1 && $network != 32) return true;
\r
537 if ($memberID === false) return true;
\r
539 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
541 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
542 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
543 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
544 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
545 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
547 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
548 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
549 <IsMigration>false</IsMigration>
\r
550 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
551 </ABApplicationHeader>
\r
552 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
553 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
554 <TicketToken>'.$ticket.'</TicketToken>
\r
558 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
561 <Type>Messenger</Type>
\r
562 <ForeignId></ForeignId>
\r
566 <MemberRole>'.$list.'</MemberRole>
\r
568 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
569 <Type>Passport</Type>
\r
570 <MembershipId>'.$memberID.'</MembershipId>
\r
571 <State>Accepted</State>
\r
580 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
581 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
582 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
583 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
584 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
586 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
587 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
588 <IsMigration>false</IsMigration>
\r
589 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
590 </ABApplicationHeader>
\r
591 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
592 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
593 <TicketToken>'.$ticket.'</TicketToken>
\r
597 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
600 <Type>Messenger</Type>
\r
601 <ForeignId></ForeignId>
\r
605 <MemberRole>'.$list.'</MemberRole>
\r
607 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
609 <MembershipId>'.$memberID.'</MembershipId>
\r
610 <State>Accepted</State>
\r
619 $header_array = array(
\r
620 'SOAPAction: '.$this->delmember_soap,
\r
621 'Content-Type: text/xml; charset=utf-8',
\r
622 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
625 $this->debug_message("*** URL: $this->delmember_url");
\r
626 $this->debug_message("*** Sending SOAP:\n$XML");
\r
627 $curl = curl_init();
\r
628 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
629 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
630 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
631 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
632 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
633 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
634 curl_setopt($curl, CURLOPT_POST, 1);
\r
635 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
636 $data = curl_exec($curl);
\r
637 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
639 $this->debug_message("*** Get Result:\n$data");
\r
641 if ($http_code != 200) {
\r
642 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
643 if (count($matches) == 0) {
\r
644 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
\r
647 $faultcode = trim($matches[1]);
\r
648 $faultstring = trim($matches[2]);
\r
649 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
650 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
\r
653 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
\r
656 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
\r
660 function addMemberToList($email, $network, $list) {
\r
661 if ($network != 1 && $network != 32) return true;
\r
662 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
666 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
667 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
668 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
669 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
670 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
672 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
673 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
674 <IsMigration>false</IsMigration>
\r
675 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
676 </ABApplicationHeader>
\r
677 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
678 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
679 <TicketToken>'.$ticket.'</TicketToken>
\r
683 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
686 <Type>Messenger</Type>
\r
687 <ForeignId></ForeignId>
\r
691 <MemberRole>'.$list.'</MemberRole>
\r
693 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
694 <Type>Passport</Type>
\r
695 <State>Accepted</State>
\r
696 <PassportName>'.$user.'</PassportName>
\r
705 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
706 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
707 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
708 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
709 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
711 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
712 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
713 <IsMigration>false</IsMigration>
\r
714 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
715 </ABApplicationHeader>
\r
716 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
717 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
718 <TicketToken>'.$ticket.'</TicketToken>
\r
722 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
725 <Type>Messenger</Type>
\r
726 <ForeignId></ForeignId>
\r
730 <MemberRole>'.$list.'</MemberRole>
\r
732 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
734 <State>Accepted</State>
\r
735 <Email>'.$user.'</Email>
\r
738 <Name>MSN.IM.BuddyType</Name>
\r
739 <Value>32:YAHOO</Value>
\r
749 $header_array = array(
\r
750 'SOAPAction: '.$this->addmember_soap,
\r
751 'Content-Type: text/xml; charset=utf-8',
\r
752 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
755 $this->debug_message("*** URL: $this->addmember_url");
\r
756 $this->debug_message("*** Sending SOAP:\n$XML");
\r
757 $curl = curl_init();
\r
758 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
759 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
760 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
761 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
762 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
763 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
764 curl_setopt($curl, CURLOPT_POST, 1);
\r
765 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
766 $data = curl_exec($curl);
\r
767 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
769 $this->debug_message("*** Get Result:\n$data");
\r
771 if ($http_code != 200) {
\r
772 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
773 if (count($matches) == 0) {
\r
774 $this->log_message("*** can't add member (network: $network) $email to $list");
\r
777 $faultcode = trim($matches[1]);
\r
778 $faultstring = trim($matches[2]);
\r
779 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
780 $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
\r
783 $this->log_message("*** add member (network: $network) $email to $list, already exist!");
\r
786 $this->log_message("*** add member (network: $network) $email to $list");
\r
790 function getMembershipList($returnData=false) {
\r
791 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
792 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
793 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
794 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
795 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
796 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
798 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
799 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
800 <IsMigration>false</IsMigration>
\r
801 <PartnerScenario>Initial</PartnerScenario>
\r
802 </ABApplicationHeader>
\r
803 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
804 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
805 <TicketToken>'.$ticket.'</TicketToken>
\r
809 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
812 <ServiceType>Messenger</ServiceType>
\r
813 <ServiceType>Invitation</ServiceType>
\r
814 <ServiceType>SocialNetwork</ServiceType>
\r
815 <ServiceType>Space</ServiceType>
\r
816 <ServiceType>Profile</ServiceType>
\r
822 $header_array = array(
\r
823 'SOAPAction: '.$this->membership_soap,
\r
824 'Content-Type: text/xml; charset=utf-8',
\r
825 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
827 $this->debug_message("*** URL: $this->membership_url");
\r
828 $this->debug_message("*** Sending SOAP:\n$XML");
\r
829 $curl = curl_init();
\r
830 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
831 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
832 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
833 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
834 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
835 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
836 curl_setopt($curl, CURLOPT_POST, 1);
\r
837 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
838 $data = curl_exec($curl);
\r
839 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
841 $this->debug_message("*** Get Result:\n$data");
\r
842 if($http_code != 200) return false;
\r
844 $aMemberships = array();
\r
846 //$this->debug_message("search p = $p");
\r
847 $start = strpos($p, '<Membership>');
\r
848 $end = strpos($p, '</Membership>');
\r
849 if ($start === false || $end === false || $start > $end) break;
\r
850 //$this->debug_message("start = $start, end = $end");
\r
852 $sMembership = substr($p, $start, $end - $start);
\r
853 $aMemberships[] = $sMembership;
\r
854 //$this->debug_message("add sMembership = $sMembership");
\r
855 $p = substr($p, $end);
\r
857 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
859 $aContactList = array();
\r
860 foreach ($aMemberships as $sMembership) {
\r
861 //$this->debug_message("sMembership = $sMembership");
\r
862 if (isset($matches)) unset($matches);
\r
863 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
864 if (count($matches) == 0) continue;
\r
865 $sMemberRole = $matches[1];
\r
866 //$this->debug_message("MemberRole = $sMemberRole");
\r
867 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
869 if (isset($aMembers)) unset($aMembers);
\r
870 $aMembers = array();
\r
872 //$this->debug_message("search p = $p");
\r
873 $start = strpos($p, '<Member xsi:type="');
\r
874 $end = strpos($p, '</Member>');
\r
875 if ($start === false || $end === false || $start > $end) break;
\r
876 //$this->debug_message("start = $start, end = $end");
\r
878 $sMember = substr($p, $start, $end - $start);
\r
879 $aMembers[] = $sMember;
\r
880 //$this->debug_message("add sMember = $sMember");
\r
881 $p = substr($p, $end);
\r
883 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
884 foreach ($aMembers as $sMember) {
\r
885 //$this->debug_message("sMember = $sMember");
\r
886 if (isset($matches)) unset($matches);
\r
887 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
888 if (count($matches) == 0) continue;
\r
889 $sMemberType = $matches[1];
\r
890 //$this->debug_message("MemberType = $sMemberType");
\r
892 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
893 if (count($matches) == 0) continue;
\r
895 if ($sMemberType == 'PassportMember') {
\r
896 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
898 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
900 else if ($sMemberType == 'EmailMember') {
\r
901 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
902 // Value is 32: or 32:YAHOO
\r
903 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
904 if (count($matches) == 0) continue;
\r
905 if ($matches[1] != 32) continue;
\r
907 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
909 if ($network == -1) continue;
\r
910 if (count($matches) > 0) {
\r
911 $email = $matches[1];
\r
912 @list($u_name, $u_domain) = @explode('@', $email);
\r
913 if ($u_domain == NULL) continue;
\r
914 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
915 $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
919 return $aContactList;
\r
923 * Connect to the NS server
\r
924 * @param $user Username
\r
925 * @param $password Password
\r
926 * @param $redirect_server Redirect server
\r
927 * @param $redirect_port Redirect port
\r
929 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
931 if ($redirect_server === '') {
\r
932 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
\r
933 if (!$this->NSfp) {
\r
934 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
\r
939 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);
\r
940 if (!$this->NSfp) {
\r
941 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
946 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
947 $this->authed = false;
\r
949 // NS: >> VER {id} MSNP9 CVR0
\r
951 // NS: >>> VER {id} MSNP15 CVR0
\r
952 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
954 $start_tm = time();
\r
955 while (!feof($this->NSfp))
\r
957 $data = $this->ns_readln();
\r
959 if ($data === false) {
\r
960 if ($this->timeout > 0) {
\r
962 $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
\r
963 if ($used_time > $this->timeout) {
\r
966 $this->ns_writeln("OUT");
\r
967 fclose($this->NSfp);
\r
968 $this->error = 'Timeout, maybe protocol changed!';
\r
969 $this->debug_message("*** $this->error");
\r
975 $code = substr($data, 0, 3);
\r
976 $start_tm = time();
\r
981 // NS: <<< VER {id} MSNP9 CVR0
\r
982 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
984 // NS: <<< VER {id} MSNP15 CVR0
\r
985 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
986 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
991 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
992 // NS: >>> USR {id} TWN I {user}
\r
994 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
995 // NS: >>> USR {id} SSO I {user}
\r
996 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
1000 // already login for passport site, finish the login process now.
\r
1001 // NS: <<< USR {id} OK {user} {verify} 0
\r
1002 if ($this->authed) return true;
\r
1003 // max. 16 digits for password
\r
1004 if (strlen($password) > 16)
\r
1005 $password = substr($password, 0, 16);
\r
1007 $this->user = $user;
\r
1008 $this->password = $password;
\r
1009 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1010 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1012 $this->passport_policy = $policy;
\r
1013 $aTickets = $this->get_passport_ticket();
\r
1014 if (!$aTickets || !is_array($aTickets)) {
\r
1017 $this->ns_writeln("OUT");
\r
1018 fclose($this->NSfp);
\r
1019 $this->error = 'Passport authenticated fail!';
\r
1020 $this->debug_message("*** $this->error");
\r
1024 $ticket = $aTickets['ticket'];
\r
1025 $secret = $aTickets['secret'];
\r
1026 $this->ticket = $aTickets;
\r
1027 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1029 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1030 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1031 $this->authed = true;
\r
1035 // main login server will redirect to anther NS after USR command
\r
1037 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1039 // NS: <<< XFR {id} NS {server} U D
\r
1040 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1041 if($Type!='NS') break;
\r
1042 @list($ip, $port) = @explode(':', $server);
\r
1043 // this connection will close after XFR
\r
1044 fclose($this->NSfp);
\r
1046 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
1047 if (!$this->NSfp) {
\r
1048 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1049 $this->debug_message("*** $this->error");
\r
1053 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1055 // NS: >> VER {id} MSNP9 CVR0
\r
1057 // NS: >>> VER {id} MSNP15 CVR0
\r
1058 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1062 // return some policy data after 'USR {id} SSO I {user}' command
\r
1063 // NS: <<< GCF 0 {size}
\r
1064 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1065 // we don't need the data, just read it and drop
\r
1066 if (is_numeric($size) && $size > 0)
\r
1067 $this->ns_readdata($size);
\r
1071 // we'll quit if got any error
\r
1072 if (is_numeric($code)) {
\r
1075 $this->ns_writeln("OUT");
\r
1076 fclose($this->NSfp);
\r
1077 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1078 $this->debug_message("*** $this->error");
\r
1081 // unknown response from server, just ignore it
\r
1085 // never goto here
\r
1089 * Sign onto the NS server and retrieve the address book
\r
1091 public function signon() {
\r
1092 $this->log_message("*** try to connect to MSN network");
\r
1095 while(!$this->connect($this->user, $this->password))
\r
1097 $this->signonFailure("!!! Can't connect to server: $this->error");
\r
1099 if($this->UpdateContacts() === false) {
\r
1100 $this->signonFailure('!!! Update contacts failed');
\r
1103 $this->LastPing=time();
\r
1104 $this->log_message("*** connected, wait for command");
\r
1105 $start_tm = time();
\r
1106 $ping_tm = time();
\r
1107 if(($this->aContactList = $this->getMembershipList()) === false) {
\r
1108 $this->signonFailure('!!! Get Membership list failed');
\r
1111 if ($this->update_pending) {
\r
1112 if (is_array($this->aContactList)) {
\r
1113 $pending = 'Pending';
\r
1114 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1115 foreach ($aUserList as $u_name => $aNetworks) {
\r
1116 foreach ($aNetworks as $network => $aData) {
\r
1117 if (isset($aData[$pending])) {
\r
1120 foreach (array('Allow', 'Reverse') as $list) {
\r
1121 if (isset($aData[$list]))
\r
1124 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1125 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1131 $id = $aData[$pending];
\r
1132 // we can delete it from pending now
\r
1133 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1134 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
1139 foreach (array('Allow', 'Reverse') as $list) {
\r
1140 if (!isset($aData[$list])) {
\r
1141 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1142 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1154 if (is_array($this->aContactList)) {
\r
1155 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1156 $str = '<d n="'.$u_domain.'">';
\r
1157 $len += strlen($str);
\r
1158 if ($len > 7400) {
\r
1159 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1162 $len = strlen($str);
\r
1165 foreach ($aUserList as $u_name => $aNetworks) {
\r
1166 foreach ($aNetworks as $network => $status) {
\r
1167 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1168 $len += strlen($str);
\r
1169 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1171 if ($len > 7475) {
\r
1173 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1175 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1176 $len = strlen($sList);
\r
1185 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1186 // NS: >>> BLP {id} BL
\r
1187 $this->ns_writeln("BLP $this->id BL");
\r
1188 foreach ($this->aADL as $str) {
\r
1189 $len = strlen($str);
\r
1190 // NS: >>> ADL {id} {size}
\r
1191 $this->ns_writeln("ADL $this->id $len");
\r
1192 $this->ns_writedata($str);
\r
1194 // NS: >>> PRP {id} MFN name
\r
1195 if ($this->alias == '') $this->alias = $user;
\r
1196 $aliasname = rawurlencode($this->alias);
\r
1197 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1199 //$MsnObj=$this->PhotoStckObj();
\r
1200 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1201 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1202 if($this->PhotoStickerFile!==false)
\r
1203 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1204 // NS: >>> UUX {id} length
\r
1205 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1206 $len = strlen($str);
\r
1207 $this->ns_writeln("UUX $this->id $len");
\r
1208 $this->ns_writedata($str);
\r
1214 * Called if there is an error during signon
\r
1216 * @param $message Error message to log
\r
1218 private function signonFailure($message) {
\r
1219 $this->log_message($message);
\r
1220 $this->callHandler('ConnectFailed', NULL);
\r
1221 $this->NSRetryWait($this->retry_wait);
\r
1224 function derive_key($key, $magic) {
\r
1225 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1226 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1227 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1228 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1229 return $hash2.substr($hash4, 0, 4);
\r
1232 function generateLoginBLOB($key, $challenge) {
\r
1233 $key1 = base64_decode($key);
\r
1234 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1235 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1237 // get hash of challenge using key2
\r
1238 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1240 // get 8 bytes random data
\r
1241 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1243 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1245 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1250 return base64_encode($blob);
\r
1253 function getOIM_maildata() {
\r
1254 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1255 if (count($matches) == 0) {
\r
1256 $this->debug_message('*** no web ticket?');
\r
1259 $t = htmlspecialchars($matches[1]);
\r
1260 $p = htmlspecialchars($matches[2]);
\r
1261 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1262 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1263 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1264 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1266 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1272 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1274 </soap:Envelope>';
\r
1276 $header_array = array(
\r
1277 'SOAPAction: '.$this->oim_maildata_soap,
\r
1278 'Content-Type: text/xml; charset=utf-8',
\r
1279 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1282 $this->debug_message("*** URL: $this->oim_maildata_url");
\r
1283 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1284 $curl = curl_init();
\r
1285 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1286 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1287 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1288 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1289 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1290 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1291 curl_setopt($curl, CURLOPT_POST, 1);
\r
1292 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1293 $data = curl_exec($curl);
\r
1294 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1295 curl_close($curl);
\r
1296 $this->debug_message("*** Get Result:\n$data");
\r
1298 if ($http_code != 200) {
\r
1299 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
\r
1303 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1304 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1305 if (count($matches) == 0) {
\r
1306 $this->debug_message("*** Can't get OIM maildata");
\r
1309 return $matches[2];
\r
1312 function getOIM_message($msgid) {
\r
1313 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1314 if (count($matches) == 0) {
\r
1315 $this->debug_message('*** no web ticket?');
\r
1318 $t = htmlspecialchars($matches[1]);
\r
1319 $p = htmlspecialchars($matches[2]);
\r
1322 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1323 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1324 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1325 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1327 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1333 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1334 <messageId>'.$msgid.'</messageId>
\r
1335 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1338 </soap:Envelope>';
\r
1340 $header_array = array(
\r
1341 'SOAPAction: '.$this->oim_read_soap,
\r
1342 'Content-Type: text/xml; charset=utf-8',
\r
1343 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1346 $this->debug_message("*** URL: $this->oim_read_url");
\r
1347 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1348 $curl = curl_init();
\r
1349 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1350 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1351 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1352 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1353 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1354 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1355 curl_setopt($curl, CURLOPT_POST, 1);
\r
1356 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1357 $data = curl_exec($curl);
\r
1358 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1359 curl_close($curl);
\r
1360 $this->debug_message("*** Get Result:\n$data");
\r
1362 if ($http_code != 200) {
\r
1363 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1367 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1369 $start = strpos($data, '<GetMessageResult>');
\r
1370 $end = strpos($data, '</GetMessageResult>');
\r
1371 if ($start === false || $end === false || $start > $end) {
\r
1372 $this->debug_message("*** Can't get OIM: $msgid");
\r
1375 $lines = substr($data, $start + 18, $end - $start);
\r
1376 $aLines = @explode("\n", $lines);
\r
1380 foreach ($aLines as $line) {
\r
1381 $line = rtrim($line);
\r
1383 if ($line === '') {
\r
1389 // stop at empty lines
\r
1390 if ($line === '') break;
\r
1393 $sMsg = base64_decode($sOIM);
\r
1394 $this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1397 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1398 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1399 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1400 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1402 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1408 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1410 <messageId>'.$msgid.'</messageId>
\r
1414 </soap:Envelope>';
\r
1416 $header_array = array(
\r
1417 'SOAPAction: '.$this->oim_del_soap,
\r
1418 'Content-Type: text/xml; charset=utf-8',
\r
1419 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1422 $this->debug_message("*** URL: $this->oim_del_url");
\r
1423 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1424 $curl = curl_init();
\r
1425 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1426 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1427 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1428 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1429 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1430 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1431 curl_setopt($curl, CURLOPT_POST, 1);
\r
1432 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1433 $data = curl_exec($curl);
\r
1434 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1435 curl_close($curl);
\r
1436 $this->debug_message("*** Get Result:\n$data");
\r
1438 if ($http_code != 200)
\r
1439 $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
\r
1441 $this->debug_message("*** OIM ($msgid) deleted");
\r
1444 private function NSLogout() {
\r
1445 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1448 $this->ns_writeln("OUT");
\r
1449 fclose($this->NSfp);
\r
1450 $this->NSfp = false;
\r
1451 $this->log_message("*** logout now!");
\r
1455 private function NSRetryWait($Wait) {
\r
1456 $this->log_message("*** wait for $Wait seconds");
\r
1457 for($i=0;$i<$Wait;$i++) {
\r
1459 if($this->kill_me) return false;
\r
1463 public function ProcessSendMessageFileQueue() {
\r
1464 $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');
\r
1465 if (!is_array($aFiles)) return true;
\r
1467 foreach ($aFiles as $filename) {
\r
1468 $fp = fopen($filename, 'rt');
\r
1469 if (!$fp) continue;
\r
1472 $buf = trim(fgets($fp));
\r
1473 if (substr($buf, 0, 3) == 'TO:') {
\r
1474 $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));
\r
1475 while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";
\r
1478 if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')
\r
1479 $this->log_message("!!! message format error? delete $filename");
\r
1482 foreach($aTo as $To)
\r
1484 @list($user, $domain, $network) = @explode('@', $To);
\r
1485 $MessageList[$network]["$user@$domain"]=$sMessage;
\r
1488 if($this->backup_file)
\r
1490 $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';
\r
1491 if (!file_exists($backup_dir)) @mkdir($backup_dir);
\r
1492 $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);
\r
1493 if (@rename($filename, $backup_name))
\r
1494 $this->log_message("*** move file to $backup_name");
\r
1496 else @unlink($filename);
\r
1498 foreach ($MessageList as $network => $Messages)
\r
1500 switch(trim($network))
\r
1504 // okay, try to ask a switchboard (SB) for sending message
\r
1505 // NS: >>> XFR {id} SB
\r
1506 // $this->ns_writeln("XFR $this->id SB");
\r
1507 foreach($Messages as $User => $Message)
\r
1508 $this->MessageQueue[$User][]=$Message;
\r
1510 case 'Offline': //MSN
\r
1512 //FIXME: 修正Send OIM
\r
1513 foreach($Messages as $To => $Message)
\r
1516 for ($i = 0; $i < $this->oim_try; $i++)
\r
1518 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
1519 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
1520 // need challenge lockkey
\r
1521 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
1522 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
1525 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
1527 if ($this->re_login)
\r
1529 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
1532 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
1533 // maybe we need to re-login again
\r
1534 if(!$this->get_passport_ticket())
\r
1536 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1539 $this->log_message("**** get new ticket, try it again");
\r
1546 foreach($Messages as $To => $Message) {
\r
1547 $Message=$this->getMessage($Message, $network);
\r
1548 $len = strlen($Message);
\r
1549 $this->ns_writeln("UUM $this->id $To $network 1 $len");
\r
1550 $this->ns_writedata($Message);
\r
1551 $this->log_message("*** sent to $To (network: $network):\n$Message");
\r
1555 if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))
\r
1557 $this->MessageQueue[$User]['XFRSent']=false;
\r
1558 $this->MessageQueue[$User]['ReqTime']=false;
\r
1562 public function SignalFunction($signal)
\r
1572 $ChildPid=pcntl_wait($status,WUNTRACED);
\r
1575 $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);
\r
1576 unset($this->ChildProcess[$ChildPid]);
\r
1582 public function Run()
\r
1584 $this->log_message("*** startup ***");
\r
1585 if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");
\r
1586 if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");
\r
1587 if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");
\r
1588 $process_file = false;
\r
1591 $aContactList = array();
\r
1594 if($this->kill_me)
\r
1596 $this->log_message("*** Okay, kill me now!");
\r
1597 return $this->NSLogout();
\r
1599 if (!is_resource($this->NSfp) || feof($this->NSfp))
\r
1601 $this->log_message("*** try to connect to MSN network");
\r
1602 if (!$this->connect($this->user, $this->password))
\r
1604 $this->log_message("!!! Can't connect to server: $this->error");
\r
1605 if(!$this->NSRetryWait($this->retry_wait)) continue;
\r
1607 $this->UpdateContacts();
\r
1608 $this->LastPing=time();
\r
1609 $this->log_message("*** connected, wait for command");
\r
1610 $start_tm = time();
\r
1611 $ping_tm = time();
\r
1612 $aContactList = $this->getMembershipList();
\r
1613 if ($this->update_pending) {
\r
1614 if (is_array($aContactList)) {
\r
1615 $pending = 'Pending';
\r
1616 foreach ($aContactList as $u_domain => $aUserList) {
\r
1617 foreach ($aUserList as $u_name => $aNetworks) {
\r
1618 foreach ($aNetworks as $network => $aData) {
\r
1619 if (isset($aData[$pending])) {
\r
1622 foreach (array('Allow', 'Reverse') as $list) {
\r
1623 if (isset($aData[$list]))
\r
1626 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1627 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1633 $id = $aData[$pending];
\r
1634 // we can delete it from pending now
\r
1635 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1636 unset($aContactList[$u_domain][$u_name][$network][$pending]);
\r
1641 foreach (array('Allow', 'Reverse') as $list) {
\r
1642 if (!isset($aData[$list])) {
\r
1643 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1644 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1656 if (is_array($aContactList)) {
\r
1657 foreach ($aContactList as $u_domain => $aUserList) {
\r
1658 $str = '<d n="'.$u_domain.'">';
\r
1659 $len += strlen($str);
\r
1660 if ($len > 7400) {
\r
1661 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1664 $len = strlen($str);
\r
1667 foreach ($aUserList as $u_name => $aNetworks) {
\r
1668 foreach ($aNetworks as $network => $status) {
\r
1669 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1670 $len += strlen($str);
\r
1671 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1673 if ($len > 7475) {
\r
1675 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1677 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1678 $len = strlen($sList);
\r
1687 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1688 // NS: >>> BLP {id} BL
\r
1689 $this->ns_writeln("BLP $this->id BL");
\r
1690 foreach ($aADL as $str) {
\r
1691 $len = strlen($str);
\r
1692 // NS: >>> ADL {id} {size}
\r
1693 $this->ns_writeln("ADL $this->id $len");
\r
1694 $this->ns_writedata($str);
\r
1696 // NS: >>> PRP {id} MFN name
\r
1697 if ($this->alias == '') $this->alias = $user;
\r
1698 $aliasname = rawurlencode($this->alias);
\r
1699 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1701 //$MsnObj=$this->PhotoStckObj();
\r
1702 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1703 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1704 if($this->PhotoStickerFile!==false)
\r
1705 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1706 // NS: >>> UUX {id} length
\r
1707 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1708 $len = strlen($str);
\r
1709 $this->ns_writeln("UUX $this->id $len");
\r
1710 $this->ns_writedata($str);
\r
1712 $data = $this->ns_readln();
\r
1715 //If No NS Message Process SendMessageFileQueue
\r
1716 if (time()-$this->LastPing > $this->ping_wait)
\r
1719 $this->ns_writeln("PNG");
\r
1720 $this->LastPing = time();
\r
1722 if(count($this->ChildProcess)<$this->MAXChildProcess)
\r
1725 foreach($this->MessageQueue as $User => $Message)
\r
1727 if(!trim($User)) continue;
\r
1728 if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;
\r
1729 if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))
\r
1731 $this->MessageQueue[$User]['XFRSent']=true;
\r
1732 $this->MessageQueue[$User]['ReqTime']=time();
\r
1733 $this->log_message("*** Request SB for $User");
\r
1734 $this->ns_writeln("XFR $this->id SB");
\r
1739 if($this->ProcessSendMessageFileQueue()) continue;
\r
1742 switch (substr($data,0,3))
\r
1745 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
1746 // NS: <<< SBS 0 null
\r
1751 // NS: <<< RFS ???
\r
1752 // refresh ADL, so we re-send it again
\r
1753 if (is_array($aADL)) {
\r
1754 foreach ($aADL as $str) {
\r
1755 $len = strlen($str);
\r
1756 // NS: >>> ADL {id} {size}
\r
1757 $this->ns_writeln("ADL $this->id $len");
\r
1758 $this->ns_writedata($str);
\r
1764 // NS: <<< LST {email} {alias} 11 0
\r
1765 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
1766 @list($u_name, $u_domain) = @explode('@', $email);
\r
1767 if (!isset($aContactList[$u_domain][$u_name][1])) {
\r
1768 $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
1769 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
1774 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
1775 // NS: <<< ADL 0 {size}
\r
1776 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
1777 if (is_numeric($size) && $size > 0)
\r
1779 $data = $this->ns_readdata($size);
\r
1780 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1781 if (is_array($matches) && count($matches) > 0)
\r
1783 $u_domain = $matches[1];
\r
1784 $u_name = $matches[2];
\r
1785 $network = $matches[4];
\r
1786 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1787 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
1790 $this->re_login = false;
\r
1792 foreach (array('Allow', 'Reverse') as $list)
\r
1794 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1796 if ($this->re_login) {
\r
1797 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1800 $aTickets = $this->get_passport_ticket();
\r
1801 if (!$aTickets || !is_array($aTickets)) {
\r
1802 // failed to login? ignore it
\r
1803 $this->log_message("*** can't re-login, something wrong here");
\r
1804 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1807 $this->re_login = true;
\r
1808 $this->ticket = $aTickets;
\r
1809 $this->log_message("**** get new ticket, try it again");
\r
1810 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1812 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1816 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1819 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
1821 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
1822 $len = strlen($str);
\r
1825 $this->log_message("*** someone add us to their list: $data");
\r
1826 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
1831 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
1832 // NS: <<< RML 0 {size}
\r
1833 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
1834 if (is_numeric($size) && $size > 0)
\r
1836 $data = $this->ns_readdata($size);
\r
1837 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1838 if (is_array($matches) && count($matches) > 0)
\r
1840 $u_domain = $matches[1];
\r
1841 $u_name = $matches[2];
\r
1842 $network = $matches[4];
\r
1843 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1845 $aData = $aContactList[$u_domain][$u_name][$network];
\r
1846 foreach ($aData as $list => $id)
\r
1847 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
1848 unset($aContactList[$u_domain][$u_name][$network]);
\r
1849 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
1852 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
1853 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
1856 $this->log_message("*** someone remove us from their list: $data");
\r
1861 // randomly, we get MSG notification from server
\r
1862 // NS: <<< MSG Hotmail Hotmail {size}
\r
1863 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
1864 if (is_numeric($size) && $size > 0) {
\r
1865 $data = $this->ns_readdata($size);
\r
1866 $aLines = @explode("\n", $data);
\r
1870 foreach ($aLines as $line) {
\r
1871 $line = rtrim($line);
\r
1873 if ($line === '') {
\r
1877 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
1878 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
1879 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
1880 // we just need text/x-msmsgsinitialmdatanotification
\r
1881 // or text/x-msmsgsoimnotification
\r
1888 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
1889 $maildata = trim(substr($line, 10));
\r
1894 $this->log_message("*** ingnore MSG for: $line");
\r
1897 if ($maildata == '') {
\r
1898 $this->log_message("*** ingnore MSG not for OIM");
\r
1901 $this->re_login = false;
\r
1902 if (strcasecmp($maildata, 'too-large') == 0) {
\r
1903 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
1904 $maildata = $this->getOIM_maildata();
\r
1905 if ($maildata === false) {
\r
1906 $this->log_message("*** can't get mail-data via SOAP");
\r
1907 // maybe we need to re-login again
\r
1908 $aTickets = $this->get_passport_ticket();
\r
1909 if (!$aTickets || !is_array($aTickets)) {
\r
1910 // failed to login? ignore it
\r
1911 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1914 $this->re_login = true;
\r
1915 $this->ticket = $aTickets;
\r
1916 $this->log_message("**** get new ticket, try it again");
\r
1917 $maildata = $this->getOIM_maildata();
\r
1918 if ($maildata === false) {
\r
1919 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
1924 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
1928 $start = strpos($p, '<M>');
\r
1929 $end = strpos($p, '</M>');
\r
1930 if ($start === false || $end === false || $start > $end) break;
\r
1932 $sOIM = substr($p, $start, $end - $start);
\r
1934 $p = substr($p, $end);
\r
1936 if (count($aOIMs) == 0) {
\r
1937 $this->log_message("*** ingnore empty OIM");
\r
1940 foreach ($aOIMs as $maildata) {
\r
1941 // T: 11 for MSN, 13 for Yahoo
\r
1942 // S: 6 for MSN, 7 for Yahoo
\r
1943 // RT: the datetime received by server
\r
1944 // RS: already read or not
\r
1945 // SZ: size of message
\r
1948 // F: always 00000000-0000-0000-0000-000000000009
\r
1949 // N: sender alias
\r
1950 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
1951 if (count($matches) == 0) {
\r
1952 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
1955 $oim_type = $matches[1];
\r
1956 if ($oim_type = 13)
\r
1960 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
1961 if (count($matches) == 0) {
\r
1962 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
1965 $oim_sender = $matches[1];
\r
1966 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
1967 if (count($matches) == 0) {
\r
1968 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
1971 $oim_msgid = $matches[1];
\r
1972 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
1973 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
1974 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
1975 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
1976 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
1977 $sMsg = $this->getOIM_message($oim_msgid);
\r
1978 if ($sMsg === false) {
\r
1979 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
1980 if ($this->re_login) {
\r
1981 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1984 $aTickets = $this->get_passport_ticket();
\r
1985 if (!$aTickets || !is_array($aTickets)) {
\r
1986 // failed to login? ignore it
\r
1987 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1990 $this->re_login = true;
\r
1991 $this->ticket = $aTickets;
\r
1992 $this->log_message("**** get new ticket, try it again");
\r
1993 $sMsg = $this->getOIM_message($oim_msgid);
\r
1994 if ($sMsg === false) {
\r
1995 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1999 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
2001 $this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
2007 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
2008 // NS: <<< UBM {email} $network $type {size}
\r
2009 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
2010 if (is_numeric($size) && $size > 0)
\r
2012 $data = $this->ns_readdata($size);
\r
2013 $aLines = @explode("\n", $data);
\r
2017 foreach ($aLines as $line) {
\r
2018 $line = rtrim($line);
\r
2020 if ($line === '') {
\r
2024 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2030 $aSubLines = @explode("\r", $line);
\r
2031 foreach ($aSubLines as $str) {
\r
2039 $this->log_message("*** ingnore from $from_email: $line");
\r
2042 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
2043 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2048 // randomly, we get UBX notification from server
\r
2049 // NS: <<< UBX email {network} {size}
\r
2050 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
2051 // we don't need the notification data, so just ignore it
\r
2052 if (is_numeric($size) && $size > 0)
\r
2053 $this->ns_readdata($size);
\r
2057 // randomly, we'll get challenge from server
\r
2058 // NS: <<< CHL 0 {code}
\r
2059 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
2060 $fingerprint = $this->getChallenge($chl_code);
\r
2061 // NS: >>> QRY {id} {product_id} 32
\r
2062 // NS: >>> fingerprint
\r
2063 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
2064 $this->ns_writedata($fingerprint);
\r
2065 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2066 if($this->PhotoStickerFile!==false)
\r
2067 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2070 // NS: <<< CHG {id} {status} {code}
\r
2072 // change our status to online first
\r
2076 // sometimes, NS will redirect to another NS
\r
2078 // NS: <<< XFR {id} NS {server} 0 {server}
\r
2080 // NS: <<< XFR {id} NS {server} U D
\r
2081 // for normal switchboard XFR
\r
2082 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
2083 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
2084 @list($ip, $port) = @explode(':', $server);
\r
2085 if ($server_type != 'SB') {
\r
2087 // this connection will close after XFR
\r
2088 $this->NSLogout();
\r
2091 if(count($this->MessageQueue))
\r
2093 foreach($this->MessageQueue as $User => $Message)
\r
2095 //$this->ChildProcess[$ChildPid]
\r
2096 $this->log_message("*** XFR SB $User");
\r
2097 $pid=pcntl_fork();
\r
2101 $this->ChildProcess[$pid]=$User;
\r
2106 $this->log_message("*** Fork Error $User");
\r
2112 $this->log_message("*** Child Process Start for $User");
\r
2113 unset($Message['XFRSent']);
\r
2114 unset($Message['ReqTime']);
\r
2115 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
2116 if ($bSBresult === false)
\r
2118 // error for switchboard
\r
2119 $this->log_message("!!! error for sending message to ".$User);
\r
2124 unset($this->MessageQueue[$User]);
\r
2127 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
2128 if ($bSBresult === false) {
\r
2129 // error for switchboard
\r
2130 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
2131 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
2135 // NS: <<< QNG {time}
\r
2136 @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
2137 if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
2138 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
2139 //Mod by Ricky Set Online
\r
2143 if($this->PhotoStickerFile!==false)
\r
2144 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2146 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2147 // someone is trying to talk to us
\r
2148 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2149 $this->log_message("NS: <<< RNG $data");
\r
2150 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2151 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2152 if($this->IsIgnoreMail($email))
\r
2154 $this->log_message("*** Ignore RNG from $email");
\r
2157 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
2158 $this->addContact($email,1,$email, true);
\r
2159 $pid=pcntl_fork();
\r
2163 $this->ChildProcess[$pid]='RNG';
\r
2168 $this->log_message("*** Fork Error $User");
\r
2174 $this->log_message("*** Ring Child Process Start for $User");
\r
2175 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
2180 // force logout from NS
\r
2181 // NS: <<< OUT xxx
\r
2182 fclose($this->NSfp);
\r
2183 $this->log_message("*** LOGOUT from NS");
\r
2187 $code = substr($data,0,3);
\r
2188 if (is_numeric($code)) {
\r
2189 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2190 $this->debug_message("*** NS: $this->error");
\r
2192 return $this->NsLogout();
\r
2197 return $this->NsLogout();
\r
2200 function getChallenge($code)
\r
2203 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
2204 // Step 1: The MD5 Hash
\r
2205 $md5Hash = md5($code.$this->prod_key);
\r
2206 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2207 for ($i = 0; $i < 4; $i++) {
\r
2208 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
2209 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
2212 // Step 2: A new string
\r
2213 $chl_id = $code.$this->prod_id;
\r
2214 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
2216 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
2217 for ($i = 0; $i < count($aID); $i++) {
\r
2218 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
2219 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
2222 // Step 3: The 64 bit key
\r
2223 $magic_num = 0x0E79A9C1;
\r
2224 $str7f = 0x7FFFFFFF;
\r
2227 for ($i = 0; $i < count($aID); $i += 2) {
\r
2229 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
2230 $temp = bcadd($temp, $high);
\r
2231 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
2232 $temp = bcmod($temp, $str7f);
\r
2234 $high = $aID[$i+1];
\r
2235 $high = bcmod(bcadd($high, $temp), $str7f);
\r
2236 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
2237 $high = bcmod($high, $str7f);
\r
2239 $low = bcadd(bcadd($low, $high), $temp);
\r
2242 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
2243 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
2245 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
2246 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
2247 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
2248 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
2249 // we need integer here
\r
2250 $high = 0+$new_high;
\r
2252 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
2253 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
2254 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
2255 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
2256 // we need integer here
\r
2257 $low = 0+$new_low;
\r
2259 // we just use 32 bits integer, don't need the key, just high/low
\r
2260 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
2262 // Step 4: Using the key
\r
2263 $md5Hash = md5($code.$this->prod_key);
\r
2264 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2267 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
2268 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
2269 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
2270 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
2275 private function getMessage($sMessage, $network = 1)
\r
2277 $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
2278 $msg_header_len = strlen($msg_header);
\r
2279 if ($network == 1)
\r
2280 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
2282 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
2283 $sMessage=str_replace("\r", '', $sMessage);
\r
2284 $msg=substr($sMessage,0,$maxlen);
\r
2285 return $msg_header.$msg;
\r
2289 * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息
\r
2293 private function DoSwitchBoard($Action,$Param)
\r
2295 $SessionEnd=false;
\r
2298 $LastActive=time();
\r
2299 stream_set_timeout($this->SBfp, $this->SBStreamTimeout);
\r
2303 $cki_code=$Param['cki'];
\r
2304 $user=$Param['user'];
\r
2305 $this->SwitchBoardMessageQueue=$Param['Msg'];
\r
2306 // SB: >>> USR {id} {user} {cki}
\r
2307 $this->SB_writeln("USR $this->id $this->user $cki_code");
\r
2308 $this->SwitchBoardSessionUser=$user;
\r
2311 $ticket=$Param['ticket'];
\r
2312 $sid=$Param['sid'];
\r
2313 $user=$Param['user'];
\r
2314 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2315 $this->SB_writeln("ANS $this->id $this->user $ticket $sid");
\r
2316 $this->SwitchBoardSessionUser=$user;
\r
2321 while((!feof($this->SBfp))&&(!$SessionEnd))
\r
2323 $data = $this->SB_readln();
\r
2324 if($this->kill_me)
\r
2326 $this->log_message("*** SB Okay, kill me now!");
\r
2329 if($data === false)
\r
2331 if(time()-$LastActive > $this->SBIdleTimeout)
\r
2333 $this->debug_message("*** SB Idle Timeout!");
\r
2336 if(!$Joined) continue;
\r
2337 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2339 if($Message=='') continue;
\r
2340 $aMessage = $this->getMessage($Message);
\r
2342 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2343 if($MsnObjDefine!=='')
\r
2345 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2346 $len = strlen($SendString);
\r
2347 $this->SB_writeln("MSG $this->id N $len");
\r
2348 $this->SB_writedata($SendString);
\r
2351 $len = strlen($aMessage);
\r
2352 $this->SB_writeln("MSG $this->id N $len");
\r
2353 $this->SB_writedata($aMessage);
\r
2355 $this->SwitchBoardMessageQueue=array();
\r
2356 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2359 $code = substr($data, 0, 3);
\r
2363 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
2364 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
2365 $this->log_message("*** $email join us");
\r
2369 $this->log_message("*** Quit for BYE");
\r
2373 // SB: <<< USR {id} OK {user} {alias}
\r
2374 // we don't need the data, just ignore it
\r
2375 // request user to join this switchboard
\r
2376 // SB: >>> CAL {id} {user}
\r
2377 $this->SB_writeln("CAL $this->id $user");
\r
2380 // SB: <<< CAL {id} RINGING {?}
\r
2381 // we don't need this, just ignore, and wait for other response
\r
2385 // SB: <<< JOI {user} {alias} {clientid?}
\r
2386 // someone join us
\r
2387 // we don't need the data, just ignore it
\r
2388 // no more user here
\r
2392 // SB: <<< MSG {email} {alias} {len}
\r
2393 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
2394 $len = trim($len);
\r
2395 $data = $this->SB_readdata($len);
\r
2396 $aLines = @explode("\n", $data);
\r
2401 foreach ($aLines as $line)
\r
2403 $line = rtrim($line);
\r
2405 if ($line === '') {
\r
2409 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2410 // typing notification, just ignore
\r
2414 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
2415 // we don't handle any split message, just ignore
\r
2419 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
2420 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
2422 $p = strstr($data, "\n\n");
\r
2424 if ($p === false) {
\r
2425 $p = strstr($data, "\r\n\r\n");
\r
2427 $sMsg = substr($p, 4);
\r
2430 $sMsg = substr($p, 2);
\r
2433 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
2434 // ignore all application/x-... message
\r
2436 // application/x-ms-ink => ink message
\r
2440 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
2441 // ignore all text/x-... message
\r
2443 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
2444 // text/x-mms-animemoticon => customized animemotion word
\r
2456 $this->log_message("*** ingnore from $from_email: $line");
\r
2461 // we will ignore any p2p message after sending acknowledgement
\r
2463 $len = strlen($sMsg);
\r
2464 $this->log_message("*** p2p message from $from_email, size $len");
\r
2465 // header = 48 bytes
\r
2466 // content >= 0 bytes
\r
2467 // footer = 4 bytes
\r
2468 // so it need to >= 52 bytes
\r
2469 /*if ($len < 52) {
\r
2470 $this->log_message("*** p2p: size error, less than 52!");
\r
2473 $aDwords = @unpack("V12dword", $sMsg);
\r
2474 if (!is_array($aDwords)) {
\r
2475 $this->log_message("*** p2p: header unpack error!");
\r
2478 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
2479 $hdr_SessionID = $aDwords['dword1'];
\r
2480 $hdr_Identifier = $aDwords['dword2'];
\r
2481 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
2482 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
2483 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
2484 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
2485 $hdr_MessageLength = $aDwords['dword7'];
\r
2486 $hdr_Flag = $aDwords['dword8'];
\r
2487 $hdr_AckID = $aDwords['dword9'];
\r
2488 $hdr_AckUID = $aDwords['dword10'];
\r
2489 $hdr_AckSizeLow = $aDwords['dword11'];
\r
2490 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
2491 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
2492 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
2493 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
2494 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
2495 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
2496 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
2497 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
2498 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
2499 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
2500 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
2501 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
2502 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
2503 if($hdr_Flag==2) {
\r
2504 //This is an ACK from SB ignore....
\r
2505 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
2508 $MsgBody=$this->linetoArray(substr($sMsg,48,-4));
\r
2509 $this->debug_message("*** p2p: body".print_r($MsgBody,true));
\r
2510 if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))
\r
2514 if($this->SB_readln()===false) break;
\r
2516 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));
\r
2517 preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);
\r
2518 $BranchGUID=$Matches[1];
\r
2519 //it's an invite to send a display picture.
\r
2520 $new_id = ~$hdr_Identifier;
\r
2521 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2524 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2529 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2530 $footer = pack("L", 0);
\r
2531 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2532 $len = strlen($message);
\r
2533 $this->SB_writeln("MSG $this->id D $len");
\r
2534 $this->SB_writedata($message);
\r
2535 $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
2536 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
2537 $this->SB_readln();//Read ACK;
\r
2538 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
2540 //Send 200 OK message
\r
2541 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
2543 "MSNSLP/1.0 200 OK\r\n".
\r
2544 "To: <msnmsgr:".$from_email.">\r\n".
\r
2545 "From: <msnmsgr:".$this->user.">\r\n".
\r
2546 "Via: ".$MsgBody['Via']."\r\n".
\r
2547 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
2548 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2549 "Max-Forwards: 0\r\n".
\r
2550 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
2551 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
2553 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2554 $hdr_TotalDataSizeHigh=0;
\r
2555 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2558 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2559 strlen($MessagePayload),
\r
2566 "MIME-Version: 1.0\r\n".
\r
2567 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2568 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2569 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
2570 $this->SB_writedata($message);
\r
2571 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
2572 $this->SB_readln();//Read ACK;
\r
2574 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
2575 //send Data preparation message
\r
2576 //send 4 null bytes as data
\r
2577 $hdr_TotalDataSizeLow=4;
\r
2578 $hdr_TotalDataSizeHigh=0;
\r
2580 $hdr = pack("LLLLLLLLLLLL",
\r
2581 $MsgBody['SessionID'],
\r
2584 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2585 $hdr_TotalDataSizeLow,
\r
2591 "MIME-Version: 1.0\r\n".
\r
2592 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2593 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";
\r
2594 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
2595 $this->SB_writedata($message);
\r
2596 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
2597 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
2598 $this->SB_readln();//Read ACK;
\r
2600 //send Data Content..
\r
2601 $footer=pack('N',1);
\r
2603 $FileSize=filesize($PictureFilePath);
\r
2604 if($hTitle=fopen($PictureFilePath,'rb'))
\r
2608 while(!feof($hTitle))
\r
2610 $FileContent=fread($hTitle,1024);
\r
2611 $FileContentSize=strlen($FileContent);
\r
2612 $hdr = pack("LLLLLLLLLLLL",
\r
2613 $MsgBody['SessionID'],
\r
2624 "MIME-Version: 1.0\r\n".
\r
2625 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2626 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
2627 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
2628 $this->SB_writedata($message);
\r
2629 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
2630 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
2631 //$this->SB_readln();//Read ACK;
\r
2632 $Offset+=$FileContentSize;
\r
2637 $MessageContent="\r\n".pack("C", 0);
\r
2639 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
2640 "To: <msnmsgr:$from_email>\r\n".
\r
2641 "From: <msnmsgr:".$this->user.">\r\n".
\r
2642 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
2644 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2645 "Max-Forwards: 0\r\n".
\r
2646 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
2647 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
2648 $footer=pack('N',0);
\r
2649 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2650 $hdr_TotalDataSizeHigh=0;
\r
2652 $hdr = pack("LLLLLLLLLLLL",
\r
2656 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2663 "MIME-Version: 1.0\r\n".
\r
2664 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2665 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2666 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2668 $this->SB_writedata($message);
\r
2669 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
2674 //if ($hdr_Flag == 2) {
\r
2675 // just send ACK...
\r
2676 // $this->SB_writeln("ACK $id");
\r
2679 if ($hdr_SessionID == 4) {
\r
2681 $this->debug_message("*** p2p: ignore flag 4");
\r
2684 $finished = false;
\r
2685 if ($hdr_TotalDataSizeHigh == 0) {
\r
2686 // only 32 bites size
\r
2687 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
2691 // we won't accept any file transfer
\r
2692 // so I think we won't get any message size need to use 64 bits
\r
2693 // 64 bits size here, can't count directly...
\r
2694 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
2695 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
2696 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
2697 $now_size = bcadd($dataoffset, $messagelength);
\r
2698 if (bccomp($now_size, $totalsize) >= 0)
\r
2702 // ignore not finished split packet
\r
2703 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
2706 //$new_id = ~$hdr_Identifier;
\r
2709 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2712 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2717 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2718 $footer = pack("L", 0);
\r
2719 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2720 $len = strlen($message);
\r
2721 $this->SB_writeln("MSG $id D $len");
\r
2723 $this->SB_writedata($message);
\r
2724 $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
2725 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2729 $this->log_message("*** MSG from $from_email: $sMsg");
\r
2730 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2733 $this->log_message("*** User $user is offline. Try OIM.");
\r
2734 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2735 $this->SendMessage($Message,"$user@Offline");
\r
2739 if (is_numeric($code))
\r
2741 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2742 $this->debug_message("*** SB: $this->error");
\r
2747 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2749 if (feof($this->SBfp))
\r
2751 // lost connection? error? try OIM later
\r
2752 @fclose($this->SBfp);
\r
2755 $this->SB_writeln("OUT");
\r
2756 @fclose($this->SBfp);
\r
2759 /*private function switchboard_control($ip, $port, $cki_code, $user, $Messages)
\r
2761 $this->SwitchBoardProcess=1;
\r
2762 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2763 $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2766 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2769 return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));
\r
2771 private function switchboard_ring($ip, $port, $sid, $ticket,$user)
\r
2773 $this->SwitchBoardProcess=2;
\r
2774 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2775 $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2778 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2781 return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));
\r
2784 // read data for specified size
\r
2785 private function ns_readdata($size) {
\r
2788 while (!feof($this->NSfp)) {
\r
2789 $buf = @fread($this->NSfp, $size - $count);
\r
2791 $count += strlen($buf);
\r
2792 if ($count >= $size) break;
\r
2794 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2799 private function ns_readln() {
\r
2800 $data = @fgets($this->NSfp, 4096);
\r
2801 if ($data !== false) {
\r
2802 $data = trim($data);
\r
2803 $this->debug_message("NS: <<< $data");
\r
2808 // write to server, append \r\n, also increase id
\r
2809 private function ns_writeln($data) {
\r
2810 @fwrite($this->NSfp, $data."\r\n");
\r
2811 $this->debug_message("NS: >>> $data");
\r
2816 // write data to server
\r
2817 private function ns_writedata($data) {
\r
2818 @fwrite($this->NSfp, $data);
\r
2819 $this->debug_message("NS: >>> $data");
\r
2823 // read data for specified size for SB
\r
2824 private function sb_readdata($socket, $size) {
\r
2827 while (!feof($this->SBfp)) {
\r
2828 $buf = @fread($this->SBfp, $size - $count);
\r
2830 $count += strlen($buf);
\r
2831 if ($count >= $size) break;
\r
2833 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2837 // read one line for SB
\r
2838 private function sb_readln($socket) {
\r
2839 $data = @fgets($socket, 4096);
\r
2840 if ($data !== false) {
\r
2841 $data = trim($data);
\r
2842 $this->debug_message("SB: <<< $data");
\r
2847 // write to server for SB, append \r\n, also increase id
\r
2848 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
2849 private function sb_writeln($socket, &$id, $data) {
\r
2850 @fwrite($socket, $data."\r\n");
\r
2851 $this->debug_message("SB: >>> $data");
\r
2856 // write data to server
\r
2857 private function sb_writedata($socket, $data) {
\r
2858 @fwrite($socket, $data);
\r
2859 $this->debug_message("SB: >>> $data");
\r
2863 // show debug information
\r
2864 function debug_message($str) {
\r
2865 if (!$this->debug) return;
\r
2866 if($this->debug===STDOUT) echo $str."\n";
\r
2867 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
2868 $fp = fopen($fname, 'at');
\r
2870 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2874 // still show debug information, if we can't open log_file
\r
2879 function dump_binary($str) {
\r
2883 $len = strlen($str);
\r
2884 for ($i = 0; $i < $len; $i++) {
\r
2885 if (($i % 16) == 0) {
\r
2886 if ($buf !== '') {
\r
2887 $buf .= "$h_str $a_str\n";
\r
2889 $buf .= sprintf("%04X:", $i);
\r
2893 $ch = ord($str[$i]);
\r
2897 $a_str .= chr($ch);
\r
2898 $h_str .= sprintf(" %02X", $ch);
\r
2900 if ($h_str !== '')
\r
2901 $buf .= "$h_str $a_str\n";
\r
2906 function log_message($str) {
\r
2907 /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';
\r
2908 $fp = fopen($fname, 'at');
\r
2910 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2913 $this->debug_message($str);
\r
2918 * @param $FilePath 圖檔路徑
\r
2919 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
2922 private function MsnObj($FilePath,$Type=3)
\r
2924 if(!($FileSize=filesize($FilePath))) return '';
\r
2925 $Location=md5($FilePath);
\r
2926 $Friendly=md5($FilePath.$Type);
\r
2927 if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
2928 $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));
\r
2929 $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
2930 $this->MsnObjArray[$Location]=$FilePath;
\r
2931 $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
2932 $this->MsnObjMap[$Location]=$MsnObj;
\r
2933 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
2937 private function linetoArray($lines) {
\r
2938 $lines=str_replace("\r",'',$lines);
\r
2939 $lines=explode("\n",$lines);
\r
2940 foreach($lines as $line) {
\r
2941 if(!isset($line{3})) continue;
\r
2942 list($Key,$Val)=explode(':',$line);
\r
2943 $Data[trim($Key)]=trim($Val);
\r
2948 private function GetPictureFilePath($Context)
\r
2950 $MsnObj=base64_decode($Context);
\r
2951 if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))
\r
2952 $location=$Match[1];
\r
2953 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
2954 if($location&&(isset($this->MsnObjArray[$location])))
\r
2955 return $this->MsnObjArray[$location];
\r
2959 private function GetMsnObjDefine($Message)
\r
2962 if(is_array($this->Emotions))
\r
2963 foreach($this->Emotions as $Pattern => $FilePath)
\r
2965 if(strpos($Message,$Pattern)!==false)
\r
2966 $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";
\r
2968 return $DefineString;
\r
2972 * Read and handle incoming command from NS
\r
2974 public function nsReceive() {
\r
2975 // Sign in again if not signed in or socket failed
\r
2976 if (!is_resource($this->NSfp) || feof($this->NSfp)) {
\r
2977 $this->callHandler('Reconnect', NULL);
\r
2982 $data = $this->ns_readln();
\r
2983 if($data === false) {
\r
2984 // There was no data / an error when reading from the socket so reconnect
\r
2985 $this->callHandler('Reconnect', NULL);
\r
2988 switch (substr($data,0,3))
\r
2991 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
2992 // NS: <<< SBS 0 null
\r
2997 // NS: <<< RFS ???
\r
2998 // refresh ADL, so we re-send it again
\r
2999 if (is_array($this->aADL)) {
\r
3000 foreach ($this->aADL as $str) {
\r
3001 $len = strlen($str);
\r
3002 // NS: >>> ADL {id} {size}
\r
3003 $this->ns_writeln("ADL $this->id $len");
\r
3004 $this->ns_writedata($str);
\r
3010 // NS: <<< LST {email} {alias} 11 0
\r
3011 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
3012 @list($u_name, $u_domain) = @explode('@', $email);
\r
3013 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
3014 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
3015 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
3020 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
3021 // NS: <<< ADL 0 {size}
\r
3022 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
3023 if (is_numeric($size) && $size > 0)
\r
3025 $data = $this->ns_readdata($size);
\r
3026 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3027 if (is_array($matches) && count($matches) > 0)
\r
3029 $u_domain = $matches[1];
\r
3030 $u_name = $matches[2];
\r
3031 $network = $matches[4];
\r
3032 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3033 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
3036 $this->re_login = false;
\r
3038 foreach (array('Allow', 'Reverse') as $list)
\r
3040 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3042 if ($this->re_login) {
\r
3043 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3046 $aTickets = $this->get_passport_ticket();
\r
3047 if (!$aTickets || !is_array($aTickets)) {
\r
3048 // failed to login? ignore it
\r
3049 $this->log_message("*** can't re-login, something wrong here");
\r
3050 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3053 $this->re_login = true;
\r
3054 $this->ticket = $aTickets;
\r
3055 $this->log_message("**** get new ticket, try it again");
\r
3056 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3058 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3062 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3065 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
3067 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
3068 $len = strlen($str);
\r
3071 $this->log_message("*** someone add us to their list: $data");
\r
3072 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
3077 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
3078 // NS: <<< RML 0 {size}
\r
3079 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
3080 if (is_numeric($size) && $size > 0)
\r
3082 $data = $this->ns_readdata($size);
\r
3083 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3084 if (is_array($matches) && count($matches) > 0)
\r
3086 $u_domain = $matches[1];
\r
3087 $u_name = $matches[2];
\r
3088 $network = $matches[4];
\r
3089 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3091 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
3092 foreach ($aData as $list => $id)
\r
3093 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
3094 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
3095 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
3098 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
3099 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
3102 $this->log_message("*** someone remove us from their list: $data");
\r
3107 // randomly, we get MSG notification from server
\r
3108 // NS: <<< MSG Hotmail Hotmail {size}
\r
3109 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
3110 if (is_numeric($size) && $size > 0) {
\r
3111 $data = $this->ns_readdata($size);
\r
3112 $aLines = @explode("\n", $data);
\r
3116 foreach ($aLines as $line) {
\r
3117 $line = rtrim($line);
\r
3119 if ($line === '') {
\r
3123 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
3124 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
3125 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
3126 // we just need text/x-msmsgsinitialmdatanotification
\r
3127 // or text/x-msmsgsoimnotification
\r
3134 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
3135 $maildata = trim(substr($line, 10));
\r
3140 $this->log_message("*** ingnore MSG for: $line");
\r
3143 if ($maildata == '') {
\r
3144 $this->log_message("*** ingnore MSG not for OIM");
\r
3147 $this->re_login = false;
\r
3148 if (strcasecmp($maildata, 'too-large') == 0) {
\r
3149 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
3150 $maildata = $this->getOIM_maildata();
\r
3151 if ($maildata === false) {
\r
3152 $this->log_message("*** can't get mail-data via SOAP");
\r
3153 // maybe we need to re-login again
\r
3154 $aTickets = $this->get_passport_ticket();
\r
3155 if (!$aTickets || !is_array($aTickets)) {
\r
3156 // failed to login? ignore it
\r
3157 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3160 $this->re_login = true;
\r
3161 $this->ticket = $aTickets;
\r
3162 $this->log_message("**** get new ticket, try it again");
\r
3163 $maildata = $this->getOIM_maildata();
\r
3164 if ($maildata === false) {
\r
3165 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
3170 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
3174 $start = strpos($p, '<M>');
\r
3175 $end = strpos($p, '</M>');
\r
3176 if ($start === false || $end === false || $start > $end) break;
\r
3178 $sOIM = substr($p, $start, $end - $start);
\r
3180 $p = substr($p, $end);
\r
3182 if (count($aOIMs) == 0) {
\r
3183 $this->log_message("*** ingnore empty OIM");
\r
3186 foreach ($aOIMs as $maildata) {
\r
3187 // T: 11 for MSN, 13 for Yahoo
\r
3188 // S: 6 for MSN, 7 for Yahoo
\r
3189 // RT: the datetime received by server
\r
3190 // RS: already read or not
\r
3191 // SZ: size of message
\r
3194 // F: always 00000000-0000-0000-0000-000000000009
\r
3195 // N: sender alias
\r
3196 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
3197 if (count($matches) == 0) {
\r
3198 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
3201 $oim_type = $matches[1];
\r
3202 if ($oim_type = 13)
\r
3206 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
3207 if (count($matches) == 0) {
\r
3208 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
3211 $oim_sender = $matches[1];
\r
3212 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
3213 if (count($matches) == 0) {
\r
3214 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
3217 $oim_msgid = $matches[1];
\r
3218 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
3219 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
3220 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
3221 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
3222 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
3223 $sMsg = $this->getOIM_message($oim_msgid);
\r
3224 if ($sMsg === false) {
\r
3225 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
3226 if ($this->re_login) {
\r
3227 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3230 $aTickets = $this->get_passport_ticket();
\r
3231 if (!$aTickets || !is_array($aTickets)) {
\r
3232 // failed to login? ignore it
\r
3233 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3236 $this->re_login = true;
\r
3237 $this->ticket = $aTickets;
\r
3238 $this->log_message("**** get new ticket, try it again");
\r
3239 $sMsg = $this->getOIM_message($oim_msgid);
\r
3240 if ($sMsg === false) {
\r
3241 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3245 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
3247 //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
3248 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
3254 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
3255 // NS: <<< UBM {email} $network $type {size}
\r
3256 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
3257 if (is_numeric($size) && $size > 0)
\r
3259 $data = $this->ns_readdata($size);
\r
3260 $aLines = @explode("\n", $data);
\r
3264 foreach ($aLines as $line) {
\r
3265 $line = rtrim($line);
\r
3267 if ($line === '') {
\r
3271 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
3277 $aSubLines = @explode("\r", $line);
\r
3278 foreach ($aSubLines as $str) {
\r
3286 $this->log_message("*** ingnore from $from_email: $line");
\r
3289 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
3290 //$this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
3291 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
3296 // randomly, we get UBX notification from server
\r
3297 // NS: <<< UBX email {network} {size}
\r
3298 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
3299 // we don't need the notification data, so just ignore it
\r
3300 if (is_numeric($size) && $size > 0)
\r
3301 $this->ns_readdata($size);
\r
3305 // randomly, we'll get challenge from server
\r
3306 // NS: <<< CHL 0 {code}
\r
3307 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
3308 $fingerprint = $this->getChallenge($chl_code);
\r
3309 // NS: >>> QRY {id} {product_id} 32
\r
3310 // NS: >>> fingerprint
\r
3311 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
3312 $this->ns_writedata($fingerprint);
\r
3313 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3314 if($this->PhotoStickerFile!==false)
\r
3315 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3318 // NS: <<< CHG {id} {status} {code}
\r
3320 // change our status to online first
\r
3324 // sometimes, NS will redirect to another NS
\r
3326 // NS: <<< XFR {id} NS {server} 0 {server}
\r
3328 // NS: <<< XFR {id} NS {server} U D
\r
3329 // for normal switchboard XFR
\r
3330 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
3331 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
3332 @list($ip, $port) = @explode(':', $server);
\r
3333 if ($server_type != 'SB') {
\r
3335 // this connection will close after XFR
\r
3336 $this->NSLogout();
\r
3339 if(count($this->MessageQueue))
\r
3341 foreach($this->MessageQueue as $User => $Message)
\r
3343 //$this->ChildProcess[$ChildPid]
\r
3344 $this->log_message("*** XFR SB $User");
\r
3345 $pid=pcntl_fork();
\r
3349 $this->ChildProcess[$pid]=$User;
\r
3354 $this->log_message("*** Fork Error $User");
\r
3360 $this->log_message("*** Child Process Start for $User");
\r
3361 unset($Message['XFRSent']);
\r
3362 unset($Message['ReqTime']);
\r
3363 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
3364 if ($bSBresult === false)
\r
3366 // error for switchboard
\r
3367 $this->log_message("!!! error for sending message to ".$User);
\r
3372 unset($this->MessageQueue[$User]);
\r
3375 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
3376 if ($bSBresult === false) {
\r
3377 // error for switchboard
\r
3378 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
3379 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
3383 // NS: <<< QNG {time}
\r
3384 @list(/* QNG */, $ping_wait) = @explode(' ', $data);
\r
3385 //if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
3386 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
3387 //Mod by Ricky Set Online
\r
3389 $this->callHandler('Pong', $ping_wait);
\r
3393 if($this->PhotoStickerFile!==false)
\r
3394 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3396 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3397 // someone is trying to talk to us
\r
3398 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
3399 $this->log_message("NS: <<< RNG $data");
\r
3400 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
3401 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
3402 if($this->IsIgnoreMail($email))
\r
3404 $this->log_message("*** Ignore RNG from $email");
\r
3407 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
3408 $this->addContact($email,1,$email, true);
\r
3409 $pid=pcntl_fork();
\r
3413 $this->ChildProcess[$pid]='RNG';
\r
3418 $this->log_message("*** Fork Error $User");
\r
3424 $this->log_message("*** Ring Child Process Start for $User");
\r
3425 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
3430 // force logout from NS
\r
3431 // NS: <<< OUT xxx
\r
3432 $this->log_message("*** LOGOUT from NS");
\r
3433 return $this->NsLogout();
\r
3436 $code = substr($data,0,3);
\r
3437 if (is_numeric($code)) {
\r
3438 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
3439 $this->debug_message("*** NS: $this->error");
\r
3441 return $this->NsLogout();
\r
3449 * Read and handle incoming command/message from
\r
3450 * a switchboard session socket
\r
3452 public function sbReceive() {
\r
3457 * Send a request for a switchboard session
\r
3458 * @param $to Target email for switchboard session
\r
3460 private function reqSBSession($to) {
\r
3461 $this->log_message("*** Request SB for $to");
\r
3462 $this->ns_writeln("XFR $this->id SB");
\r
3464 // Add to the queue of those waiting for a switchboard session reponse
\r
3465 $this->switchBoardSessions[$to] = array('socket' => NULL, 'id' => 1, 'lastActive' => NULL, 'joined' => false, 'XFRReqTime' => time());
\r
3466 $this->waitingForXFR[] = &$this->switchBoardSessions[$to];
\r
3470 * Following an XFR or RNG, connect to the switchboard session
\r
3471 * @param $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case or RNG)
\r
3472 * @param $ip IP of Switchboard
\r
3473 * @param $port Port of Switchboard
\r
3474 * @param $to User on other end of Switchboard
\r
3475 * @param $param Array of parameters - 'cki', 'ticket', 'sid'
\r
3476 * @return Whether successful
\r
3478 private function connectToSBSession($mode, $ip, $port, $to, $param) {
\r
3479 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
3481 $this->switchBoardSessions[$to]['socket'] = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
3482 $socket = $this->switchBoardSessions[$to]['socket'];
\r
3484 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
3487 $this->switchBoardSockets[$socket] = $socket;
\r
3489 stream_set_timeout($socket, $this->SBStreamTimeout);
\r
3491 $id = &$this->switchBoardSessions[$to]['id'];
\r
3493 if($mode == 'Active') {
\r
3494 $cki_code = $param['cki'];
\r
3496 // SB: >>> USR {id} {user} {cki}
\r
3497 $this->sb_writeln($socket, $id, "USR $id $this->user $cki_code");
\r
3500 $ticket = $param['ticket'];
\r
3501 $sid = $param['sid'];
\r
3503 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
3504 $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");
\r
3507 $this->switchBoardSessions[$to]['lastActive'] = time();
\r
3511 * Send a message via an existing SB session
\r
3512 * @param $message Message
\r
3513 * @param $to Recipient for message
\r
3514 * @return Whether successful
\r
3516 private function sendMessageViaSB($message, $to) {
\r
3517 if(socketcheck($this->switchBoardSessions[$to]['socket'])) {
\r
3518 $this->reqSBSession($to);
\r
3522 if(!$this->switchBoardSessions[$to]['joined']) {
\r
3523 // If our participant has not joined the session yet we can't message them!
\r
3527 $id = &$this->switchBoardSessions[$to]['id'];
\r
3528 $socket = $this->switchBoardSessions[$to]['socket'];
\r
3530 $aMessage = $this->getMessage($Message);
\r
3532 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
3533 if($MsnObjDefine !== '')
\r
3535 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
3536 $len = strlen($SendString);
\r
3537 // TODO handle failure during write to socket
\r
3538 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
3539 $this->sb_writedata($socket, $SendString);
\r
3541 $len = strlen($aMessage);
\r
3542 // TODO handle failure during write to socket
\r
3543 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
3544 $this->sb_writedata($socket, $aMessage);
\r
3546 // Don't close the SB session, we might as well leave it open
\r
3554 * @param $sMessage
\r
3557 private function sendOIM($to, $sMessage, $lockkey) {
\r
3558 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
3559 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
3560 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
3561 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
3563 <From memberName="'.$this->user.'"
\r
3564 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
3567 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
3568 msnpVer="'.$this->protocol.'"
\r
3569 buildVer="'.$this->buildver.'"/>
\r
3570 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
3571 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
3572 appid="'.$this->prod_id.'"
\r
3573 lockkey="'.$lockkey.'"
\r
3574 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
3575 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
3576 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
3577 <MessageNumber>1</MessageNumber>
\r
3581 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
3582 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
3583 Content-Type: text/plain; charset=UTF-8
\r
3584 Content-Transfer-Encoding: base64
\r
3585 X-OIM-Message-Type: OfflineMessage
\r
3586 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
3587 X-OIM-Sequence-Num: 1
\r
3589 '.chunk_split(base64_encode($sMessage)).'
\r
3592 </soap:Envelope>';
\r
3594 $header_array = array(
\r
3595 'SOAPAction: '.$this->oim_send_soap,
\r
3596 'Content-Type: text/xml',
\r
3597 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
3600 $this->debug_message("*** URL: $this->oim_send_url");
\r
3601 $this->debug_message("*** Sending SOAP:\n$XML");
\r
3602 $curl = curl_init();
\r
3603 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
3604 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
3605 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
3606 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
3607 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
3608 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
3609 curl_setopt($curl, CURLOPT_POST, 1);
\r
3610 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
3611 $data = curl_exec($curl);
\r
3612 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
3613 curl_close($curl);
\r
3614 $this->debug_message("*** Get Result:\n$data");
\r
3616 if ($http_code == 200) {
\r
3617 $this->debug_message("*** OIM sent for $to");
\r
3621 $challenge = false;
\r
3622 $auth_policy = false;
\r
3623 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
3624 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
3625 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
3626 if (count($matches) != 0) {
\r
3627 // yes, we get new LockKeyChallenge
\r
3628 $challenge = $matches[2];
\r
3629 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
3631 // auth policy error
\r
3632 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
3633 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
3634 if (count($matches) != 0) {
\r
3635 $auth_policy = $matches[2];
\r
3636 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
3638 if ($auth_policy === false && $challenge === false) {
\r
3639 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
3640 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
3641 if (count($matches) == 0) {
\r
3642 // no error, we assume the OIM is sent
\r
3643 $this->debug_message("*** OIM sent for $to");
\r
3646 $err_code = $matches[2];
\r
3647 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
3648 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
3649 if (count($matches) > 0)
\r
3650 $err_msg = $matches[1];
\r
3653 $this->debug_message("*** OIM failed for $to");
\r
3654 $this->debug_message("*** OIM Error code: $err_code");
\r
3655 $this->debug_message("*** OIM Error Message: $err_msg");
\r
3658 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
3662 * Send a message to a user on another network
\r
3663 * @param $message Message
\r
3664 * @param $to Intended recipient
\r
3665 * @param $network Network
\r
3667 private function sendOtherNetworkMessage($message, $to, $network) {
\r
3668 $message=$this->getMessage($nessage, $network);
\r
3669 $len = strlen($message);
\r
3670 $this->ns_writeln("UUM $this->id $to $network 1 $len");
\r
3671 $this->ns_writedata($Message);
\r
3672 $this->log_message("*** sent to $to (network: $network):\n$Message");
\r
3677 * @param $message Message
\r
3678 * @param $to To address in form user@host.com@network
\r
3679 * where network is 1 for MSN, 32 for Yahoo
\r
3680 * and 'Offline' for offline messages
\r
3682 public function sendMessage($message, $to) {
\r
3683 if($message != '') {
\r
3684 list($name,$host,$network)=explode('@',$to);
\r
3685 $network=$network==''?1:$network;
\r
3687 if($network === 1 && $this->switchBoardSessions[$to]['socket'] != NULL && time()-$this->switchBoardSessions[$to]['lastActive'] < $this->SBIdleTimeout) {
\r
3688 $recipient = $name . $host;
\r
3689 $this->debug_message("*** Sending Message to $recipient using existing SB session");
\r
3690 return $this->sendMessageViaSB($message, $recipient);
\r
3691 } elseif($network == 'Offline') {
\r
3693 //FIXME: 修正Send OIM
\r
3695 for ($i = 0; $i < $this->oim_try; $i++)
\r
3697 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
3698 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
3699 // need challenge lockkey
\r
3700 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
3701 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
3704 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
3706 if ($this->re_login)
\r
3708 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
3711 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
3712 // maybe we need to re-login again
\r
3713 if(!$this->get_passport_ticket())
\r
3715 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3718 $this->log_message("**** get new ticket, try it again");
\r
3723 $this->debug_message("*** Not MSN network or no existing SB session");
\r
3724 $this->reqSBSession($to);
\r
3731 //FIXME Not sure if this is needed?
\r
3732 private function endSBSession($socket) {
\r
3733 if (feof($socket))
\r
3735 // lost connection? error? try OIM later
\r
3740 $this->sb_writeln($socket, $fake, "OUT");
\r
3746 * Sends a ping command
\r
3748 * Should be called about every 50 seconds
\r
3750 public function sendPing() {
\r
3752 $this->ns_writeln("PNG");
\r
3756 * Get the NS socket
\r
3758 public function getNSSocket() {
\r
3759 return $this->NSfp;
\r
3763 * Get the Switchboard sockets currently in use
\r
3765 public function getSBSockets() {
\r
3766 return $this->switchBoardSockets;
\r
3770 * Get all the sockets currently in use
\r
3772 public function getSockets() {
\r
3773 return array_merge($this->NSfp, $this->switchBoardSockets);
\r
3777 * Checks socket for end of file
\r
3780 * @param Resource $socket Socket to check
\r
3781 * @return boolean true if end of file (socket)
\r
3783 private static function socketcheck($socket){
\r
3784 $info = stream_get_meta_data($socket);
\r
3785 return $info['eof'];
\r
3789 * Calls User Handler
\r
3791 * Calls registered handler for a specific event.
\r
3793 * @param String $event Command (event) name (Rvous etc)
\r
3794 * @param String $data Raw message from server
\r
3795 * @see registerHandler
\r
3798 private function callHandler($event, $data) {
\r
3799 if (isset($this->myEventHandlers[$event])) {
\r
3800 call_user_func($this->myEventHandlers[$event], $data);
\r
3805 * Registers a user handler
\r
3808 * IMIn, Pong, ConnectFailed, Reconnect
\r
3810 * @param String $event Event name
\r
3811 * @param String $handler User function to call
\r
3812 * @see callHandler
\r
3813 * @return boolean Returns true if successful
\r
3815 public function registerHandler($event, $handler) {
\r
3816 if (is_callable($handler)) {
\r
3817 $this->myEventHandlers[$event] = $handler;
\r