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
143 if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");
\r
144 if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");
\r
145 if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");
\r
147 if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");
\r
148 if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");
\r
151 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
153 normal MSN 8.1 clientid is:
\r
154 01110110 01001100 11000000 00101100
\r
157 we just use following:
\r
158 * 0x04: Your client can send/receive Ink (GIF format)
\r
159 * 0x08: Your client can send/recieve Ink (ISF format)
\r
160 * 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
161 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
164 $this->clientid = $client_id;
\r
165 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
168 private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)
\r
171 foreach($Array as $Key => $Val)
\r
173 if($Key{0}==':') continue;
\r
175 if(is_array($Val[':']))
\r
177 foreach($Val[':'] as $AttribName => $AttribVal)
\r
178 $Attrib.=" $AttribName='$AttribVal'";
\r
183 $Key=substr($Key,1);
\r
184 foreach($Val as $ListKey => $ListVal)
\r
186 if($ListKey{0}==':') continue;
\r
187 if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);
\r
188 elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';
\r
189 $ArrayString.="<$Key$Attrib>$ListVal</$Key>";
\r
193 if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);
\r
194 elseif(is_bool($Val)) $Val=$Val?'true':'false';
\r
195 $ArrayString.="<$Key$Attrib>$Val</$Key>";
\r
197 if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);
\r
198 return $ArrayString;
\r
201 private function get_passport_ticket($url = '')
\r
203 $user = $this->user;
\r
204 $password = htmlspecialchars($this->password);
\r
207 $passport_url = $this->passport_url;
\r
209 $passport_url = $url;
\r
211 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
212 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
213 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
214 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
215 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
216 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
217 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
218 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
219 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
221 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
222 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
223 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
224 <ps:UIVersion>1</ps:UIVersion>
\r
225 <ps:Cookies></ps:Cookies>
\r
226 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
229 <wsse:UsernameToken Id="user">
\r
230 <wsse:Username>'.$user.'</wsse:Username>
\r
231 <wsse:Password>'.$password.'</wsse:Password>
\r
232 </wsse:UsernameToken>
\r
236 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
237 <wst:RequestSecurityToken Id="RST0">
\r
238 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
240 <wsa:EndpointReference>
\r
241 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
242 </wsa:EndpointReference>
\r
244 </wst:RequestSecurityToken>
\r
245 <wst:RequestSecurityToken Id="RST1">
\r
246 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
248 <wsa:EndpointReference>
\r
249 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
250 </wsa:EndpointReference>
\r
252 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
253 </wst:RequestSecurityToken>
\r
254 <wst:RequestSecurityToken Id="RST2">
\r
255 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
257 <wsa:EndpointReference>
\r
258 <wsa:Address>messenger.msn.com</wsa:Address>
\r
259 </wsa:EndpointReference>
\r
261 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
262 </wst:RequestSecurityToken>
\r
263 <wst:RequestSecurityToken Id="RST3">
\r
264 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
266 <wsa:EndpointReference>
\r
267 <wsa:Address>contacts.msn.com</wsa:Address>
\r
268 </wsa:EndpointReference>
\r
270 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
271 </wst:RequestSecurityToken>
\r
272 <wst:RequestSecurityToken Id="RST4">
\r
273 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
275 <wsa:EndpointReference>
\r
276 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
277 </wsa:EndpointReference>
\r
279 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
280 </wst:RequestSecurityToken>
\r
281 <wst:RequestSecurityToken Id="RST5">
\r
282 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
284 <wsa:EndpointReference>
\r
285 <wsa:Address>spaces.live.com</wsa:Address>
\r
286 </wsa:EndpointReference>
\r
288 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
289 </wst:RequestSecurityToken>
\r
290 <wst:RequestSecurityToken Id="RST6">
\r
291 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
293 <wsa:EndpointReference>
\r
294 <wsa:Address>storage.msn.com</wsa:Address>
\r
295 </wsa:EndpointReference>
\r
297 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
298 </wst:RequestSecurityToken>
\r
299 </ps:RequestMultipleSecurityTokens>
\r
303 $this->debug_message("*** URL: $passport_url");
\r
304 $this->debug_message("*** Sending SOAP:\n$XML");
\r
305 $curl = curl_init();
\r
306 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
307 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
308 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
309 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
310 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
311 curl_setopt($curl, CURLOPT_POST, 1);
\r
312 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
313 $data = curl_exec($curl);
\r
314 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
316 $this->debug_message("*** Get Result:\n$data");
\r
318 if ($http_code != 200) {
\r
319 // sometimes, rediret to another URL
\r
321 //<faultcode>psf:Redirect</faultcode>
\r
322 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
323 //<faultstring>Authentication Failure</faultstring>
\r
324 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
325 $this->debug_message("*** Can't get passport ticket! http code = $http_code");
\r
328 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
329 if (count($matches) == 0) {
\r
330 $this->debug_message("*** redirect, but can't get redirect URL!");
\r
333 $redirect_url = $matches[1];
\r
334 if ($redirect_url == $passport_url) {
\r
335 $this->debug_message("*** redirect, but redirect to same URL!");
\r
338 $this->debug_message("*** redirect to $redirect_url");
\r
339 return $this->get_passport_ticket($redirect_url);
\r
342 // sometimes, rediret to another URL, also return 200
\r
344 //<faultcode>psf:Redirect</faultcode>
\r
345 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
346 //<faultstring>Authentication Failure</faultstring>
\r
347 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
348 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
349 if (count($matches) != 0) {
\r
350 $redirect_url = $matches[1];
\r
351 if ($redirect_url == $passport_url) {
\r
352 $this->debug_message("*** redirect, but redirect to same URL!");
\r
355 $this->debug_message("*** redirect to $redirect_url");
\r
356 return $this->get_passport_ticket($redirect_url);
\r
360 // no Redurect faultcode or URL
\r
361 // we should get the ticket here
\r
363 // we need ticket and secret code
\r
364 // RST1: messengerclear.live.com
\r
365 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
366 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
367 // RST2: messenger.msn.com
\r
368 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
369 // RST3: contacts.msn.com
\r
370 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
371 // RST4: messengersecure.live.com
\r
372 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
373 // RST5: spaces.live.com
\r
374 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
375 // RST6: storage.msn.com
\r
376 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
378 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
379 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
380 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
381 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
382 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
383 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
384 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
388 // no ticket found!
\r
389 if (count($matches) == 0) {
\r
390 $this->debug_message("*** Can't get passport ticket!");
\r
394 //$this->debug_message(var_export($matches, true));
\r
395 // matches[0]: all data
\r
396 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
398 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
400 // matches[5]: RST2 (messenger.msn.com) ticket
\r
402 // matches[7]: RST3 (contacts.msn.com) ticket
\r
404 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
405 // matches[10]: ...
\r
406 // matches[11]: RST5 (spaces.live.com) ticket
\r
407 // matches[12]: ...
\r
408 // matches[13]: RST6 (storage.live.com) ticket
\r
409 // matches[14]: ...
\r
412 // ticket => $matches[1]
\r
413 // secret => $matches[3]
\r
414 // web_ticket => $matches[5]
\r
415 // contact_ticket => $matches[7]
\r
416 // oim_ticket => $matches[9]
\r
417 // space_ticket => $matches[11]
\r
418 // storage_ticket => $matches[13]
\r
420 // yes, we get ticket
\r
422 'ticket' => html_entity_decode($matches[1]),
\r
423 'secret' => html_entity_decode($matches[3]),
\r
424 'web_ticket' => html_entity_decode($matches[5]),
\r
425 'contact_ticket' => html_entity_decode($matches[7]),
\r
426 'oim_ticket' => html_entity_decode($matches[9]),
\r
427 'space_ticket' => html_entity_decode($matches[11]),
\r
428 'storage_ticket' => html_entity_decode($matches[13])
\r
430 $this->ticket=$aTickets;
\r
431 $this->debug_message(var_export($aTickets, true));
\r
432 $ABAuthHeaderArray=array(
\r
433 'ABAuthHeader'=>array(
\r
434 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
435 'ManagedGroupRequest'=>false,
\r
436 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),
\r
439 $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));
\r
443 private function UpdateContacts()
\r
445 $ABApplicationHeaderArray=array(
\r
446 'ABApplicationHeader'=>array(
\r
447 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
448 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
449 'IsMigration'=>false,
\r
450 'PartnerScenario'=>'ContactSave'
\r
454 $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
455 $ABFindAllArray=array(
\r
456 'ABFindAll'=>array(
\r
457 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
458 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
460 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',
\r
463 $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');
\r
464 $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));
\r
465 $this->Contacts=array();
\r
468 $this->debug_message("*** Update Contacts...");
\r
469 $Result=$this->ABService->ABFindAll($ABFindAll);
\r
470 $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());
\r
471 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
472 $this->Contacts[$Contact->contactInfo->passportName]=$Contact;
\r
474 catch(Exception $e)
\r
476 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
482 private function addContact($email, $network, $display = '', $sendADL = false)
\r
484 if ($network != 1) return true;
\r
485 if(isset($this->Contacts[$email])) return true;
\r
487 $ABContactAddArray=array(
\r
488 'ABContactAdd'=>array(
\r
489 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
490 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
493 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
494 'contactInfo'=>array(
\r
495 'contactType'=>'LivePending',
\r
496 'passportName'=>$email,
\r
497 'isMessengerUser'=>true,
\r
498 'MessengerMemberInfo'=>array(
\r
499 'DisplayName'=>$email
\r
505 'EnableAllowListManagement'=>true
\r
509 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
512 $this->debug_message("*** Add Contacts $email...");
\r
513 $this->ABService->ABContactAdd($ABContactAdd);
\r
515 catch(Exception $e)
\r
517 $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
520 if ($sendADL && !feof($this->NSfp)) {
\r
521 @list($u_name, $u_domain) = @explode('@', $email);
\r
522 foreach (array('1', '2') as $l) {
\r
523 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
524 $len = strlen($str);
\r
525 // NS: >>> ADL {id} {size}
\r
526 $this->ns_writeln("ADL $this->id $len");
\r
527 $this->ns_writedata($str);
\r
530 $this->UpdateContacts();
\r
534 function delMemberFromList($memberID, $email, $network, $list) {
\r
535 if ($network != 1 && $network != 32) return true;
\r
536 if ($memberID === false) return true;
\r
538 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
540 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
541 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
542 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
543 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
544 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
546 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
547 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
548 <IsMigration>false</IsMigration>
\r
549 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
550 </ABApplicationHeader>
\r
551 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
552 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
553 <TicketToken>'.$ticket.'</TicketToken>
\r
557 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
560 <Type>Messenger</Type>
\r
561 <ForeignId></ForeignId>
\r
565 <MemberRole>'.$list.'</MemberRole>
\r
567 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
568 <Type>Passport</Type>
\r
569 <MembershipId>'.$memberID.'</MembershipId>
\r
570 <State>Accepted</State>
\r
579 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
580 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
581 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
582 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
583 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
585 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
586 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
587 <IsMigration>false</IsMigration>
\r
588 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
589 </ABApplicationHeader>
\r
590 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
591 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
592 <TicketToken>'.$ticket.'</TicketToken>
\r
596 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
599 <Type>Messenger</Type>
\r
600 <ForeignId></ForeignId>
\r
604 <MemberRole>'.$list.'</MemberRole>
\r
606 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
608 <MembershipId>'.$memberID.'</MembershipId>
\r
609 <State>Accepted</State>
\r
618 $header_array = array(
\r
619 'SOAPAction: '.$this->delmember_soap,
\r
620 'Content-Type: text/xml; charset=utf-8',
\r
621 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
624 $this->debug_message("*** URL: $this->delmember_url");
\r
625 $this->debug_message("*** Sending SOAP:\n$XML");
\r
626 $curl = curl_init();
\r
627 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
628 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
629 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
630 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
631 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
632 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
633 curl_setopt($curl, CURLOPT_POST, 1);
\r
634 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
635 $data = curl_exec($curl);
\r
636 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
638 $this->debug_message("*** Get Result:\n$data");
\r
640 if ($http_code != 200) {
\r
641 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
642 if (count($matches) == 0) {
\r
643 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
\r
646 $faultcode = trim($matches[1]);
\r
647 $faultstring = trim($matches[2]);
\r
648 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
649 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
\r
652 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
\r
655 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
\r
659 function addMemberToList($email, $network, $list) {
\r
660 if ($network != 1 && $network != 32) return true;
\r
661 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
665 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
666 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
667 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
668 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
669 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
671 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
672 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
673 <IsMigration>false</IsMigration>
\r
674 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
675 </ABApplicationHeader>
\r
676 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
677 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
678 <TicketToken>'.$ticket.'</TicketToken>
\r
682 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
685 <Type>Messenger</Type>
\r
686 <ForeignId></ForeignId>
\r
690 <MemberRole>'.$list.'</MemberRole>
\r
692 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
693 <Type>Passport</Type>
\r
694 <State>Accepted</State>
\r
695 <PassportName>'.$user.'</PassportName>
\r
704 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
705 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
706 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
707 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
708 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
710 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
711 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
712 <IsMigration>false</IsMigration>
\r
713 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
714 </ABApplicationHeader>
\r
715 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
716 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
717 <TicketToken>'.$ticket.'</TicketToken>
\r
721 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
724 <Type>Messenger</Type>
\r
725 <ForeignId></ForeignId>
\r
729 <MemberRole>'.$list.'</MemberRole>
\r
731 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
733 <State>Accepted</State>
\r
734 <Email>'.$user.'</Email>
\r
737 <Name>MSN.IM.BuddyType</Name>
\r
738 <Value>32:YAHOO</Value>
\r
748 $header_array = array(
\r
749 'SOAPAction: '.$this->addmember_soap,
\r
750 'Content-Type: text/xml; charset=utf-8',
\r
751 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
754 $this->debug_message("*** URL: $this->addmember_url");
\r
755 $this->debug_message("*** Sending SOAP:\n$XML");
\r
756 $curl = curl_init();
\r
757 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
758 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
759 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
760 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
761 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
762 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
763 curl_setopt($curl, CURLOPT_POST, 1);
\r
764 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
765 $data = curl_exec($curl);
\r
766 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
768 $this->debug_message("*** Get Result:\n$data");
\r
770 if ($http_code != 200) {
\r
771 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
772 if (count($matches) == 0) {
\r
773 $this->log_message("*** can't add member (network: $network) $email to $list");
\r
776 $faultcode = trim($matches[1]);
\r
777 $faultstring = trim($matches[2]);
\r
778 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
779 $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
\r
782 $this->log_message("*** add member (network: $network) $email to $list, already exist!");
\r
785 $this->log_message("*** add member (network: $network) $email to $list");
\r
789 function getMembershipList($returnData=false) {
\r
790 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
791 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
792 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
793 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
794 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
795 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
797 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
798 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
799 <IsMigration>false</IsMigration>
\r
800 <PartnerScenario>Initial</PartnerScenario>
\r
801 </ABApplicationHeader>
\r
802 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
803 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
804 <TicketToken>'.$ticket.'</TicketToken>
\r
808 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
811 <ServiceType>Messenger</ServiceType>
\r
812 <ServiceType>Invitation</ServiceType>
\r
813 <ServiceType>SocialNetwork</ServiceType>
\r
814 <ServiceType>Space</ServiceType>
\r
815 <ServiceType>Profile</ServiceType>
\r
821 $header_array = array(
\r
822 'SOAPAction: '.$this->membership_soap,
\r
823 'Content-Type: text/xml; charset=utf-8',
\r
824 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
826 $this->debug_message("*** URL: $this->membership_url");
\r
827 $this->debug_message("*** Sending SOAP:\n$XML");
\r
828 $curl = curl_init();
\r
829 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
830 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
831 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
832 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
833 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
834 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
835 curl_setopt($curl, CURLOPT_POST, 1);
\r
836 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
837 $data = curl_exec($curl);
\r
838 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
840 $this->debug_message("*** Get Result:\n$data");
\r
841 if($http_code != 200) return false;
\r
843 $aMemberships = array();
\r
845 //$this->debug_message("search p = $p");
\r
846 $start = strpos($p, '<Membership>');
\r
847 $end = strpos($p, '</Membership>');
\r
848 if ($start === false || $end === false || $start > $end) break;
\r
849 //$this->debug_message("start = $start, end = $end");
\r
851 $sMembership = substr($p, $start, $end - $start);
\r
852 $aMemberships[] = $sMembership;
\r
853 //$this->debug_message("add sMembership = $sMembership");
\r
854 $p = substr($p, $end);
\r
856 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
858 $aContactList = array();
\r
859 foreach ($aMemberships as $sMembership) {
\r
860 //$this->debug_message("sMembership = $sMembership");
\r
861 if (isset($matches)) unset($matches);
\r
862 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
863 if (count($matches) == 0) continue;
\r
864 $sMemberRole = $matches[1];
\r
865 //$this->debug_message("MemberRole = $sMemberRole");
\r
866 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
868 if (isset($aMembers)) unset($aMembers);
\r
869 $aMembers = array();
\r
871 //$this->debug_message("search p = $p");
\r
872 $start = strpos($p, '<Member xsi:type="');
\r
873 $end = strpos($p, '</Member>');
\r
874 if ($start === false || $end === false || $start > $end) break;
\r
875 //$this->debug_message("start = $start, end = $end");
\r
877 $sMember = substr($p, $start, $end - $start);
\r
878 $aMembers[] = $sMember;
\r
879 //$this->debug_message("add sMember = $sMember");
\r
880 $p = substr($p, $end);
\r
882 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
883 foreach ($aMembers as $sMember) {
\r
884 //$this->debug_message("sMember = $sMember");
\r
885 if (isset($matches)) unset($matches);
\r
886 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
887 if (count($matches) == 0) continue;
\r
888 $sMemberType = $matches[1];
\r
889 //$this->debug_message("MemberType = $sMemberType");
\r
891 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
892 if (count($matches) == 0) continue;
\r
894 if ($sMemberType == 'PassportMember') {
\r
895 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
897 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
899 else if ($sMemberType == 'EmailMember') {
\r
900 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
901 // Value is 32: or 32:YAHOO
\r
902 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
903 if (count($matches) == 0) continue;
\r
904 if ($matches[1] != 32) continue;
\r
906 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
908 if ($network == -1) continue;
\r
909 if (count($matches) > 0) {
\r
910 $email = $matches[1];
\r
911 @list($u_name, $u_domain) = @explode('@', $email);
\r
912 if ($u_domain == NULL) continue;
\r
913 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
914 $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
918 return $aContactList;
\r
922 * Connect to the NS server
\r
923 * @param $user Username
\r
924 * @param $password Password
\r
925 * @param $redirect_server Redirect server
\r
926 * @param $redirect_port Redirect port
\r
928 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
930 if ($redirect_server === '') {
\r
931 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
\r
932 if (!$this->NSfp) {
\r
933 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
\r
938 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);
\r
939 if (!$this->NSfp) {
\r
940 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
945 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
946 $this->authed = false;
\r
948 // NS: >> VER {id} MSNP9 CVR0
\r
950 // NS: >>> VER {id} MSNP15 CVR0
\r
951 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
953 $start_tm = time();
\r
954 while (!feof($this->NSfp))
\r
956 $data = $this->ns_readln();
\r
958 if ($data === false) {
\r
959 if ($this->timeout > 0) {
\r
961 $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
\r
962 if ($used_time > $this->timeout) {
\r
965 $this->ns_writeln("OUT");
\r
966 fclose($this->NSfp);
\r
967 $this->error = 'Timeout, maybe protocol changed!';
\r
968 $this->debug_message("*** $this->error");
\r
974 $code = substr($data, 0, 3);
\r
975 $start_tm = time();
\r
980 // NS: <<< VER {id} MSNP9 CVR0
\r
981 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
983 // NS: <<< VER {id} MSNP15 CVR0
\r
984 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
985 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
990 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
991 // NS: >>> USR {id} TWN I {user}
\r
993 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
994 // NS: >>> USR {id} SSO I {user}
\r
995 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
999 // already login for passport site, finish the login process now.
\r
1000 // NS: <<< USR {id} OK {user} {verify} 0
\r
1001 if ($this->authed) return true;
\r
1002 // max. 16 digits for password
\r
1003 if (strlen($password) > 16)
\r
1004 $password = substr($password, 0, 16);
\r
1006 $this->user = $user;
\r
1007 $this->password = $password;
\r
1008 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1009 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1011 $this->passport_policy = $policy;
\r
1012 $aTickets = $this->get_passport_ticket();
\r
1013 if (!$aTickets || !is_array($aTickets)) {
\r
1016 $this->ns_writeln("OUT");
\r
1017 fclose($this->NSfp);
\r
1018 $this->error = 'Passport authenticated fail!';
\r
1019 $this->debug_message("*** $this->error");
\r
1023 $ticket = $aTickets['ticket'];
\r
1024 $secret = $aTickets['secret'];
\r
1025 $this->ticket = $aTickets;
\r
1026 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1028 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1029 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1030 $this->authed = true;
\r
1034 // main login server will redirect to anther NS after USR command
\r
1036 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1038 // NS: <<< XFR {id} NS {server} U D
\r
1039 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1040 if($Type!='NS') break;
\r
1041 @list($ip, $port) = @explode(':', $server);
\r
1042 // this connection will close after XFR
\r
1043 fclose($this->NSfp);
\r
1045 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
1046 if (!$this->NSfp) {
\r
1047 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1048 $this->debug_message("*** $this->error");
\r
1052 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1054 // NS: >> VER {id} MSNP9 CVR0
\r
1056 // NS: >>> VER {id} MSNP15 CVR0
\r
1057 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1061 // return some policy data after 'USR {id} SSO I {user}' command
\r
1062 // NS: <<< GCF 0 {size}
\r
1063 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1064 // we don't need the data, just read it and drop
\r
1065 if (is_numeric($size) && $size > 0)
\r
1066 $this->ns_readdata($size);
\r
1070 // we'll quit if got any error
\r
1071 if (is_numeric($code)) {
\r
1074 $this->ns_writeln("OUT");
\r
1075 fclose($this->NSfp);
\r
1076 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1077 $this->debug_message("*** $this->error");
\r
1080 // unknown response from server, just ignore it
\r
1084 // never goto here
\r
1088 * Sign onto the NS server and retrieve the address book
\r
1090 public function signon() {
\r
1091 $this->log_message("*** try to connect to MSN network");
\r
1092 while(!$this->connect($this->user, $this->password))
\r
1094 $this->signonFailed("!!! Can't connect to server: $this->error");
\r
1096 if(!$this->UpdateContacts()) {
\r
1097 $this->signonFailed('!!! Could not update contacts');
\r
1098 return $this->signon();
\r
1100 $this->LastPing=time();
\r
1101 $this->log_message("*** connected, wait for command");
\r
1102 $start_tm = time();
\r
1103 $ping_tm = time();
\r
1104 if(($this->aContactList = $this->getMembershipList()) === false) {
\r
1105 $this->signonFailed('!!! Could not get Membership List');
\r
1106 return $this->signon();
\r
1108 if ($this->update_pending) {
\r
1109 if (is_array($this->aContactList)) {
\r
1110 $pending = 'Pending';
\r
1111 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1112 foreach ($aUserList as $u_name => $aNetworks) {
\r
1113 foreach ($aNetworks as $network => $aData) {
\r
1114 if (isset($aData[$pending])) {
\r
1117 foreach (array('Allow', 'Reverse') as $list) {
\r
1118 if (isset($aData[$list]))
\r
1121 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1122 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1128 $id = $aData[$pending];
\r
1129 // we can delete it from pending now
\r
1130 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1131 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
1136 foreach (array('Allow', 'Reverse') as $list) {
\r
1137 if (!isset($aData[$list])) {
\r
1138 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1139 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1151 if (is_array($this->aContactList)) {
\r
1152 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1153 $str = '<d n="'.$u_domain.'">';
\r
1154 $len += strlen($str);
\r
1155 if ($len > 7400) {
\r
1156 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1159 $len = strlen($str);
\r
1162 foreach ($aUserList as $u_name => $aNetworks) {
\r
1163 foreach ($aNetworks as $network => $status) {
\r
1164 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1165 $len += strlen($str);
\r
1166 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1168 if ($len > 7475) {
\r
1170 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1172 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1173 $len = strlen($sList);
\r
1182 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1183 // NS: >>> BLP {id} BL
\r
1184 $this->ns_writeln("BLP $this->id BL");
\r
1185 foreach ($this->aADL as $str) {
\r
1186 $len = strlen($str);
\r
1187 // NS: >>> ADL {id} {size}
\r
1188 $this->ns_writeln("ADL $this->id $len");
\r
1189 $this->ns_writedata($str);
\r
1191 // NS: >>> PRP {id} MFN name
\r
1192 if ($this->alias == '') $this->alias = $user;
\r
1193 $aliasname = rawurlencode($this->alias);
\r
1194 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1196 //$MsnObj=$this->PhotoStckObj();
\r
1197 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1198 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1199 if($this->PhotoStickerFile!==false)
\r
1200 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1201 // NS: >>> UUX {id} length
\r
1202 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1203 $len = strlen($str);
\r
1204 $this->ns_writeln("UUX $this->id $len");
\r
1205 $this->ns_writedata($str);
\r
1208 private function signonFailed($message) {
\r
1209 $this->log_message($message);
\r
1210 $this->callHandler('ConnectFailed', NULL);
\r
1211 $this->NSRetryWait($this->retry_wait);
\r
1214 function derive_key($key, $magic) {
\r
1215 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1216 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1217 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1218 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1219 return $hash2.substr($hash4, 0, 4);
\r
1222 function generateLoginBLOB($key, $challenge) {
\r
1223 $key1 = base64_decode($key);
\r
1224 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1225 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1227 // get hash of challenge using key2
\r
1228 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1230 // get 8 bytes random data
\r
1231 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1233 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1235 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1240 return base64_encode($blob);
\r
1243 function getOIM_maildata() {
\r
1244 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1245 if (count($matches) == 0) {
\r
1246 $this->debug_message('*** no web ticket?');
\r
1249 $t = htmlspecialchars($matches[1]);
\r
1250 $p = htmlspecialchars($matches[2]);
\r
1251 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1252 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1253 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1254 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1256 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1262 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1264 </soap:Envelope>';
\r
1266 $header_array = array(
\r
1267 'SOAPAction: '.$this->oim_maildata_soap,
\r
1268 'Content-Type: text/xml; charset=utf-8',
\r
1269 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1272 $this->debug_message("*** URL: $this->oim_maildata_url");
\r
1273 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1274 $curl = curl_init();
\r
1275 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1276 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1277 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1278 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1279 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1280 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1281 curl_setopt($curl, CURLOPT_POST, 1);
\r
1282 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1283 $data = curl_exec($curl);
\r
1284 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1285 curl_close($curl);
\r
1286 $this->debug_message("*** Get Result:\n$data");
\r
1288 if ($http_code != 200) {
\r
1289 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
\r
1293 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1294 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1295 if (count($matches) == 0) {
\r
1296 $this->debug_message("*** Can't get OIM maildata");
\r
1299 return $matches[2];
\r
1302 function getOIM_message($msgid) {
\r
1303 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1304 if (count($matches) == 0) {
\r
1305 $this->debug_message('*** no web ticket?');
\r
1308 $t = htmlspecialchars($matches[1]);
\r
1309 $p = htmlspecialchars($matches[2]);
\r
1312 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1313 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1314 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1315 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1317 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1323 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1324 <messageId>'.$msgid.'</messageId>
\r
1325 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1328 </soap:Envelope>';
\r
1330 $header_array = array(
\r
1331 'SOAPAction: '.$this->oim_read_soap,
\r
1332 'Content-Type: text/xml; charset=utf-8',
\r
1333 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1336 $this->debug_message("*** URL: $this->oim_read_url");
\r
1337 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1338 $curl = curl_init();
\r
1339 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1340 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1341 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1342 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1343 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1344 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1345 curl_setopt($curl, CURLOPT_POST, 1);
\r
1346 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1347 $data = curl_exec($curl);
\r
1348 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1349 curl_close($curl);
\r
1350 $this->debug_message("*** Get Result:\n$data");
\r
1352 if ($http_code != 200) {
\r
1353 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1357 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1359 $start = strpos($data, '<GetMessageResult>');
\r
1360 $end = strpos($data, '</GetMessageResult>');
\r
1361 if ($start === false || $end === false || $start > $end) {
\r
1362 $this->debug_message("*** Can't get OIM: $msgid");
\r
1365 $lines = substr($data, $start + 18, $end - $start);
\r
1366 $aLines = @explode("\n", $lines);
\r
1370 foreach ($aLines as $line) {
\r
1371 $line = rtrim($line);
\r
1373 if ($line === '') {
\r
1379 // stop at empty lines
\r
1380 if ($line === '') break;
\r
1383 $sMsg = base64_decode($sOIM);
\r
1384 $this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1387 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1388 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1389 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1390 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1392 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1398 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1400 <messageId>'.$msgid.'</messageId>
\r
1404 </soap:Envelope>';
\r
1406 $header_array = array(
\r
1407 'SOAPAction: '.$this->oim_del_soap,
\r
1408 'Content-Type: text/xml; charset=utf-8',
\r
1409 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1412 $this->debug_message("*** URL: $this->oim_del_url");
\r
1413 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1414 $curl = curl_init();
\r
1415 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1416 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1417 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1418 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1419 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1420 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1421 curl_setopt($curl, CURLOPT_POST, 1);
\r
1422 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1423 $data = curl_exec($curl);
\r
1424 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1425 curl_close($curl);
\r
1426 $this->debug_message("*** Get Result:\n$data");
\r
1428 if ($http_code != 200)
\r
1429 $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
\r
1431 $this->debug_message("*** OIM ($msgid) deleted");
\r
1434 private function NSLogout() {
\r
1435 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1438 $this->ns_writeln("OUT");
\r
1439 fclose($this->NSfp);
\r
1440 $this->NSfp = false;
\r
1441 $this->log_message("*** logout now!");
\r
1445 private function NSRetryWait($Wait) {
\r
1446 $this->log_message("*** wait for $Wait seconds");
\r
1447 for($i=0;$i<$Wait;$i++) {
\r
1449 if($this->kill_me) return false;
\r
1453 public function ProcessSendMessageFileQueue() {
\r
1454 $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');
\r
1455 if (!is_array($aFiles)) return true;
\r
1457 foreach ($aFiles as $filename) {
\r
1458 $fp = fopen($filename, 'rt');
\r
1459 if (!$fp) continue;
\r
1462 $buf = trim(fgets($fp));
\r
1463 if (substr($buf, 0, 3) == 'TO:') {
\r
1464 $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));
\r
1465 while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";
\r
1468 if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')
\r
1469 $this->log_message("!!! message format error? delete $filename");
\r
1472 foreach($aTo as $To)
\r
1474 @list($user, $domain, $network) = @explode('@', $To);
\r
1475 $MessageList[$network]["$user@$domain"]=$sMessage;
\r
1478 if($this->backup_file)
\r
1480 $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';
\r
1481 if (!file_exists($backup_dir)) @mkdir($backup_dir);
\r
1482 $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);
\r
1483 if (@rename($filename, $backup_name))
\r
1484 $this->log_message("*** move file to $backup_name");
\r
1486 else @unlink($filename);
\r
1488 foreach ($MessageList as $network => $Messages)
\r
1490 switch(trim($network))
\r
1494 // okay, try to ask a switchboard (SB) for sending message
\r
1495 // NS: >>> XFR {id} SB
\r
1496 // $this->ns_writeln("XFR $this->id SB");
\r
1497 foreach($Messages as $User => $Message)
\r
1498 $this->MessageQueue[$User][]=$Message;
\r
1500 case 'Offline': //MSN
\r
1502 //FIXME: 修正Send OIM
\r
1503 foreach($Messages as $To => $Message)
\r
1506 for ($i = 0; $i < $this->oim_try; $i++)
\r
1508 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
1509 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
1510 // need challenge lockkey
\r
1511 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
1512 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
1515 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
1517 if ($this->re_login)
\r
1519 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
1522 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
1523 // maybe we need to re-login again
\r
1524 if(!$this->get_passport_ticket())
\r
1526 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1529 $this->log_message("**** get new ticket, try it again");
\r
1536 foreach($Messages as $To => $Message) {
\r
1537 $Message=$this->getMessage($Message, $network);
\r
1538 $len = strlen($Message);
\r
1539 $this->ns_writeln("UUM $this->id $To $network 1 $len");
\r
1540 $this->ns_writedata($Message);
\r
1541 $this->log_message("*** sent to $To (network: $network):\n$Message");
\r
1545 if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))
\r
1547 $this->MessageQueue[$User]['XFRSent']=false;
\r
1548 $this->MessageQueue[$User]['ReqTime']=false;
\r
1552 public function SignalFunction($signal)
\r
1562 $ChildPid=pcntl_wait($status,WUNTRACED);
\r
1565 $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);
\r
1566 unset($this->ChildProcess[$ChildPid]);
\r
1572 public function Run()
\r
1574 $this->log_message("*** startup ***");
\r
1575 if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");
\r
1576 if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");
\r
1577 if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");
\r
1578 $process_file = false;
\r
1581 $aContactList = array();
\r
1584 if($this->kill_me)
\r
1586 $this->log_message("*** Okay, kill me now!");
\r
1587 return $this->NSLogout();
\r
1589 if (!is_resource($this->NSfp) || feof($this->NSfp))
\r
1591 $this->log_message("*** try to connect to MSN network");
\r
1592 if (!$this->connect($this->user, $this->password))
\r
1594 $this->log_message("!!! Can't connect to server: $this->error");
\r
1595 if(!$this->NSRetryWait($this->retry_wait)) continue;
\r
1597 $this->UpdateContacts();
\r
1598 $this->LastPing=time();
\r
1599 $this->log_message("*** connected, wait for command");
\r
1600 $start_tm = time();
\r
1601 $ping_tm = time();
\r
1602 $aContactList = $this->getMembershipList();
\r
1603 if ($this->update_pending) {
\r
1604 if (is_array($aContactList)) {
\r
1605 $pending = 'Pending';
\r
1606 foreach ($aContactList as $u_domain => $aUserList) {
\r
1607 foreach ($aUserList as $u_name => $aNetworks) {
\r
1608 foreach ($aNetworks as $network => $aData) {
\r
1609 if (isset($aData[$pending])) {
\r
1612 foreach (array('Allow', 'Reverse') as $list) {
\r
1613 if (isset($aData[$list]))
\r
1616 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1617 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1623 $id = $aData[$pending];
\r
1624 // we can delete it from pending now
\r
1625 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1626 unset($aContactList[$u_domain][$u_name][$network][$pending]);
\r
1631 foreach (array('Allow', 'Reverse') as $list) {
\r
1632 if (!isset($aData[$list])) {
\r
1633 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1634 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1646 if (is_array($aContactList)) {
\r
1647 foreach ($aContactList as $u_domain => $aUserList) {
\r
1648 $str = '<d n="'.$u_domain.'">';
\r
1649 $len += strlen($str);
\r
1650 if ($len > 7400) {
\r
1651 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1654 $len = strlen($str);
\r
1657 foreach ($aUserList as $u_name => $aNetworks) {
\r
1658 foreach ($aNetworks as $network => $status) {
\r
1659 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1660 $len += strlen($str);
\r
1661 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1663 if ($len > 7475) {
\r
1665 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1667 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1668 $len = strlen($sList);
\r
1677 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1678 // NS: >>> BLP {id} BL
\r
1679 $this->ns_writeln("BLP $this->id BL");
\r
1680 foreach ($aADL as $str) {
\r
1681 $len = strlen($str);
\r
1682 // NS: >>> ADL {id} {size}
\r
1683 $this->ns_writeln("ADL $this->id $len");
\r
1684 $this->ns_writedata($str);
\r
1686 // NS: >>> PRP {id} MFN name
\r
1687 if ($this->alias == '') $this->alias = $user;
\r
1688 $aliasname = rawurlencode($this->alias);
\r
1689 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1691 //$MsnObj=$this->PhotoStckObj();
\r
1692 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1693 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1694 if($this->PhotoStickerFile!==false)
\r
1695 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1696 // NS: >>> UUX {id} length
\r
1697 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1698 $len = strlen($str);
\r
1699 $this->ns_writeln("UUX $this->id $len");
\r
1700 $this->ns_writedata($str);
\r
1702 $data = $this->ns_readln();
\r
1705 //If No NS Message Process SendMessageFileQueue
\r
1706 if (time()-$this->LastPing > $this->ping_wait)
\r
1709 $this->ns_writeln("PNG");
\r
1710 $this->LastPing = time();
\r
1712 if(count($this->ChildProcess)<$this->MAXChildProcess)
\r
1715 foreach($this->MessageQueue as $User => $Message)
\r
1717 if(!trim($User)) continue;
\r
1718 if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;
\r
1719 if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))
\r
1721 $this->MessageQueue[$User]['XFRSent']=true;
\r
1722 $this->MessageQueue[$User]['ReqTime']=time();
\r
1723 $this->log_message("*** Request SB for $User");
\r
1724 $this->ns_writeln("XFR $this->id SB");
\r
1729 if($this->ProcessSendMessageFileQueue()) continue;
\r
1732 switch (substr($data,0,3))
\r
1735 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
1736 // NS: <<< SBS 0 null
\r
1741 // NS: <<< RFS ???
\r
1742 // refresh ADL, so we re-send it again
\r
1743 if (is_array($aADL)) {
\r
1744 foreach ($aADL as $str) {
\r
1745 $len = strlen($str);
\r
1746 // NS: >>> ADL {id} {size}
\r
1747 $this->ns_writeln("ADL $this->id $len");
\r
1748 $this->ns_writedata($str);
\r
1754 // NS: <<< LST {email} {alias} 11 0
\r
1755 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
1756 @list($u_name, $u_domain) = @explode('@', $email);
\r
1757 if (!isset($aContactList[$u_domain][$u_name][1])) {
\r
1758 $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
1759 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
1764 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
1765 // NS: <<< ADL 0 {size}
\r
1766 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
1767 if (is_numeric($size) && $size > 0)
\r
1769 $data = $this->ns_readdata($size);
\r
1770 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1771 if (is_array($matches) && count($matches) > 0)
\r
1773 $u_domain = $matches[1];
\r
1774 $u_name = $matches[2];
\r
1775 $network = $matches[4];
\r
1776 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1777 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
1780 $this->re_login = false;
\r
1782 foreach (array('Allow', 'Reverse') as $list)
\r
1784 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1786 if ($this->re_login) {
\r
1787 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1790 $aTickets = $this->get_passport_ticket();
\r
1791 if (!$aTickets || !is_array($aTickets)) {
\r
1792 // failed to login? ignore it
\r
1793 $this->log_message("*** can't re-login, something wrong here");
\r
1794 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1797 $this->re_login = true;
\r
1798 $this->ticket = $aTickets;
\r
1799 $this->log_message("**** get new ticket, try it again");
\r
1800 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1802 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1806 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1809 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
1811 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
1812 $len = strlen($str);
\r
1815 $this->log_message("*** someone add us to their list: $data");
\r
1816 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
1821 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
1822 // NS: <<< RML 0 {size}
\r
1823 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
1824 if (is_numeric($size) && $size > 0)
\r
1826 $data = $this->ns_readdata($size);
\r
1827 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1828 if (is_array($matches) && count($matches) > 0)
\r
1830 $u_domain = $matches[1];
\r
1831 $u_name = $matches[2];
\r
1832 $network = $matches[4];
\r
1833 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1835 $aData = $aContactList[$u_domain][$u_name][$network];
\r
1836 foreach ($aData as $list => $id)
\r
1837 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
1838 unset($aContactList[$u_domain][$u_name][$network]);
\r
1839 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
1842 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
1843 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
1846 $this->log_message("*** someone remove us from their list: $data");
\r
1851 // randomly, we get MSG notification from server
\r
1852 // NS: <<< MSG Hotmail Hotmail {size}
\r
1853 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
1854 if (is_numeric($size) && $size > 0) {
\r
1855 $data = $this->ns_readdata($size);
\r
1856 $aLines = @explode("\n", $data);
\r
1860 foreach ($aLines as $line) {
\r
1861 $line = rtrim($line);
\r
1863 if ($line === '') {
\r
1867 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
1868 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
1869 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
1870 // we just need text/x-msmsgsinitialmdatanotification
\r
1871 // or text/x-msmsgsoimnotification
\r
1878 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
1879 $maildata = trim(substr($line, 10));
\r
1884 $this->log_message("*** ingnore MSG for: $line");
\r
1887 if ($maildata == '') {
\r
1888 $this->log_message("*** ingnore MSG not for OIM");
\r
1891 $this->re_login = false;
\r
1892 if (strcasecmp($maildata, 'too-large') == 0) {
\r
1893 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
1894 $maildata = $this->getOIM_maildata();
\r
1895 if ($maildata === false) {
\r
1896 $this->log_message("*** can't get mail-data via SOAP");
\r
1897 // maybe we need to re-login again
\r
1898 $aTickets = $this->get_passport_ticket();
\r
1899 if (!$aTickets || !is_array($aTickets)) {
\r
1900 // failed to login? ignore it
\r
1901 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1904 $this->re_login = true;
\r
1905 $this->ticket = $aTickets;
\r
1906 $this->log_message("**** get new ticket, try it again");
\r
1907 $maildata = $this->getOIM_maildata();
\r
1908 if ($maildata === false) {
\r
1909 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
1914 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
1918 $start = strpos($p, '<M>');
\r
1919 $end = strpos($p, '</M>');
\r
1920 if ($start === false || $end === false || $start > $end) break;
\r
1922 $sOIM = substr($p, $start, $end - $start);
\r
1924 $p = substr($p, $end);
\r
1926 if (count($aOIMs) == 0) {
\r
1927 $this->log_message("*** ingnore empty OIM");
\r
1930 foreach ($aOIMs as $maildata) {
\r
1931 // T: 11 for MSN, 13 for Yahoo
\r
1932 // S: 6 for MSN, 7 for Yahoo
\r
1933 // RT: the datetime received by server
\r
1934 // RS: already read or not
\r
1935 // SZ: size of message
\r
1938 // F: always 00000000-0000-0000-0000-000000000009
\r
1939 // N: sender alias
\r
1940 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
1941 if (count($matches) == 0) {
\r
1942 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
1945 $oim_type = $matches[1];
\r
1946 if ($oim_type = 13)
\r
1950 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
1951 if (count($matches) == 0) {
\r
1952 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
1955 $oim_sender = $matches[1];
\r
1956 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
1957 if (count($matches) == 0) {
\r
1958 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
1961 $oim_msgid = $matches[1];
\r
1962 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
1963 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
1964 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
1965 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
1966 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
1967 $sMsg = $this->getOIM_message($oim_msgid);
\r
1968 if ($sMsg === false) {
\r
1969 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
1970 if ($this->re_login) {
\r
1971 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1974 $aTickets = $this->get_passport_ticket();
\r
1975 if (!$aTickets || !is_array($aTickets)) {
\r
1976 // failed to login? ignore it
\r
1977 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1980 $this->re_login = true;
\r
1981 $this->ticket = $aTickets;
\r
1982 $this->log_message("**** get new ticket, try it again");
\r
1983 $sMsg = $this->getOIM_message($oim_msgid);
\r
1984 if ($sMsg === false) {
\r
1985 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1989 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
1991 $this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
1997 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
1998 // NS: <<< UBM {email} $network $type {size}
\r
1999 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
2000 if (is_numeric($size) && $size > 0)
\r
2002 $data = $this->ns_readdata($size);
\r
2003 $aLines = @explode("\n", $data);
\r
2007 foreach ($aLines as $line) {
\r
2008 $line = rtrim($line);
\r
2010 if ($line === '') {
\r
2014 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2020 $aSubLines = @explode("\r", $line);
\r
2021 foreach ($aSubLines as $str) {
\r
2029 $this->log_message("*** ingnore from $from_email: $line");
\r
2032 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
2033 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2038 // randomly, we get UBX notification from server
\r
2039 // NS: <<< UBX email {network} {size}
\r
2040 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
2041 // we don't need the notification data, so just ignore it
\r
2042 if (is_numeric($size) && $size > 0)
\r
2043 $this->ns_readdata($size);
\r
2047 // randomly, we'll get challenge from server
\r
2048 // NS: <<< CHL 0 {code}
\r
2049 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
2050 $fingerprint = $this->getChallenge($chl_code);
\r
2051 // NS: >>> QRY {id} {product_id} 32
\r
2052 // NS: >>> fingerprint
\r
2053 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
2054 $this->ns_writedata($fingerprint);
\r
2055 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2056 if($this->PhotoStickerFile!==false)
\r
2057 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2060 // NS: <<< CHG {id} {status} {code}
\r
2062 // change our status to online first
\r
2066 // sometimes, NS will redirect to another NS
\r
2068 // NS: <<< XFR {id} NS {server} 0 {server}
\r
2070 // NS: <<< XFR {id} NS {server} U D
\r
2071 // for normal switchboard XFR
\r
2072 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
2073 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
2074 @list($ip, $port) = @explode(':', $server);
\r
2075 if ($server_type != 'SB') {
\r
2077 // this connection will close after XFR
\r
2078 $this->NSLogout();
\r
2081 if(count($this->MessageQueue))
\r
2083 foreach($this->MessageQueue as $User => $Message)
\r
2085 //$this->ChildProcess[$ChildPid]
\r
2086 $this->log_message("*** XFR SB $User");
\r
2087 $pid=pcntl_fork();
\r
2091 $this->ChildProcess[$pid]=$User;
\r
2096 $this->log_message("*** Fork Error $User");
\r
2102 $this->log_message("*** Child Process Start for $User");
\r
2103 unset($Message['XFRSent']);
\r
2104 unset($Message['ReqTime']);
\r
2105 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
2106 if ($bSBresult === false)
\r
2108 // error for switchboard
\r
2109 $this->log_message("!!! error for sending message to ".$User);
\r
2114 unset($this->MessageQueue[$User]);
\r
2117 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
2118 if ($bSBresult === false) {
\r
2119 // error for switchboard
\r
2120 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
2121 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
2125 // NS: <<< QNG {time}
\r
2126 @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
2127 if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
2128 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
2129 //Mod by Ricky Set Online
\r
2133 if($this->PhotoStickerFile!==false)
\r
2134 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2136 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2137 // someone is trying to talk to us
\r
2138 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2139 $this->log_message("NS: <<< RNG $data");
\r
2140 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2141 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2142 if($this->IsIgnoreMail($email))
\r
2144 $this->log_message("*** Ignore RNG from $email");
\r
2147 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
2148 $this->addContact($email,1,$email, true);
\r
2149 $pid=pcntl_fork();
\r
2153 $this->ChildProcess[$pid]='RNG';
\r
2158 $this->log_message("*** Fork Error $User");
\r
2164 $this->log_message("*** Ring Child Process Start for $User");
\r
2165 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
2170 // force logout from NS
\r
2171 // NS: <<< OUT xxx
\r
2172 fclose($this->NSfp);
\r
2173 $this->log_message("*** LOGOUT from NS");
\r
2177 $code = substr($data,0,3);
\r
2178 if (is_numeric($code)) {
\r
2179 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2180 $this->debug_message("*** NS: $this->error");
\r
2182 return $this->NsLogout();
\r
2187 return $this->NsLogout();
\r
2190 function getChallenge($code)
\r
2193 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
2194 // Step 1: The MD5 Hash
\r
2195 $md5Hash = md5($code.$this->prod_key);
\r
2196 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2197 for ($i = 0; $i < 4; $i++) {
\r
2198 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
2199 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
2202 // Step 2: A new string
\r
2203 $chl_id = $code.$this->prod_id;
\r
2204 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
2206 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
2207 for ($i = 0; $i < count($aID); $i++) {
\r
2208 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
2209 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
2212 // Step 3: The 64 bit key
\r
2213 $magic_num = 0x0E79A9C1;
\r
2214 $str7f = 0x7FFFFFFF;
\r
2217 for ($i = 0; $i < count($aID); $i += 2) {
\r
2219 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
2220 $temp = bcadd($temp, $high);
\r
2221 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
2222 $temp = bcmod($temp, $str7f);
\r
2224 $high = $aID[$i+1];
\r
2225 $high = bcmod(bcadd($high, $temp), $str7f);
\r
2226 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
2227 $high = bcmod($high, $str7f);
\r
2229 $low = bcadd(bcadd($low, $high), $temp);
\r
2232 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
2233 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
2235 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
2236 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
2237 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
2238 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
2239 // we need integer here
\r
2240 $high = 0+$new_high;
\r
2242 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
2243 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
2244 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
2245 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
2246 // we need integer here
\r
2247 $low = 0+$new_low;
\r
2249 // we just use 32 bits integer, don't need the key, just high/low
\r
2250 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
2252 // Step 4: Using the key
\r
2253 $md5Hash = md5($code.$this->prod_key);
\r
2254 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2257 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
2258 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
2259 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
2260 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
2265 private function getMessage($sMessage, $network = 1)
\r
2267 $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
2268 $msg_header_len = strlen($msg_header);
\r
2269 if ($network == 1)
\r
2270 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
2272 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
2273 $sMessage=str_replace("\r", '', $sMessage);
\r
2274 $msg=substr($sMessage,0,$maxlen);
\r
2275 return $msg_header.$msg;
\r
2279 * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息
\r
2283 private function DoSwitchBoard($Action,$Param)
\r
2285 $SessionEnd=false;
\r
2288 $LastActive=time();
\r
2289 stream_set_timeout($this->SBfp, $this->SBStreamTimeout);
\r
2293 $cki_code=$Param['cki'];
\r
2294 $user=$Param['user'];
\r
2295 $this->SwitchBoardMessageQueue=$Param['Msg'];
\r
2296 // SB: >>> USR {id} {user} {cki}
\r
2297 $this->SB_writeln("USR $this->id $this->user $cki_code");
\r
2298 $this->SwitchBoardSessionUser=$user;
\r
2301 $ticket=$Param['ticket'];
\r
2302 $sid=$Param['sid'];
\r
2303 $user=$Param['user'];
\r
2304 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2305 $this->SB_writeln("ANS $this->id $this->user $ticket $sid");
\r
2306 $this->SwitchBoardSessionUser=$user;
\r
2311 while((!feof($this->SBfp))&&(!$SessionEnd))
\r
2313 $data = $this->SB_readln();
\r
2314 if($this->kill_me)
\r
2316 $this->log_message("*** SB Okay, kill me now!");
\r
2319 if($data === false)
\r
2321 if(time()-$LastActive > $this->SBIdleTimeout)
\r
2323 $this->debug_message("*** SB Idle Timeout!");
\r
2326 if(!$Joined) continue;
\r
2327 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2329 if($Message=='') continue;
\r
2330 $aMessage = $this->getMessage($Message);
\r
2332 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2333 if($MsnObjDefine!=='')
\r
2335 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2336 $len = strlen($SendString);
\r
2337 $this->SB_writeln("MSG $this->id N $len");
\r
2338 $this->SB_writedata($SendString);
\r
2341 $len = strlen($aMessage);
\r
2342 $this->SB_writeln("MSG $this->id N $len");
\r
2343 $this->SB_writedata($aMessage);
\r
2345 $this->SwitchBoardMessageQueue=array();
\r
2346 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2349 $code = substr($data, 0, 3);
\r
2353 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
2354 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
2355 $this->log_message("*** $email join us");
\r
2359 $this->log_message("*** Quit for BYE");
\r
2363 // SB: <<< USR {id} OK {user} {alias}
\r
2364 // we don't need the data, just ignore it
\r
2365 // request user to join this switchboard
\r
2366 // SB: >>> CAL {id} {user}
\r
2367 $this->SB_writeln("CAL $this->id $user");
\r
2370 // SB: <<< CAL {id} RINGING {?}
\r
2371 // we don't need this, just ignore, and wait for other response
\r
2375 // SB: <<< JOI {user} {alias} {clientid?}
\r
2376 // someone join us
\r
2377 // we don't need the data, just ignore it
\r
2378 // no more user here
\r
2382 // SB: <<< MSG {email} {alias} {len}
\r
2383 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
2384 $len = trim($len);
\r
2385 $data = $this->SB_readdata($len);
\r
2386 $aLines = @explode("\n", $data);
\r
2391 foreach ($aLines as $line)
\r
2393 $line = rtrim($line);
\r
2395 if ($line === '') {
\r
2399 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2400 // typing notification, just ignore
\r
2404 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
2405 // we don't handle any split message, just ignore
\r
2409 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
2410 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
2412 $p = strstr($data, "\n\n");
\r
2414 if ($p === false) {
\r
2415 $p = strstr($data, "\r\n\r\n");
\r
2417 $sMsg = substr($p, 4);
\r
2420 $sMsg = substr($p, 2);
\r
2423 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
2424 // ignore all application/x-... message
\r
2426 // application/x-ms-ink => ink message
\r
2430 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
2431 // ignore all text/x-... message
\r
2433 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
2434 // text/x-mms-animemoticon => customized animemotion word
\r
2446 $this->log_message("*** ingnore from $from_email: $line");
\r
2451 // we will ignore any p2p message after sending acknowledgement
\r
2453 $len = strlen($sMsg);
\r
2454 $this->log_message("*** p2p message from $from_email, size $len");
\r
2455 // header = 48 bytes
\r
2456 // content >= 0 bytes
\r
2457 // footer = 4 bytes
\r
2458 // so it need to >= 52 bytes
\r
2459 /*if ($len < 52) {
\r
2460 $this->log_message("*** p2p: size error, less than 52!");
\r
2463 $aDwords = @unpack("V12dword", $sMsg);
\r
2464 if (!is_array($aDwords)) {
\r
2465 $this->log_message("*** p2p: header unpack error!");
\r
2468 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
2469 $hdr_SessionID = $aDwords['dword1'];
\r
2470 $hdr_Identifier = $aDwords['dword2'];
\r
2471 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
2472 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
2473 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
2474 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
2475 $hdr_MessageLength = $aDwords['dword7'];
\r
2476 $hdr_Flag = $aDwords['dword8'];
\r
2477 $hdr_AckID = $aDwords['dword9'];
\r
2478 $hdr_AckUID = $aDwords['dword10'];
\r
2479 $hdr_AckSizeLow = $aDwords['dword11'];
\r
2480 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
2481 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
2482 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
2483 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
2484 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
2485 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
2486 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
2487 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
2488 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
2489 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
2490 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
2491 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
2492 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
2493 if($hdr_Flag==2) {
\r
2494 //This is an ACK from SB ignore....
\r
2495 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
2498 $MsgBody=$this->linetoArray(substr($sMsg,48,-4));
\r
2499 $this->debug_message("*** p2p: body".print_r($MsgBody,true));
\r
2500 if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))
\r
2504 if($this->SB_readln()===false) break;
\r
2506 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));
\r
2507 preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);
\r
2508 $BranchGUID=$Matches[1];
\r
2509 //it's an invite to send a display picture.
\r
2510 $new_id = ~$hdr_Identifier;
\r
2511 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2514 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2519 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2520 $footer = pack("L", 0);
\r
2521 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2522 $len = strlen($message);
\r
2523 $this->SB_writeln("MSG $this->id D $len");
\r
2524 $this->SB_writedata($message);
\r
2525 $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
2526 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
2527 $this->SB_readln();//Read ACK;
\r
2528 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
2530 //Send 200 OK message
\r
2531 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
2533 "MSNSLP/1.0 200 OK\r\n".
\r
2534 "To: <msnmsgr:".$from_email.">\r\n".
\r
2535 "From: <msnmsgr:".$this->user.">\r\n".
\r
2536 "Via: ".$MsgBody['Via']."\r\n".
\r
2537 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
2538 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2539 "Max-Forwards: 0\r\n".
\r
2540 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
2541 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
2543 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2544 $hdr_TotalDataSizeHigh=0;
\r
2545 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2548 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2549 strlen($MessagePayload),
\r
2556 "MIME-Version: 1.0\r\n".
\r
2557 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2558 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2559 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
2560 $this->SB_writedata($message);
\r
2561 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
2562 $this->SB_readln();//Read ACK;
\r
2564 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
2565 //send Data preparation message
\r
2566 //send 4 null bytes as data
\r
2567 $hdr_TotalDataSizeLow=4;
\r
2568 $hdr_TotalDataSizeHigh=0;
\r
2570 $hdr = pack("LLLLLLLLLLLL",
\r
2571 $MsgBody['SessionID'],
\r
2574 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2575 $hdr_TotalDataSizeLow,
\r
2581 "MIME-Version: 1.0\r\n".
\r
2582 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2583 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";
\r
2584 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
2585 $this->SB_writedata($message);
\r
2586 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
2587 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
2588 $this->SB_readln();//Read ACK;
\r
2590 //send Data Content..
\r
2591 $footer=pack('N',1);
\r
2593 $FileSize=filesize($PictureFilePath);
\r
2594 if($hTitle=fopen($PictureFilePath,'rb'))
\r
2598 while(!feof($hTitle))
\r
2600 $FileContent=fread($hTitle,1024);
\r
2601 $FileContentSize=strlen($FileContent);
\r
2602 $hdr = pack("LLLLLLLLLLLL",
\r
2603 $MsgBody['SessionID'],
\r
2614 "MIME-Version: 1.0\r\n".
\r
2615 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2616 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
2617 $this->SB_writeln("MSG $this->id D ".strlen($message));
\r
2618 $this->SB_writedata($message);
\r
2619 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
2620 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
2621 //$this->SB_readln();//Read ACK;
\r
2622 $Offset+=$FileContentSize;
\r
2627 $MessageContent="\r\n".pack("C", 0);
\r
2629 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
2630 "To: <msnmsgr:$from_email>\r\n".
\r
2631 "From: <msnmsgr:".$this->user.">\r\n".
\r
2632 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
2634 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2635 "Max-Forwards: 0\r\n".
\r
2636 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
2637 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
2638 $footer=pack('N',0);
\r
2639 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2640 $hdr_TotalDataSizeHigh=0;
\r
2642 $hdr = pack("LLLLLLLLLLLL",
\r
2646 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2653 "MIME-Version: 1.0\r\n".
\r
2654 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2655 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2656 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2658 $this->SB_writedata($message);
\r
2659 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
2664 //if ($hdr_Flag == 2) {
\r
2665 // just send ACK...
\r
2666 // $this->SB_writeln("ACK $id");
\r
2669 if ($hdr_SessionID == 4) {
\r
2671 $this->debug_message("*** p2p: ignore flag 4");
\r
2674 $finished = false;
\r
2675 if ($hdr_TotalDataSizeHigh == 0) {
\r
2676 // only 32 bites size
\r
2677 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
2681 // we won't accept any file transfer
\r
2682 // so I think we won't get any message size need to use 64 bits
\r
2683 // 64 bits size here, can't count directly...
\r
2684 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
2685 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
2686 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
2687 $now_size = bcadd($dataoffset, $messagelength);
\r
2688 if (bccomp($now_size, $totalsize) >= 0)
\r
2692 // ignore not finished split packet
\r
2693 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
2696 //$new_id = ~$hdr_Identifier;
\r
2699 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2702 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2707 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2708 $footer = pack("L", 0);
\r
2709 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2710 $len = strlen($message);
\r
2711 $this->SB_writeln("MSG $id D $len");
\r
2713 $this->SB_writedata($message);
\r
2714 $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
2715 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2719 $this->log_message("*** MSG from $from_email: $sMsg");
\r
2720 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2723 $this->log_message("*** User $user is offline. Try OIM.");
\r
2724 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2725 $this->SendMessage($Message,"$user@Offline");
\r
2729 if (is_numeric($code))
\r
2731 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2732 $this->debug_message("*** SB: $this->error");
\r
2737 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2739 if (feof($this->SBfp))
\r
2741 // lost connection? error? try OIM later
\r
2742 @fclose($this->SBfp);
\r
2745 $this->SB_writeln("OUT");
\r
2746 @fclose($this->SBfp);
\r
2749 /*private function switchboard_control($ip, $port, $cki_code, $user, $Messages)
\r
2751 $this->SwitchBoardProcess=1;
\r
2752 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2753 $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2756 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2759 return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));
\r
2761 private function switchboard_ring($ip, $port, $sid, $ticket,$user)
\r
2763 $this->SwitchBoardProcess=2;
\r
2764 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2765 $this->SBfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2768 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2771 return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));
\r
2774 // read data for specified size
\r
2775 private function ns_readdata($size) {
\r
2778 while (!feof($this->NSfp)) {
\r
2779 $buf = @fread($this->NSfp, $size - $count);
\r
2781 $count += strlen($buf);
\r
2782 if ($count >= $size) break;
\r
2784 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2789 private function ns_readln() {
\r
2790 $data = @fgets($this->NSfp, 4096);
\r
2791 if ($data !== false) {
\r
2792 $data = trim($data);
\r
2793 $this->debug_message("NS: <<< $data");
\r
2798 // write to server, append \r\n, also increase id
\r
2799 private function ns_writeln($data) {
\r
2800 @fwrite($this->NSfp, $data."\r\n");
\r
2801 $this->debug_message("NS: >>> $data");
\r
2806 // write data to server
\r
2807 private function ns_writedata($data) {
\r
2808 @fwrite($this->NSfp, $data);
\r
2809 $this->debug_message("NS: >>> $data");
\r
2813 // read data for specified size for SB
\r
2814 private function sb_readdata($socket, $size) {
\r
2817 while (!feof($this->SBfp)) {
\r
2818 $buf = @fread($this->SBfp, $size - $count);
\r
2820 $count += strlen($buf);
\r
2821 if ($count >= $size) break;
\r
2823 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2827 // read one line for SB
\r
2828 private function sb_readln($socket) {
\r
2829 $data = @fgets($socket, 4096);
\r
2830 if ($data !== false) {
\r
2831 $data = trim($data);
\r
2832 $this->debug_message("SB: <<< $data");
\r
2837 // write to server for SB, append \r\n, also increase id
\r
2838 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
2839 private function sb_writeln($socket, &$id, $data) {
\r
2840 @fwrite($socket, $data."\r\n");
\r
2841 $this->debug_message("SB: >>> $data");
\r
2846 // write data to server
\r
2847 private function sb_writedata($socket, $data) {
\r
2848 @fwrite($socket, $data);
\r
2849 $this->debug_message("SB: >>> $data");
\r
2853 // show debug information
\r
2854 function debug_message($str) {
\r
2855 if (!$this->debug) return;
\r
2856 if($this->debug===STDOUT) echo $str."\n";
\r
2857 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
2858 $fp = fopen($fname, 'at');
\r
2860 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2864 // still show debug information, if we can't open log_file
\r
2869 function dump_binary($str) {
\r
2873 $len = strlen($str);
\r
2874 for ($i = 0; $i < $len; $i++) {
\r
2875 if (($i % 16) == 0) {
\r
2876 if ($buf !== '') {
\r
2877 $buf .= "$h_str $a_str\n";
\r
2879 $buf .= sprintf("%04X:", $i);
\r
2883 $ch = ord($str[$i]);
\r
2887 $a_str .= chr($ch);
\r
2888 $h_str .= sprintf(" %02X", $ch);
\r
2890 if ($h_str !== '')
\r
2891 $buf .= "$h_str $a_str\n";
\r
2896 function log_message($str) {
\r
2897 /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';
\r
2898 $fp = fopen($fname, 'at');
\r
2900 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2903 $this->debug_message($str);
\r
2908 * @param $FilePath 圖檔路徑
\r
2909 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
2912 private function MsnObj($FilePath,$Type=3)
\r
2914 if(!($FileSize=filesize($FilePath))) return '';
\r
2915 $Location=md5($FilePath);
\r
2916 $Friendly=md5($FilePath.$Type);
\r
2917 if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
2918 $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));
\r
2919 $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
2920 $this->MsnObjArray[$Location]=$FilePath;
\r
2921 $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
2922 $this->MsnObjMap[$Location]=$MsnObj;
\r
2923 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
2927 private function linetoArray($lines) {
\r
2928 $lines=str_replace("\r",'',$lines);
\r
2929 $lines=explode("\n",$lines);
\r
2930 foreach($lines as $line) {
\r
2931 if(!isset($line{3})) continue;
\r
2932 list($Key,$Val)=explode(':',$line);
\r
2933 $Data[trim($Key)]=trim($Val);
\r
2938 private function GetPictureFilePath($Context)
\r
2940 $MsnObj=base64_decode($Context);
\r
2941 if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))
\r
2942 $location=$Match[1];
\r
2943 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
2944 if($location&&(isset($this->MsnObjArray[$location])))
\r
2945 return $this->MsnObjArray[$location];
\r
2949 private function GetMsnObjDefine($Message)
\r
2952 if(is_array($this->Emotions))
\r
2953 foreach($this->Emotions as $Pattern => $FilePath)
\r
2955 if(strpos($Message,$Pattern)!==false)
\r
2956 $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";
\r
2958 return $DefineString;
\r
2962 * Read and handle incoming command from NS
\r
2964 public function nsReceive() {
\r
2965 // Sign in again if not signed in or socket failed
\r
2966 if (!is_resource($this->NSfp) || feof($this->NSfp)) {
\r
2967 $this->callHandler('Reconnect', NULL);
\r
2972 $data = $this->ns_readln();
\r
2973 if($data === false) {
\r
2974 // There was no data / an error when reading from the socket so reconnect
\r
2975 $this->callHandler('Reconnect', NULL);
\r
2978 switch (substr($data,0,3))
\r
2981 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
2982 // NS: <<< SBS 0 null
\r
2987 // NS: <<< RFS ???
\r
2988 // refresh ADL, so we re-send it again
\r
2989 if (is_array($this->aADL)) {
\r
2990 foreach ($this->aADL as $str) {
\r
2991 $len = strlen($str);
\r
2992 // NS: >>> ADL {id} {size}
\r
2993 $this->ns_writeln("ADL $this->id $len");
\r
2994 $this->ns_writedata($str);
\r
3000 // NS: <<< LST {email} {alias} 11 0
\r
3001 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
3002 @list($u_name, $u_domain) = @explode('@', $email);
\r
3003 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
3004 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
3005 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
3010 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
3011 // NS: <<< ADL 0 {size}
\r
3012 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
3013 if (is_numeric($size) && $size > 0)
\r
3015 $data = $this->ns_readdata($size);
\r
3016 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3017 if (is_array($matches) && count($matches) > 0)
\r
3019 $u_domain = $matches[1];
\r
3020 $u_name = $matches[2];
\r
3021 $network = $matches[4];
\r
3022 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3023 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
3026 $this->re_login = false;
\r
3028 foreach (array('Allow', 'Reverse') as $list)
\r
3030 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3032 if ($this->re_login) {
\r
3033 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3036 $aTickets = $this->get_passport_ticket();
\r
3037 if (!$aTickets || !is_array($aTickets)) {
\r
3038 // failed to login? ignore it
\r
3039 $this->log_message("*** can't re-login, something wrong here");
\r
3040 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3043 $this->re_login = true;
\r
3044 $this->ticket = $aTickets;
\r
3045 $this->log_message("**** get new ticket, try it again");
\r
3046 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3048 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3052 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3055 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
3057 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
3058 $len = strlen($str);
\r
3061 $this->log_message("*** someone add us to their list: $data");
\r
3062 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
3067 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
3068 // NS: <<< RML 0 {size}
\r
3069 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
3070 if (is_numeric($size) && $size > 0)
\r
3072 $data = $this->ns_readdata($size);
\r
3073 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3074 if (is_array($matches) && count($matches) > 0)
\r
3076 $u_domain = $matches[1];
\r
3077 $u_name = $matches[2];
\r
3078 $network = $matches[4];
\r
3079 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3081 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
3082 foreach ($aData as $list => $id)
\r
3083 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
3084 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
3085 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
3088 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
3089 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
3092 $this->log_message("*** someone remove us from their list: $data");
\r
3097 // randomly, we get MSG notification from server
\r
3098 // NS: <<< MSG Hotmail Hotmail {size}
\r
3099 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
3100 if (is_numeric($size) && $size > 0) {
\r
3101 $data = $this->ns_readdata($size);
\r
3102 $aLines = @explode("\n", $data);
\r
3106 foreach ($aLines as $line) {
\r
3107 $line = rtrim($line);
\r
3109 if ($line === '') {
\r
3113 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
3114 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
3115 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
3116 // we just need text/x-msmsgsinitialmdatanotification
\r
3117 // or text/x-msmsgsoimnotification
\r
3124 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
3125 $maildata = trim(substr($line, 10));
\r
3130 $this->log_message("*** ingnore MSG for: $line");
\r
3133 if ($maildata == '') {
\r
3134 $this->log_message("*** ingnore MSG not for OIM");
\r
3137 $this->re_login = false;
\r
3138 if (strcasecmp($maildata, 'too-large') == 0) {
\r
3139 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
3140 $maildata = $this->getOIM_maildata();
\r
3141 if ($maildata === false) {
\r
3142 $this->log_message("*** can't get mail-data via SOAP");
\r
3143 // maybe we need to re-login again
\r
3144 $aTickets = $this->get_passport_ticket();
\r
3145 if (!$aTickets || !is_array($aTickets)) {
\r
3146 // failed to login? ignore it
\r
3147 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3150 $this->re_login = true;
\r
3151 $this->ticket = $aTickets;
\r
3152 $this->log_message("**** get new ticket, try it again");
\r
3153 $maildata = $this->getOIM_maildata();
\r
3154 if ($maildata === false) {
\r
3155 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
3160 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
3164 $start = strpos($p, '<M>');
\r
3165 $end = strpos($p, '</M>');
\r
3166 if ($start === false || $end === false || $start > $end) break;
\r
3168 $sOIM = substr($p, $start, $end - $start);
\r
3170 $p = substr($p, $end);
\r
3172 if (count($aOIMs) == 0) {
\r
3173 $this->log_message("*** ingnore empty OIM");
\r
3176 foreach ($aOIMs as $maildata) {
\r
3177 // T: 11 for MSN, 13 for Yahoo
\r
3178 // S: 6 for MSN, 7 for Yahoo
\r
3179 // RT: the datetime received by server
\r
3180 // RS: already read or not
\r
3181 // SZ: size of message
\r
3184 // F: always 00000000-0000-0000-0000-000000000009
\r
3185 // N: sender alias
\r
3186 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
3187 if (count($matches) == 0) {
\r
3188 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
3191 $oim_type = $matches[1];
\r
3192 if ($oim_type = 13)
\r
3196 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
3197 if (count($matches) == 0) {
\r
3198 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
3201 $oim_sender = $matches[1];
\r
3202 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
3203 if (count($matches) == 0) {
\r
3204 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
3207 $oim_msgid = $matches[1];
\r
3208 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
3209 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
3210 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
3211 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
3212 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
3213 $sMsg = $this->getOIM_message($oim_msgid);
\r
3214 if ($sMsg === false) {
\r
3215 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
3216 if ($this->re_login) {
\r
3217 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3220 $aTickets = $this->get_passport_ticket();
\r
3221 if (!$aTickets || !is_array($aTickets)) {
\r
3222 // failed to login? ignore it
\r
3223 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3226 $this->re_login = true;
\r
3227 $this->ticket = $aTickets;
\r
3228 $this->log_message("**** get new ticket, try it again");
\r
3229 $sMsg = $this->getOIM_message($oim_msgid);
\r
3230 if ($sMsg === false) {
\r
3231 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3235 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
3237 //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
3238 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
3244 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
3245 // NS: <<< UBM {email} $network $type {size}
\r
3246 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
3247 if (is_numeric($size) && $size > 0)
\r
3249 $data = $this->ns_readdata($size);
\r
3250 $aLines = @explode("\n", $data);
\r
3254 foreach ($aLines as $line) {
\r
3255 $line = rtrim($line);
\r
3257 if ($line === '') {
\r
3261 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
3267 $aSubLines = @explode("\r", $line);
\r
3268 foreach ($aSubLines as $str) {
\r
3276 $this->log_message("*** ingnore from $from_email: $line");
\r
3279 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
3280 //$this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
3281 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
3286 // randomly, we get UBX notification from server
\r
3287 // NS: <<< UBX email {network} {size}
\r
3288 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
3289 // we don't need the notification data, so just ignore it
\r
3290 if (is_numeric($size) && $size > 0)
\r
3291 $this->ns_readdata($size);
\r
3295 // randomly, we'll get challenge from server
\r
3296 // NS: <<< CHL 0 {code}
\r
3297 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
3298 $fingerprint = $this->getChallenge($chl_code);
\r
3299 // NS: >>> QRY {id} {product_id} 32
\r
3300 // NS: >>> fingerprint
\r
3301 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
3302 $this->ns_writedata($fingerprint);
\r
3303 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3304 if($this->PhotoStickerFile!==false)
\r
3305 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3308 // NS: <<< CHG {id} {status} {code}
\r
3310 // change our status to online first
\r
3314 // sometimes, NS will redirect to another NS
\r
3316 // NS: <<< XFR {id} NS {server} 0 {server}
\r
3318 // NS: <<< XFR {id} NS {server} U D
\r
3319 // for normal switchboard XFR
\r
3320 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
3321 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
3322 @list($ip, $port) = @explode(':', $server);
\r
3323 if ($server_type != 'SB') {
\r
3325 // this connection will close after XFR
\r
3326 $this->NSLogout();
\r
3329 if(count($this->MessageQueue))
\r
3331 foreach($this->MessageQueue as $User => $Message)
\r
3333 //$this->ChildProcess[$ChildPid]
\r
3334 $this->log_message("*** XFR SB $User");
\r
3335 $pid=pcntl_fork();
\r
3339 $this->ChildProcess[$pid]=$User;
\r
3344 $this->log_message("*** Fork Error $User");
\r
3350 $this->log_message("*** Child Process Start for $User");
\r
3351 unset($Message['XFRSent']);
\r
3352 unset($Message['ReqTime']);
\r
3353 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
3354 if ($bSBresult === false)
\r
3356 // error for switchboard
\r
3357 $this->log_message("!!! error for sending message to ".$User);
\r
3362 unset($this->MessageQueue[$User]);
\r
3365 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
3366 if ($bSBresult === false) {
\r
3367 // error for switchboard
\r
3368 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
3369 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
3373 // NS: <<< QNG {time}
\r
3374 @list(/* QNG */, $ping_wait) = @explode(' ', $data);
\r
3375 //if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
3376 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
3377 //Mod by Ricky Set Online
\r
3379 $this->callHandler('Pong', $ping_wait);
\r
3383 if($this->PhotoStickerFile!==false)
\r
3384 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3386 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3387 // someone is trying to talk to us
\r
3388 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
3389 $this->log_message("NS: <<< RNG $data");
\r
3390 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
3391 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
3392 if($this->IsIgnoreMail($email))
\r
3394 $this->log_message("*** Ignore RNG from $email");
\r
3397 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
3398 $this->addContact($email,1,$email, true);
\r
3399 $pid=pcntl_fork();
\r
3403 $this->ChildProcess[$pid]='RNG';
\r
3408 $this->log_message("*** Fork Error $User");
\r
3414 $this->log_message("*** Ring Child Process Start for $User");
\r
3415 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
3420 // force logout from NS
\r
3421 // NS: <<< OUT xxx
\r
3422 $this->log_message("*** LOGOUT from NS");
\r
3423 return $this->NsLogout();
\r
3426 $code = substr($data,0,3);
\r
3427 if (is_numeric($code)) {
\r
3428 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
3429 $this->debug_message("*** NS: $this->error");
\r
3431 return $this->NsLogout();
\r
3439 * Read and handle incoming command/message from
\r
3440 * a switchboard session socket
\r
3442 public function sbReceive() {
\r
3447 * Send a request for a switchboard session
\r
3448 * @param $to Target email for switchboard session
\r
3450 private function reqSBSession($to) {
\r
3451 $this->log_message("*** Request SB for $to");
\r
3452 $this->ns_writeln("XFR $this->id SB");
\r
3454 // Add to the queue of those waiting for a switchboard session reponse
\r
3455 $this->switchBoardSessions[$to] = array('socket' => NULL, 'id' => 1, 'lastActive' => NULL, 'joined' => false, 'XFRReqTime' => time());
\r
3456 $this->waitingForXFR[] = &$this->switchBoardSessions[$to];
\r
3460 * Following an XFR or RNG, connect to the switchboard session
\r
3461 * @param $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case or RNG)
\r
3462 * @param $ip IP of Switchboard
\r
3463 * @param $port Port of Switchboard
\r
3464 * @param $to User on other end of Switchboard
\r
3465 * @param $param Array of parameters - 'cki', 'ticket', 'sid'
\r
3466 * @return Whether successful
\r
3468 private function connectToSBSession($mode, $ip, $port, $to, $param) {
\r
3469 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
3471 $this->switchBoardSessions[$to]['socket'] = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
3472 $socket = $this->switchBoardSessions[$to]['socket'];
\r
3474 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
3477 $this->switchBoardSockets[$socket] = $socket;
\r
3479 stream_set_timeout($socket, $this->SBStreamTimeout);
\r
3481 $id = &$this->switchBoardSessions[$to]['id'];
\r
3483 if($mode == 'Active') {
\r
3484 $cki_code = $param['cki'];
\r
3486 // SB: >>> USR {id} {user} {cki}
\r
3487 $this->sb_writeln($socket, $id, "USR $id $this->user $cki_code");
\r
3490 $ticket = $param['ticket'];
\r
3491 $sid = $param['sid'];
\r
3493 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
3494 $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");
\r
3497 $this->switchBoardSessions[$to]['lastActive'] = time();
\r
3501 * Send a message via an existing SB session
\r
3502 * @param $message Message
\r
3503 * @param $to Recipient for message
\r
3504 * @return Whether successful
\r
3506 private function sendMessageViaSB($message, $to) {
\r
3507 if(socketcheck($this->switchBoardSessions[$to]['socket'])) {
\r
3508 $this->reqSBSession($to);
\r
3512 if(!$this->switchBoardSessions[$to]['joined']) {
\r
3513 // If our participant has not joined the session yet we can't message them!
\r
3517 $id = &$this->switchBoardSessions[$to]['id'];
\r
3518 $socket = $this->switchBoardSessions[$to]['socket'];
\r
3520 $aMessage = $this->getMessage($Message);
\r
3522 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
3523 if($MsnObjDefine !== '')
\r
3525 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
3526 $len = strlen($SendString);
\r
3527 // TODO handle failure during write to socket
\r
3528 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
3529 $this->sb_writedata($socket, $SendString);
\r
3531 $len = strlen($aMessage);
\r
3532 // TODO handle failure during write to socket
\r
3533 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
3534 $this->sb_writedata($socket, $aMessage);
\r
3536 // Don't close the SB session, we might as well leave it open
\r
3544 * @param $sMessage
\r
3547 private function sendOIM($to, $sMessage, $lockkey) {
\r
3548 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
3549 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
3550 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
3551 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
3553 <From memberName="'.$this->user.'"
\r
3554 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
3557 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
3558 msnpVer="'.$this->protocol.'"
\r
3559 buildVer="'.$this->buildver.'"/>
\r
3560 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
3561 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
3562 appid="'.$this->prod_id.'"
\r
3563 lockkey="'.$lockkey.'"
\r
3564 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
3565 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
3566 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
3567 <MessageNumber>1</MessageNumber>
\r
3571 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
3572 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
3573 Content-Type: text/plain; charset=UTF-8
\r
3574 Content-Transfer-Encoding: base64
\r
3575 X-OIM-Message-Type: OfflineMessage
\r
3576 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
3577 X-OIM-Sequence-Num: 1
\r
3579 '.chunk_split(base64_encode($sMessage)).'
\r
3582 </soap:Envelope>';
\r
3584 $header_array = array(
\r
3585 'SOAPAction: '.$this->oim_send_soap,
\r
3586 'Content-Type: text/xml',
\r
3587 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
3590 $this->debug_message("*** URL: $this->oim_send_url");
\r
3591 $this->debug_message("*** Sending SOAP:\n$XML");
\r
3592 $curl = curl_init();
\r
3593 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
3594 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
3595 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
3596 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
3597 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
3598 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
3599 curl_setopt($curl, CURLOPT_POST, 1);
\r
3600 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
3601 $data = curl_exec($curl);
\r
3602 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
3603 curl_close($curl);
\r
3604 $this->debug_message("*** Get Result:\n$data");
\r
3606 if ($http_code == 200) {
\r
3607 $this->debug_message("*** OIM sent for $to");
\r
3611 $challenge = false;
\r
3612 $auth_policy = false;
\r
3613 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
3614 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
3615 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
3616 if (count($matches) != 0) {
\r
3617 // yes, we get new LockKeyChallenge
\r
3618 $challenge = $matches[2];
\r
3619 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
3621 // auth policy error
\r
3622 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
3623 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
3624 if (count($matches) != 0) {
\r
3625 $auth_policy = $matches[2];
\r
3626 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
3628 if ($auth_policy === false && $challenge === false) {
\r
3629 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
3630 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
3631 if (count($matches) == 0) {
\r
3632 // no error, we assume the OIM is sent
\r
3633 $this->debug_message("*** OIM sent for $to");
\r
3636 $err_code = $matches[2];
\r
3637 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
3638 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
3639 if (count($matches) > 0)
\r
3640 $err_msg = $matches[1];
\r
3643 $this->debug_message("*** OIM failed for $to");
\r
3644 $this->debug_message("*** OIM Error code: $err_code");
\r
3645 $this->debug_message("*** OIM Error Message: $err_msg");
\r
3648 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
3652 * Send a message to a user on another network
\r
3653 * @param $message Message
\r
3654 * @param $to Intended recipient
\r
3655 * @param $network Network
\r
3657 private function sendOtherNetworkMessage($message, $to, $network) {
\r
3658 $message=$this->getMessage($nessage, $network);
\r
3659 $len = strlen($message);
\r
3660 $this->ns_writeln("UUM $this->id $to $network 1 $len");
\r
3661 $this->ns_writedata($Message);
\r
3662 $this->log_message("*** sent to $to (network: $network):\n$Message");
\r
3667 * @param $message Message
\r
3668 * @param $to To address in form user@host.com@network
\r
3669 * where network is 1 for MSN, 32 for Yahoo
\r
3670 * and 'Offline' for offline messages
\r
3672 public function sendMessage($message, $to) {
\r
3673 if($message != '') {
\r
3674 list($name,$host,$network)=explode('@',$to);
\r
3675 $network=$network==''?1:$network;
\r
3677 if($network === 1 && $this->switchBoardSessions[$to]['socket'] != NULL && time()-$this->switchBoardSessions[$to]['lastActive'] < $this->SBIdleTimeout) {
\r
3678 $recipient = $name . $host;
\r
3679 $this->debug_message("*** Sending Message to $recipient using existing SB session");
\r
3680 return $this->sendMessageViaSB($message, $recipient);
\r
3681 } elseif($network == 'Offline') {
\r
3683 //FIXME: 修正Send OIM
\r
3685 for ($i = 0; $i < $this->oim_try; $i++)
\r
3687 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
3688 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
3689 // need challenge lockkey
\r
3690 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
3691 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
3694 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
3696 if ($this->re_login)
\r
3698 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
3701 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
3702 // maybe we need to re-login again
\r
3703 if(!$this->get_passport_ticket())
\r
3705 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3708 $this->log_message("**** get new ticket, try it again");
\r
3713 $this->debug_message("*** Not MSN network or no existing SB session");
\r
3714 $this->reqSBSession($to);
\r
3721 //FIXME Not sure if this is needed?
\r
3722 private function endSBSession($socket) {
\r
3723 if (feof($socket))
\r
3725 // lost connection? error? try OIM later
\r
3730 $this->sb_writeln($socket, $fake, "OUT");
\r
3736 * Sends a ping command
\r
3738 * Should be called about every 50 seconds
\r
3740 public function sendPing() {
\r
3742 $this->ns_writeln("PNG");
\r
3746 * Get the NS socket
\r
3748 public function getNSSocket() {
\r
3749 return $this->NSfp;
\r
3753 * Get the Switchboard sockets currently in use
\r
3755 public function getSBSockets() {
\r
3756 return $this->switchBoardSockets;
\r
3760 * Get all the sockets currently in use
\r
3762 public function getSockets() {
\r
3763 return array_merge($this->NSfp, $this->switchBoardSockets);
\r
3767 * Checks socket for end of file
\r
3770 * @param Resource $socket Socket to check
\r
3771 * @return boolean true if end of file (socket)
\r
3773 private static function socketcheck($socket){
\r
3774 $info = stream_get_meta_data($socket);
\r
3775 return $info['eof'];
\r
3779 * Calls User Handler
\r
3781 * Calls registered handler for a specific event.
\r
3783 * @param String $event Command (event) name (Rvous etc)
\r
3784 * @param String $data Raw message from server
\r
3785 * @see registerHandler
\r
3788 private function callHandler($event, $data) {
\r
3789 if (isset($this->myEventHandlers[$event])) {
\r
3790 call_user_func($this->myEventHandlers[$event], $data);
\r
3795 * Registers a user handler
\r
3798 * IMIn, Pong, ConnectFailed, Reconnect
\r
3800 * @param String $event Event name
\r
3801 * @param String $handler User function to call
\r
3802 * @see callHandler
\r
3803 * @return boolean Returns true if successful
\r
3805 public function registerHandler($event, $handler) {
\r
3806 if (is_callable($handler)) {
\r
3807 $this->myEventHandlers[$event] = $handler;
\r