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
29 private $kill_me = false;
\r
33 private $password = '';
\r
34 private $NSfp=false;
\r
36 private $passport_policy = '';
\r
40 private $retry_wait;
\r
41 private $backup_file;
\r
42 private $update_pending;
\r
43 private $PhotoStickerFile=false;
\r
44 private $Emotions=false;
\r
45 private $MessageQueue=array();
\r
46 private $ChildProcess=array();
\r
47 private $MAXChildProcess=3;
\r
48 private $ReqSBXFRTimeout=60;
\r
49 private $SBTimeout=2;
\r
51 private $ping_wait=50;
\r
52 private $SBIdleTimeout=10;
\r
53 private $SBStreamTimeout=10;
\r
54 private $NSStreamTimeout=2;
\r
55 private $MsnObjArray=array();
\r
56 private $MsnObjMap=array();
\r
57 private $SwitchBoardProcess=false; // false=>Main Process,1 => sb_control_process,2 => sb_ring_process
\r
58 private $SwitchBoardSessionUser=false;
\r
59 private $SwitchBoardMessageQueue=array();
\r
60 private $ABAuthHeader;
\r
64 public $server = 'messenger.hotmail.com';
\r
65 public $port = 1863;
\r
68 public $clientid = '';
\r
70 public $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
71 public $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
\r
72 public $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
73 public $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
\r
74 public $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
75 public $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
\r
77 public $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
78 public $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';
\r
80 public $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
81 public $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';
\r
83 public $addcontact_url = 'https://contacts.msn.com/abservice/abservice.asmx';
\r
84 public $addcontact_soap = 'http://www.msn.com/webservices/AddressBook/ABContactAdd';
\r
86 public $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
87 public $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
\r
92 public $authed = false;
\r
94 public $oim_try = 3;
\r
96 public $log_file = '';
\r
98 public $log_path = false;
\r
100 public $font_fn = 'Arial';
\r
101 public $font_co = '333333';
\r
102 public $font_ef = '';
\r
105 // the message length (include header) is limited (maybe since WLM 8.5 released)
\r
106 // for WLM: 1664 bytes
\r
107 // for YIM: 518 bytes
\r
108 public $max_msn_message_len = 1664;
\r
109 public $max_yahoo_message_len = 518;
\r
111 // Begin added for StatusNet
\r
113 private $aContactList = array();
\r
114 private $switchBoardSessions = array();
\r
117 * Event Handler Functions
\r
119 private $myEventHandlers = array();
\r
121 // End added for StatusNet
\r
123 private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)
\r
126 foreach($Array as $Key => $Val)
\r
128 if($Key{0}==':') continue;
\r
130 if(is_array($Val[':']))
\r
132 foreach($Val[':'] as $AttribName => $AttribVal)
\r
133 $Attrib.=" $AttribName='$AttribVal'";
\r
138 $Key=substr($Key,1);
\r
139 foreach($Val as $ListKey => $ListVal)
\r
141 if($ListKey{0}==':') continue;
\r
142 if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);
\r
143 elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';
\r
144 $ArrayString.="<$Key$Attrib>$ListVal</$Key>";
\r
148 if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);
\r
149 elseif(is_bool($Val)) $Val=$Val?'true':'false';
\r
150 $ArrayString.="<$Key$Attrib>$Val</$Key>";
\r
152 if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);
\r
153 return $ArrayString;
\r
156 public function End()
\r
158 $this->log_message("*** someone kill me ***");
\r
159 $this->kill_me=true;
\r
161 public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)
\r
163 $this->user = $Configs['user'];
\r
164 $this->password = $Configs['password'];
\r
165 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
166 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
167 $my_add_function = isset($Configs['add_user_function']) ? $Configs['add_user_function'] : false;
\r
168 $my_rem_function = isset($Configs['remove_user_function']) ? $Configs['remove_user_function'] : false;
\r
169 $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;
\r
170 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
171 $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;
\r
172 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
173 $this->PhotoStickerFile=$Configs['PhotoSticker'];
\r
174 if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)
\r
176 foreach($this->Emotions as $EmotionFilePath)
\r
177 $this->MsnObj($EmotionFilePath,$Type=2);
\r
179 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
180 $this->timeout = $timeout;
\r
182 if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");
\r
183 if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");
\r
184 if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");
\r
186 if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");
\r
187 if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");
\r
190 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
192 normal MSN 8.1 clientid is:
\r
193 01110110 01001100 11000000 00101100
\r
196 we just use following:
\r
197 * 0x04: Your client can send/receive Ink (GIF format)
\r
198 * 0x08: Your client can send/recieve Ink (ISF format)
\r
199 * 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
200 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
203 $this->clientid = $client_id;
\r
204 $this->windows =(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
\r
205 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
208 private function get_passport_ticket($url = '')
\r
210 $user = $this->user;
\r
211 $password = htmlspecialchars($this->password);
\r
214 $passport_url = $this->passport_url;
\r
216 $passport_url = $url;
\r
218 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
219 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
220 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
221 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
222 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
223 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
224 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
225 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
226 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
228 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
229 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
230 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
231 <ps:UIVersion>1</ps:UIVersion>
\r
232 <ps:Cookies></ps:Cookies>
\r
233 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
236 <wsse:UsernameToken Id="user">
\r
237 <wsse:Username>'.$user.'</wsse:Username>
\r
238 <wsse:Password>'.$password.'</wsse:Password>
\r
239 </wsse:UsernameToken>
\r
243 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
244 <wst:RequestSecurityToken Id="RST0">
\r
245 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
247 <wsa:EndpointReference>
\r
248 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
249 </wsa:EndpointReference>
\r
251 </wst:RequestSecurityToken>
\r
252 <wst:RequestSecurityToken Id="RST1">
\r
253 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
255 <wsa:EndpointReference>
\r
256 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
257 </wsa:EndpointReference>
\r
259 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
260 </wst:RequestSecurityToken>
\r
261 <wst:RequestSecurityToken Id="RST2">
\r
262 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
264 <wsa:EndpointReference>
\r
265 <wsa:Address>messenger.msn.com</wsa:Address>
\r
266 </wsa:EndpointReference>
\r
268 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
269 </wst:RequestSecurityToken>
\r
270 <wst:RequestSecurityToken Id="RST3">
\r
271 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
273 <wsa:EndpointReference>
\r
274 <wsa:Address>contacts.msn.com</wsa:Address>
\r
275 </wsa:EndpointReference>
\r
277 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
278 </wst:RequestSecurityToken>
\r
279 <wst:RequestSecurityToken Id="RST4">
\r
280 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
282 <wsa:EndpointReference>
\r
283 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
284 </wsa:EndpointReference>
\r
286 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
287 </wst:RequestSecurityToken>
\r
288 <wst:RequestSecurityToken Id="RST5">
\r
289 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
291 <wsa:EndpointReference>
\r
292 <wsa:Address>spaces.live.com</wsa:Address>
\r
293 </wsa:EndpointReference>
\r
295 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
296 </wst:RequestSecurityToken>
\r
297 <wst:RequestSecurityToken Id="RST6">
\r
298 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
300 <wsa:EndpointReference>
\r
301 <wsa:Address>storage.msn.com</wsa:Address>
\r
302 </wsa:EndpointReference>
\r
304 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
305 </wst:RequestSecurityToken>
\r
306 </ps:RequestMultipleSecurityTokens>
\r
310 $this->debug_message("*** URL: $passport_url");
\r
311 $this->debug_message("*** Sending SOAP:\n$XML");
\r
312 $curl = curl_init();
\r
313 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
314 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
315 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
316 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
317 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
318 curl_setopt($curl, CURLOPT_POST, 1);
\r
319 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
320 $data = curl_exec($curl);
\r
321 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
323 $this->debug_message("*** Get Result:\n$data");
\r
325 if ($http_code != 200) {
\r
326 // sometimes, rediret to another URL
\r
328 //<faultcode>psf:Redirect</faultcode>
\r
329 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
330 //<faultstring>Authentication Failure</faultstring>
\r
331 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
332 $this->debug_message("*** Can't get passport ticket! http code = $http_code");
\r
335 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
336 if (count($matches) == 0) {
\r
337 $this->debug_message("*** redirect, but can't get redirect URL!");
\r
340 $redirect_url = $matches[1];
\r
341 if ($redirect_url == $passport_url) {
\r
342 $this->debug_message("*** redirect, but redirect to same URL!");
\r
345 $this->debug_message("*** redirect to $redirect_url");
\r
346 return $this->get_passport_ticket($redirect_url);
\r
349 // sometimes, rediret to another URL, also return 200
\r
351 //<faultcode>psf:Redirect</faultcode>
\r
352 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
353 //<faultstring>Authentication Failure</faultstring>
\r
354 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
355 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
356 if (count($matches) != 0) {
\r
357 $redirect_url = $matches[1];
\r
358 if ($redirect_url == $passport_url) {
\r
359 $this->debug_message("*** redirect, but redirect to same URL!");
\r
362 $this->debug_message("*** redirect to $redirect_url");
\r
363 return $this->get_passport_ticket($redirect_url);
\r
367 // no Redurect faultcode or URL
\r
368 // we should get the ticket here
\r
370 // we need ticket and secret code
\r
371 // RST1: messengerclear.live.com
\r
372 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
373 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
374 // RST2: messenger.msn.com
\r
375 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
376 // RST3: contacts.msn.com
\r
377 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
378 // RST4: messengersecure.live.com
\r
379 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
380 // RST5: spaces.live.com
\r
381 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
382 // RST6: storage.msn.com
\r
383 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
385 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
386 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
387 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
388 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
389 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
390 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
391 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
395 // no ticket found!
\r
396 if (count($matches) == 0) {
\r
397 $this->debug_message("*** Can't get passport ticket!");
\r
401 //$this->debug_message(var_export($matches, true));
\r
402 // matches[0]: all data
\r
403 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
405 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
407 // matches[5]: RST2 (messenger.msn.com) ticket
\r
409 // matches[7]: RST3 (contacts.msn.com) ticket
\r
411 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
412 // matches[10]: ...
\r
413 // matches[11]: RST5 (spaces.live.com) ticket
\r
414 // matches[12]: ...
\r
415 // matches[13]: RST6 (storage.live.com) ticket
\r
416 // matches[14]: ...
\r
419 // ticket => $matches[1]
\r
420 // secret => $matches[3]
\r
421 // web_ticket => $matches[5]
\r
422 // contact_ticket => $matches[7]
\r
423 // oim_ticket => $matches[9]
\r
424 // space_ticket => $matches[11]
\r
425 // storage_ticket => $matches[13]
\r
427 // yes, we get ticket
\r
429 'ticket' => html_entity_decode($matches[1]),
\r
430 'secret' => html_entity_decode($matches[3]),
\r
431 'web_ticket' => html_entity_decode($matches[5]),
\r
432 'contact_ticket' => html_entity_decode($matches[7]),
\r
433 'oim_ticket' => html_entity_decode($matches[9]),
\r
434 'space_ticket' => html_entity_decode($matches[11]),
\r
435 'storage_ticket' => html_entity_decode($matches[13])
\r
437 $this->ticket=$aTickets;
\r
438 $this->debug_message(var_export($aTickets, true));
\r
439 $ABAuthHeaderArray=array(
\r
440 'ABAuthHeader'=>array(
\r
441 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
442 'ManagedGroupRequest'=>false,
\r
443 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),
\r
446 $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));
\r
447 file_put_contents('/tmp/STTicket.txt',htmlspecialchars($this->ticket['storage_ticket']));
\r
448 //$this->debug_message("StorageTicket:\n",htmlspecialchars($this->ticket['storage_ticket']));
\r
451 private function UpdateContacts()
\r
453 $ABApplicationHeaderArray=array(
\r
454 'ABApplicationHeader'=>array(
\r
455 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
456 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
457 'IsMigration'=>false,
\r
458 'PartnerScenario'=>'ContactSave'
\r
461 $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
462 $ABFindAllArray=array(
\r
463 'ABFindAll'=>array(
\r
464 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
465 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
467 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',
\r
470 $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');
\r
471 $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));
\r
472 $this->Contacts=array();
\r
475 $this->debug_message("*** Update Contacts...");
\r
476 $Result=$this->ABService->ABFindAll($ABFindAll);
\r
477 $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());
\r
478 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
479 $this->Contacts[$Contact->contactInfo->passportName]=$Contact;
\r
481 catch(Exception $e)
\r
483 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
486 protected function addContact($email, $network, $display = '', $sendADL = false)
\r
488 if ($network != 1) return true;
\r
489 if(isset($this->Contacts[$email])) return true;
\r
491 $ABContactAddArray=array(
\r
492 'ABContactAdd'=>array(
\r
493 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
494 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
497 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
498 'contactInfo'=>array(
\r
499 'contactType'=>'LivePending',
\r
500 'passportName'=>$email,
\r
501 'isMessengerUser'=>true,
\r
502 'MessengerMemberInfo'=>array(
\r
503 'DisplayName'=>$email
\r
509 'EnableAllowListManagement'=>true
\r
513 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
516 $this->debug_message("*** Add Contacts $email...");
\r
517 $this->ABService->ABContactAdd($ABContactAdd);
\r
519 catch(Exception $e)
\r
521 $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
523 if ($sendADL && !feof($this->NSfp)) {
\r
524 @list($u_name, $u_domain) = @explode('@', $email);
\r
525 foreach (array('1', '2') as $l) {
\r
526 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
527 $len = strlen($str);
\r
528 // NS: >>> ADL {id} {size}
\r
529 $this->ns_writeln("ADL $this->id $len");
\r
530 $this->ns_writedata($str);
\r
533 $this->UpdateContacts();
\r
537 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
539 // add contact for WLM
\r
540 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
541 $displayName = htmlspecialchars($display);
\r
544 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
545 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
546 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
547 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
548 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
550 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
551 <ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>
\r
552 <IsMigration>false</IsMigration>
\r
553 <PartnerScenario>ContactSave</PartnerScenario>
\r
554 </ABApplicationHeader>
\r
555 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
556 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
557 <TicketToken>'.$ticket.'</TicketToken>
\r
561 <ABContactAdd xmlns="http://www.msn.com/webservices/AddressBook">
\r
562 <abId>00000000-0000-0000-0000-000000000000</abId>
\r
564 <Contact xmlns="http://www.msn.com/webservices/AddressBook">
\r
566 <contactType>LivePending</contactType>
\r
567 <passportName>'.$user.'</passportName>
\r
568 <isMessengerUser>true</isMessengerUser>
\r
569 <MessengerMemberInfo>
\r
570 <DisplayName>'.$displayName.'</DisplayName>
\r
571 </MessengerMemberInfo>
\r
576 <EnableAllowListManagement>true</EnableAllowListManagement>
\r
582 $header_array = array(
\r
583 'SOAPAction: '.$this->addcontact_soap,
\r
584 'Content-Type: text/xml; charset=utf-8',
\r
585 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
588 $this->debug_message("*** URL: $this->addcontact_url");
\r
589 $this->debug_message("*** Sending SOAP:\n$XML");
\r
590 $curl = curl_init();
\r
591 curl_setopt($curl, CURLOPT_URL, $this->addcontact_url);
\r
592 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
593 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
594 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
595 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
596 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
597 curl_setopt($curl, CURLOPT_POST, 1);
\r
598 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
599 $data = curl_exec($curl);
\r
600 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
602 $this->debug_message("*** Get Result:\n$data");
\r
604 if ($http_code != 200) {
\r
605 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
606 if (count($matches) == 0) {
\r
607 $this->log_message("*** can't add contact (network: $network) $email");
\r
610 $faultcode = trim($matches[1]);
\r
611 $faultstring = trim($matches[2]);
\r
612 $this->log_message("*** can't add contact (network: $network) $email, error code: $faultcode, $faultstring");
\r
615 $this->log_message("*** add contact (network: $network) $email");
\r
616 if ($sendADL && !feof($this->NSfp)) {
\r
617 @list($u_name, $u_domain) = @explode('@', $email);
\r
618 foreach (array('1', '2') as $l) {
\r
619 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
620 $len = strlen($str);
\r
621 // NS: >>> ADL {id} {size}
\r
622 $this->ns_writeln("ADL $this->id $len");
\r
623 $this->ns_writedata($str);
\r
626 $this->UpdateContacts();
\r
630 function delMemberFromList($memberID, $email, $network, $list) {
\r
631 if ($network != 1 && $network != 32) return true;
\r
632 if ($memberID === false) return true;
\r
634 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
636 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
637 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
638 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
639 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
640 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
642 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
643 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
644 <IsMigration>false</IsMigration>
\r
645 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
646 </ABApplicationHeader>
\r
647 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
648 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
649 <TicketToken>'.$ticket.'</TicketToken>
\r
653 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
656 <Type>Messenger</Type>
\r
657 <ForeignId></ForeignId>
\r
661 <MemberRole>'.$list.'</MemberRole>
\r
663 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
664 <Type>Passport</Type>
\r
665 <MembershipId>'.$memberID.'</MembershipId>
\r
666 <State>Accepted</State>
\r
675 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
676 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
677 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
678 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
679 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
681 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
682 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
683 <IsMigration>false</IsMigration>
\r
684 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
685 </ABApplicationHeader>
\r
686 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
687 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
688 <TicketToken>'.$ticket.'</TicketToken>
\r
692 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
695 <Type>Messenger</Type>
\r
696 <ForeignId></ForeignId>
\r
700 <MemberRole>'.$list.'</MemberRole>
\r
702 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
704 <MembershipId>'.$memberID.'</MembershipId>
\r
705 <State>Accepted</State>
\r
714 $header_array = array(
\r
715 'SOAPAction: '.$this->delmember_soap,
\r
716 'Content-Type: text/xml; charset=utf-8',
\r
717 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
720 $this->debug_message("*** URL: $this->delmember_url");
\r
721 $this->debug_message("*** Sending SOAP:\n$XML");
\r
722 $curl = curl_init();
\r
723 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
724 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
725 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
726 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
727 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
728 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
729 curl_setopt($curl, CURLOPT_POST, 1);
\r
730 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
731 $data = curl_exec($curl);
\r
732 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
734 $this->debug_message("*** Get Result:\n$data");
\r
736 if ($http_code != 200) {
\r
737 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
738 if (count($matches) == 0) {
\r
739 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
\r
742 $faultcode = trim($matches[1]);
\r
743 $faultstring = trim($matches[2]);
\r
744 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
745 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
\r
748 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
\r
751 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
\r
755 function addMemberToList($email, $network, $list) {
\r
756 if ($network != 1 && $network != 32) return true;
\r
757 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
761 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
762 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
763 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
764 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
765 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
767 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
768 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
769 <IsMigration>false</IsMigration>
\r
770 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
771 </ABApplicationHeader>
\r
772 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
773 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
774 <TicketToken>'.$ticket.'</TicketToken>
\r
778 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
781 <Type>Messenger</Type>
\r
782 <ForeignId></ForeignId>
\r
786 <MemberRole>'.$list.'</MemberRole>
\r
788 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
789 <Type>Passport</Type>
\r
790 <State>Accepted</State>
\r
791 <PassportName>'.$user.'</PassportName>
\r
800 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
801 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
802 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
803 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
804 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
806 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
807 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
808 <IsMigration>false</IsMigration>
\r
809 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
810 </ABApplicationHeader>
\r
811 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
812 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
813 <TicketToken>'.$ticket.'</TicketToken>
\r
817 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
820 <Type>Messenger</Type>
\r
821 <ForeignId></ForeignId>
\r
825 <MemberRole>'.$list.'</MemberRole>
\r
827 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
829 <State>Accepted</State>
\r
830 <Email>'.$user.'</Email>
\r
833 <Name>MSN.IM.BuddyType</Name>
\r
834 <Value>32:YAHOO</Value>
\r
844 $header_array = array(
\r
845 'SOAPAction: '.$this->addmember_soap,
\r
846 'Content-Type: text/xml; charset=utf-8',
\r
847 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
850 $this->debug_message("*** URL: $this->addmember_url");
\r
851 $this->debug_message("*** Sending SOAP:\n$XML");
\r
852 $curl = curl_init();
\r
853 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
854 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
855 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
856 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
857 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
858 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
859 curl_setopt($curl, CURLOPT_POST, 1);
\r
860 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
861 $data = curl_exec($curl);
\r
862 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
864 $this->debug_message("*** Get Result:\n$data");
\r
866 if ($http_code != 200) {
\r
867 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
868 if (count($matches) == 0) {
\r
869 $this->log_message("*** can't add member (network: $network) $email to $list");
\r
872 $faultcode = trim($matches[1]);
\r
873 $faultstring = trim($matches[2]);
\r
874 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
875 $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
\r
878 $this->log_message("*** add member (network: $network) $email to $list, already exist!");
\r
881 $this->log_message("*** add member (network: $network) $email to $list");
\r
885 function getMembershipList($returnData=false) {
\r
886 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
887 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
888 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
889 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
890 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
891 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
893 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
894 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
895 <IsMigration>false</IsMigration>
\r
896 <PartnerScenario>Initial</PartnerScenario>
\r
897 </ABApplicationHeader>
\r
898 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
899 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
900 <TicketToken>'.$ticket.'</TicketToken>
\r
904 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
907 <ServiceType>Messenger</ServiceType>
\r
908 <ServiceType>Invitation</ServiceType>
\r
909 <ServiceType>SocialNetwork</ServiceType>
\r
910 <ServiceType>Space</ServiceType>
\r
911 <ServiceType>Profile</ServiceType>
\r
917 $header_array = array(
\r
918 'SOAPAction: '.$this->membership_soap,
\r
919 'Content-Type: text/xml; charset=utf-8',
\r
920 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
922 $this->debug_message("*** URL: $this->membership_url");
\r
923 $this->debug_message("*** Sending SOAP:\n$XML");
\r
924 $curl = curl_init();
\r
925 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
926 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
927 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
928 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
929 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
930 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
931 curl_setopt($curl, CURLOPT_POST, 1);
\r
932 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
933 $data = curl_exec($curl);
\r
934 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
936 $this->debug_message("*** Get Result:\n$data");
\r
937 if(($http_code != 200)||(!$returnData)) return array();
\r
939 $aMemberships = array();
\r
941 //$this->debug_message("search p = $p");
\r
942 $start = strpos($p, '<Membership>');
\r
943 $end = strpos($p, '</Membership>');
\r
944 if ($start === false || $end === false || $start > $end) break;
\r
945 //$this->debug_message("start = $start, end = $end");
\r
947 $sMembership = substr($p, $start, $end - $start);
\r
948 $aMemberships[] = $sMembership;
\r
949 //$this->debug_message("add sMembership = $sMembership");
\r
950 $p = substr($p, $end);
\r
952 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
954 $aContactList = array();
\r
955 foreach ($aMemberships as $sMembership) {
\r
956 //$this->debug_message("sMembership = $sMembership");
\r
957 if (isset($matches)) unset($matches);
\r
958 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
959 if (count($matches) == 0) continue;
\r
960 $sMemberRole = $matches[1];
\r
961 //$this->debug_message("MemberRole = $sMemberRole");
\r
962 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
964 if (isset($aMembers)) unset($aMembers);
\r
965 $aMembers = array();
\r
967 //$this->debug_message("search p = $p");
\r
968 $start = strpos($p, '<Member xsi:type="');
\r
969 $end = strpos($p, '</Member>');
\r
970 if ($start === false || $end === false || $start > $end) break;
\r
971 //$this->debug_message("start = $start, end = $end");
\r
973 $sMember = substr($p, $start, $end - $start);
\r
974 $aMembers[] = $sMember;
\r
975 //$this->debug_message("add sMember = $sMember");
\r
976 $p = substr($p, $end);
\r
978 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
979 foreach ($aMembers as $sMember) {
\r
980 //$this->debug_message("sMember = $sMember");
\r
981 if (isset($matches)) unset($matches);
\r
982 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
983 if (count($matches) == 0) continue;
\r
984 $sMemberType = $matches[1];
\r
985 //$this->debug_message("MemberType = $sMemberType");
\r
987 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
988 if (count($matches) == 0) continue;
\r
990 if ($sMemberType == 'PassportMember') {
\r
991 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
993 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
995 else if ($sMemberType == 'EmailMember') {
\r
996 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
997 // Value is 32: or 32:YAHOO
\r
998 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
999 if (count($matches) == 0) continue;
\r
1000 if ($matches[1] != 32) continue;
\r
1002 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
1004 if ($network == -1) continue;
\r
1005 if (count($matches) > 0) {
\r
1006 $email = $matches[1];
\r
1007 @list($u_name, $u_domain) = @explode('@', $email);
\r
1008 if ($u_domain == NULL) continue;
\r
1009 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
1010 $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
1014 return $aContactList;
\r
1017 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
1019 if ($redirect_server === '') {
\r
1020 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
\r
1021 if (!$this->NSfp) {
\r
1022 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
\r
1027 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);
\r
1028 if (!$this->NSfp) {
\r
1029 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
1034 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1035 $this->authed = false;
\r
1037 // NS: >> VER {id} MSNP9 CVR0
\r
1039 // NS: >>> VER {id} MSNP15 CVR0
\r
1040 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1042 $start_tm = time();
\r
1043 while (!feof($this->NSfp))
\r
1045 $data = $this->ns_readln();
\r
1047 if ($data === false) {
\r
1048 if ($this->timeout > 0) {
\r
1050 $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
\r
1051 if ($used_time > $this->timeout) {
\r
1054 $this->ns_writeln("OUT");
\r
1055 fclose($this->NSfp);
\r
1056 $this->error = 'Timeout, maybe protocol changed!';
\r
1057 $this->debug_message("*** $this->error");
\r
1063 $code = substr($data, 0, 3);
\r
1064 $start_tm = time();
\r
1069 // NS: <<< VER {id} MSNP9 CVR0
\r
1070 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
1072 // NS: <<< VER {id} MSNP15 CVR0
\r
1073 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
1074 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
1079 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
1080 // NS: >>> USR {id} TWN I {user}
\r
1082 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
1083 // NS: >>> USR {id} SSO I {user}
\r
1084 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
1088 // already login for passport site, finish the login process now.
\r
1089 // NS: <<< USR {id} OK {user} {verify} 0
\r
1090 if ($this->authed) return true;
\r
1091 // max. 16 digits for password
\r
1092 if (strlen($password) > 16)
\r
1093 $password = substr($password, 0, 16);
\r
1095 $this->user = $user;
\r
1096 $this->password = $password;
\r
1097 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1098 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1100 $this->passport_policy = $policy;
\r
1101 $aTickets = $this->get_passport_ticket();
\r
1102 if (!$aTickets || !is_array($aTickets)) {
\r
1105 $this->ns_writeln("OUT");
\r
1106 fclose($this->NSfp);
\r
1107 $this->error = 'Passport authenticated fail!';
\r
1108 $this->debug_message("*** $this->error");
\r
1112 $ticket = $aTickets['ticket'];
\r
1113 $secret = $aTickets['secret'];
\r
1114 $this->ticket = $aTickets;
\r
1115 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1117 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1118 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1119 $this->authed = true;
\r
1123 // main login server will redirect to anther NS after USR command
\r
1125 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1127 // NS: <<< XFR {id} NS {server} U D
\r
1128 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1129 if($Type!='NS') break;
\r
1130 @list($ip, $port) = @explode(':', $server);
\r
1131 // this connection will close after XFR
\r
1132 fclose($this->NSfp);
\r
1134 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
1135 if (!$this->NSfp) {
\r
1136 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1137 $this->debug_message("*** $this->error");
\r
1141 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1143 // NS: >> VER {id} MSNP9 CVR0
\r
1145 // NS: >>> VER {id} MSNP15 CVR0
\r
1146 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1150 // return some policy data after 'USR {id} SSO I {user}' command
\r
1151 // NS: <<< GCF 0 {size}
\r
1152 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1153 // we don't need the data, just read it and drop
\r
1154 if (is_numeric($size) && $size > 0)
\r
1155 $this->ns_readdata($size);
\r
1159 // we'll quit if got any error
\r
1160 if (is_numeric($code)) {
\r
1163 $this->ns_writeln("OUT");
\r
1164 fclose($this->NSfp);
\r
1165 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1166 $this->debug_message("*** $this->error");
\r
1169 // unknown response from server, just ignore it
\r
1173 // never goto here
\r
1176 function derive_key($key, $magic) {
\r
1177 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1178 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1179 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1180 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1181 return $hash2.substr($hash4, 0, 4);
\r
1184 function generateLoginBLOB($key, $challenge) {
\r
1185 $key1 = base64_decode($key);
\r
1186 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1187 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1189 // get hash of challenge using key2
\r
1190 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1192 // get 8 bytes random data
\r
1193 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1195 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1197 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1202 return base64_encode($blob);
\r
1205 function getOIM_maildata() {
\r
1206 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1207 if (count($matches) == 0) {
\r
1208 $this->debug_message('*** no web ticket?');
\r
1211 $t = htmlspecialchars($matches[1]);
\r
1212 $p = htmlspecialchars($matches[2]);
\r
1213 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1214 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1215 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1216 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1218 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1224 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1226 </soap:Envelope>';
\r
1228 $header_array = array(
\r
1229 'SOAPAction: '.$this->oim_maildata_soap,
\r
1230 'Content-Type: text/xml; charset=utf-8',
\r
1231 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1234 $this->debug_message("*** URL: $this->oim_maildata_url");
\r
1235 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1236 $curl = curl_init();
\r
1237 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1238 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1239 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1240 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1241 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1242 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1243 curl_setopt($curl, CURLOPT_POST, 1);
\r
1244 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1245 $data = curl_exec($curl);
\r
1246 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1247 curl_close($curl);
\r
1248 $this->debug_message("*** Get Result:\n$data");
\r
1250 if ($http_code != 200) {
\r
1251 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
\r
1255 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1256 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1257 if (count($matches) == 0) {
\r
1258 $this->debug_message("*** Can't get OIM maildata");
\r
1261 return $matches[2];
\r
1264 function getOIM_message($msgid) {
\r
1265 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1266 if (count($matches) == 0) {
\r
1267 $this->debug_message('*** no web ticket?');
\r
1270 $t = htmlspecialchars($matches[1]);
\r
1271 $p = htmlspecialchars($matches[2]);
\r
1274 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1275 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1276 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1277 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1279 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1285 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1286 <messageId>'.$msgid.'</messageId>
\r
1287 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1290 </soap:Envelope>';
\r
1292 $header_array = array(
\r
1293 'SOAPAction: '.$this->oim_read_soap,
\r
1294 'Content-Type: text/xml; charset=utf-8',
\r
1295 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1298 $this->debug_message("*** URL: $this->oim_read_url");
\r
1299 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1300 $curl = curl_init();
\r
1301 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1302 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1303 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1304 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1305 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1306 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1307 curl_setopt($curl, CURLOPT_POST, 1);
\r
1308 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1309 $data = curl_exec($curl);
\r
1310 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1311 curl_close($curl);
\r
1312 $this->debug_message("*** Get Result:\n$data");
\r
1314 if ($http_code != 200) {
\r
1315 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1319 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1321 $start = strpos($data, '<GetMessageResult>');
\r
1322 $end = strpos($data, '</GetMessageResult>');
\r
1323 if ($start === false || $end === false || $start > $end) {
\r
1324 $this->debug_message("*** Can't get OIM: $msgid");
\r
1327 $lines = substr($data, $start + 18, $end - $start);
\r
1328 $aLines = @explode("\n", $lines);
\r
1332 foreach ($aLines as $line) {
\r
1333 $line = rtrim($line);
\r
1335 if ($line === '') {
\r
1341 // stop at empty lines
\r
1342 if ($line === '') break;
\r
1345 $sMsg = base64_decode($sOIM);
\r
1346 $this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1349 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1350 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1351 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1352 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1354 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1360 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1362 <messageId>'.$msgid.'</messageId>
\r
1366 </soap:Envelope>';
\r
1368 $header_array = array(
\r
1369 'SOAPAction: '.$this->oim_del_soap,
\r
1370 'Content-Type: text/xml; charset=utf-8',
\r
1371 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1374 $this->debug_message("*** URL: $this->oim_del_url");
\r
1375 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1376 $curl = curl_init();
\r
1377 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1378 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1379 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1380 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1381 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1382 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1383 curl_setopt($curl, CURLOPT_POST, 1);
\r
1384 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1385 $data = curl_exec($curl);
\r
1386 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1387 curl_close($curl);
\r
1388 $this->debug_message("*** Get Result:\n$data");
\r
1390 if ($http_code != 200)
\r
1391 $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
\r
1393 $this->debug_message("*** OIM ($msgid) deleted");
\r
1396 private function NSLogout() {
\r
1397 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1400 $this->ns_writeln("OUT");
\r
1401 fclose($this->NSfp);
\r
1402 $this->NSfp = false;
\r
1403 $this->log_message("*** logout now!");
\r
1407 private function NSRetryWait($Wait) {
\r
1408 $this->log_message("*** wait for $Wait seconds");
\r
1409 for($i=0;$i<$Wait;$i++) {
\r
1411 if($this->kill_me) return false;
\r
1415 public function ProcessSendMessageFileQueue() {
\r
1416 $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');
\r
1417 if (!is_array($aFiles)) return true;
\r
1419 foreach ($aFiles as $filename) {
\r
1420 $fp = fopen($filename, 'rt');
\r
1421 if (!$fp) continue;
\r
1424 $buf = trim(fgets($fp));
\r
1425 if (substr($buf, 0, 3) == 'TO:') {
\r
1426 $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));
\r
1427 while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";
\r
1430 if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')
\r
1431 $this->log_message("!!! message format error? delete $filename");
\r
1434 foreach($aTo as $To)
\r
1436 @list($user, $domain, $network) = @explode('@', $To);
\r
1437 $MessageList[$network]["$user@$domain"]=$sMessage;
\r
1440 if($this->backup_file)
\r
1442 $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';
\r
1443 if (!file_exists($backup_dir)) @mkdir($backup_dir);
\r
1444 $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);
\r
1445 if (@rename($filename, $backup_name))
\r
1446 $this->log_message("*** move file to $backup_name");
\r
1448 else @unlink($filename);
\r
1450 foreach ($MessageList as $network => $Messages)
\r
1452 switch(trim($network))
\r
1456 // okay, try to ask a switchboard (SB) for sending message
\r
1457 // NS: >>> XFR {id} SB
\r
1458 // $this->ns_writeln("XFR $this->id SB");
\r
1459 foreach($Messages as $User => $Message)
\r
1460 $this->MessageQueue[$User][]=$Message;
\r
1462 case 'Offline': //MSN
\r
1464 //FIXME: 修正Send OIM
\r
1465 foreach($Messages as $To => $Message)
\r
1468 for ($i = 0; $i < $this->oim_try; $i++)
\r
1470 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
1471 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
1472 // need challenge lockkey
\r
1473 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
1474 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
1477 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
1481 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
1484 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
1485 // maybe we need to re-login again
\r
1486 if(!$this->get_passport_ticket())
\r
1488 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1491 $this->log_message("**** get new ticket, try it again");
\r
1498 foreach($Messages as $To => $Message) {
\r
1499 $Message=$this->getMessage($Message, $network);
\r
1500 $len = strlen($Message);
\r
1501 $this->ns_writeln("UUM $this->id $To $network 1 $len");
\r
1502 $this->ns_writedata($Message);
\r
1503 $this->log_message("*** sent to $To (network: $network):\n$Message");
\r
1507 if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))
\r
1509 $this->MessageQueue[$User]['XFRSent']=false;
\r
1510 $this->MessageQueue[$User]['ReqTime']=false;
\r
1514 public function SignalFunction($signal)
\r
1524 $ChildPid=pcntl_wait($status,WUNTRACED);
\r
1527 $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);
\r
1528 unset($this->ChildProcess[$ChildPid]);
\r
1534 public function Run()
\r
1536 $this->log_message("*** startup ***");
\r
1537 if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");
\r
1538 if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");
\r
1539 if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");
\r
1540 $process_file = false;
\r
1543 $aContactList = array();
\r
1546 if($this->kill_me)
\r
1548 $this->log_message("*** Okay, kill me now!");
\r
1549 return $this->NSLogout();
\r
1551 if (!is_resource($this->NSfp) || feof($this->NSfp))
\r
1553 $this->log_message("*** try to connect to MSN network");
\r
1554 if (!$this->connect($this->user, $this->password))
\r
1556 $this->log_message("!!! Can't connect to server: $this->error");
\r
1557 if(!$this->NSRetryWait($this->retry_wait)) continue;
\r
1559 $this->UpdateContacts();
\r
1560 $this->LastPing=time();
\r
1561 $this->log_message("*** connected, wait for command");
\r
1562 $start_tm = time();
\r
1563 $ping_tm = time();
\r
1564 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1565 $aContactList = $this->getMembershipList(true);
\r
1566 if ($this->update_pending) {
\r
1567 if (is_array($aContactList)) {
\r
1568 $pending = 'Pending';
\r
1569 foreach ($aContactList as $u_domain => $aUserList) {
\r
1570 foreach ($aUserList as $u_name => $aNetworks) {
\r
1571 foreach ($aNetworks as $network => $aData) {
\r
1572 if (isset($aData[$pending])) {
\r
1575 foreach (array('Allow', 'Reverse') as $list) {
\r
1576 if (isset($aData[$list]))
\r
1579 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1580 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1586 $id = $aData[$pending];
\r
1587 // we can delete it from pending now
\r
1588 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1589 unset($aContactList[$u_domain][$u_name][$network][$pending]);
\r
1594 foreach (array('Allow', 'Reverse') as $list) {
\r
1595 if (!isset($aData[$list])) {
\r
1596 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1597 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1609 if (is_array($aContactList)) {
\r
1610 foreach ($aContactList as $u_domain => $aUserList) {
\r
1611 $str = '<d n="'.$u_domain.'">';
\r
1612 $len += strlen($str);
\r
1613 if ($len > 7400) {
\r
1614 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1617 $len = strlen($str);
\r
1620 foreach ($aUserList as $u_name => $aNetworks) {
\r
1621 foreach ($aNetworks as $network => $status) {
\r
1622 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1623 $len += strlen($str);
\r
1624 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1626 if ($len > 7475) {
\r
1628 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1630 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1631 $len = strlen($sList);
\r
1640 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1641 // NS: >>> BLP {id} BL
\r
1642 $this->ns_writeln("BLP $this->id BL");
\r
1643 foreach ($aADL as $str) {
\r
1644 $len = strlen($str);
\r
1645 // NS: >>> ADL {id} {size}
\r
1646 $this->ns_writeln("ADL $this->id $len");
\r
1647 $this->ns_writedata($str);
\r
1649 // NS: >>> PRP {id} MFN name
\r
1650 if ($this->alias == '') $this->alias = $user;
\r
1651 $aliasname = rawurlencode($this->alias);
\r
1652 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1654 //$MsnObj=$this->PhotoStckObj();
\r
1655 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1656 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1657 if($this->PhotoStickerFile!==false)
\r
1658 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1659 // NS: >>> UUX {id} length
\r
1660 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1661 $len = strlen($str);
\r
1662 $this->ns_writeln("UUX $this->id $len");
\r
1663 $this->ns_writedata($str);
\r
1665 $data = $this->ns_readln();
\r
1668 //If No NS Message Process SendMessageFileQueue
\r
1669 if (time()-$this->LastPing > $this->ping_wait)
\r
1672 $this->ns_writeln("PNG");
\r
1673 $this->LastPing = time();
\r
1675 if(count($this->ChildProcess)<$this->MAXChildProcess)
\r
1678 foreach($this->MessageQueue as $User => $Message)
\r
1680 if(!trim($User)) continue;
\r
1681 if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;
\r
1682 if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))
\r
1684 $this->MessageQueue[$User]['XFRSent']=true;
\r
1685 $this->MessageQueue[$User]['ReqTime']=time();
\r
1686 $this->log_message("*** Request SB for $User");
\r
1687 $this->ns_writeln("XFR $this->id SB");
\r
1692 if($this->ProcessSendMessageFileQueue()) continue;
\r
1695 switch (substr($data,0,3))
\r
1698 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
1699 // NS: <<< SBS 0 null
\r
1704 // NS: <<< RFS ???
\r
1705 // refresh ADL, so we re-send it again
\r
1706 if (is_array($aADL)) {
\r
1707 foreach ($aADL as $str) {
\r
1708 $len = strlen($str);
\r
1709 // NS: >>> ADL {id} {size}
\r
1710 $this->ns_writeln("ADL $this->id $len");
\r
1711 $this->ns_writedata($str);
\r
1717 // NS: <<< LST {email} {alias} 11 0
\r
1718 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
1719 @list($u_name, $u_domain) = @explode('@', $email);
\r
1720 if (!isset($aContactList[$u_domain][$u_name][1])) {
\r
1721 $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
1722 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
1727 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
1728 // NS: <<< ADL 0 {size}
\r
1729 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
1730 if (is_numeric($size) && $size > 0)
\r
1732 $data = $this->ns_readdata($size);
\r
1733 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1734 if (is_array($matches) && count($matches) > 0)
\r
1736 $u_domain = $matches[1];
\r
1737 $u_name = $matches[2];
\r
1738 $network = $matches[4];
\r
1739 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1740 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
1743 $re_login = false;
\r
1745 foreach (array('Allow', 'Reverse') as $list)
\r
1747 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1750 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1753 $aTickets = $this->get_passport_ticket();
\r
1754 if (!$aTickets || !is_array($aTickets)) {
\r
1755 // failed to login? ignore it
\r
1756 $this->log_message("*** can't re-login, something wrong here");
\r
1757 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1761 $this->ticket = $aTickets;
\r
1762 $this->log_message("**** get new ticket, try it again");
\r
1763 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1765 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1769 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1772 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
1774 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
1775 $len = strlen($str);
\r
1778 $this->log_message("*** someone add us to their list: $data");
\r
1779 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
1784 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
1785 // NS: <<< RML 0 {size}
\r
1786 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
1787 if (is_numeric($size) && $size > 0)
\r
1789 $data = $this->ns_readdata($size);
\r
1790 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1791 if (is_array($matches) && count($matches) > 0)
\r
1793 $u_domain = $matches[1];
\r
1794 $u_name = $matches[2];
\r
1795 $network = $matches[4];
\r
1796 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1798 $aData = $aContactList[$u_domain][$u_name][$network];
\r
1799 foreach ($aData as $list => $id)
\r
1800 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
1801 unset($aContactList[$u_domain][$u_name][$network]);
\r
1802 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
1805 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
1806 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
1809 $this->log_message("*** someone remove us from their list: $data");
\r
1814 // randomly, we get MSG notification from server
\r
1815 // NS: <<< MSG Hotmail Hotmail {size}
\r
1816 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
1817 if (is_numeric($size) && $size > 0) {
\r
1818 $data = $this->ns_readdata($size);
\r
1819 $aLines = @explode("\n", $data);
\r
1823 foreach ($aLines as $line) {
\r
1824 $line = rtrim($line);
\r
1826 if ($line === '') {
\r
1830 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
1831 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
1832 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
1833 // we just need text/x-msmsgsinitialmdatanotification
\r
1834 // or text/x-msmsgsoimnotification
\r
1841 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
1842 $maildata = trim(substr($line, 10));
\r
1847 $this->log_message("*** ingnore MSG for: $line");
\r
1850 if ($maildata == '') {
\r
1851 $this->log_message("*** ingnore MSG not for OIM");
\r
1854 $re_login = false;
\r
1855 if (strcasecmp($maildata, 'too-large') == 0) {
\r
1856 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
1857 $maildata = $this->getOIM_maildata();
\r
1858 if ($maildata === false) {
\r
1859 $this->log_message("*** can't get mail-data via SOAP");
\r
1860 // maybe we need to re-login again
\r
1861 $aTickets = $this->get_passport_ticket();
\r
1862 if (!$aTickets || !is_array($aTickets)) {
\r
1863 // failed to login? ignore it
\r
1864 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1868 $this->ticket = $aTickets;
\r
1869 $this->log_message("**** get new ticket, try it again");
\r
1870 $maildata = $this->getOIM_maildata();
\r
1871 if ($maildata === false) {
\r
1872 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
1877 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
1881 $start = strpos($p, '<M>');
\r
1882 $end = strpos($p, '</M>');
\r
1883 if ($start === false || $end === false || $start > $end) break;
\r
1885 $sOIM = substr($p, $start, $end - $start);
\r
1887 $p = substr($p, $end);
\r
1889 if (count($aOIMs) == 0) {
\r
1890 $this->log_message("*** ingnore empty OIM");
\r
1893 foreach ($aOIMs as $maildata) {
\r
1894 // T: 11 for MSN, 13 for Yahoo
\r
1895 // S: 6 for MSN, 7 for Yahoo
\r
1896 // RT: the datetime received by server
\r
1897 // RS: already read or not
\r
1898 // SZ: size of message
\r
1901 // F: always 00000000-0000-0000-0000-000000000009
\r
1902 // N: sender alias
\r
1903 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
1904 if (count($matches) == 0) {
\r
1905 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
1908 $oim_type = $matches[1];
\r
1909 if ($oim_type = 13)
\r
1913 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
1914 if (count($matches) == 0) {
\r
1915 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
1918 $oim_sender = $matches[1];
\r
1919 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
1920 if (count($matches) == 0) {
\r
1921 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
1924 $oim_msgid = $matches[1];
\r
1925 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
1926 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
1927 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
1928 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
1929 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
1930 $sMsg = $this->getOIM_message($oim_msgid);
\r
1931 if ($sMsg === false) {
\r
1932 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
1934 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1937 $aTickets = $this->get_passport_ticket();
\r
1938 if (!$aTickets || !is_array($aTickets)) {
\r
1939 // failed to login? ignore it
\r
1940 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1944 $this->ticket = $aTickets;
\r
1945 $this->log_message("**** get new ticket, try it again");
\r
1946 $sMsg = $this->getOIM_message($oim_msgid);
\r
1947 if ($sMsg === false) {
\r
1948 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1952 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
1954 $this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
1960 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
1961 // NS: <<< UBM {email} $network $type {size}
\r
1962 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
1963 if (is_numeric($size) && $size > 0)
\r
1965 $data = $this->ns_readdata($size);
\r
1966 $aLines = @explode("\n", $data);
\r
1970 foreach ($aLines as $line) {
\r
1971 $line = rtrim($line);
\r
1973 if ($line === '') {
\r
1977 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
1983 $aSubLines = @explode("\r", $line);
\r
1984 foreach ($aSubLines as $str) {
\r
1992 $this->log_message("*** ingnore from $from_email: $line");
\r
1995 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
1996 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2001 // randomly, we get UBX notification from server
\r
2002 // NS: <<< UBX email {network} {size}
\r
2003 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
2004 // we don't need the notification data, so just ignore it
\r
2005 if (is_numeric($size) && $size > 0)
\r
2006 $this->ns_readdata($size);
\r
2010 // randomly, we'll get challenge from server
\r
2011 // NS: <<< CHL 0 {code}
\r
2012 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
2013 $fingerprint = $this->getChallenge($chl_code);
\r
2014 // NS: >>> QRY {id} {product_id} 32
\r
2015 // NS: >>> fingerprint
\r
2016 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
2017 $this->ns_writedata($fingerprint);
\r
2018 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2019 if($this->PhotoStickerFile!==false)
\r
2020 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2023 // NS: <<< CHG {id} {status} {code}
\r
2025 // change our status to online first
\r
2029 // sometimes, NS will redirect to another NS
\r
2031 // NS: <<< XFR {id} NS {server} 0 {server}
\r
2033 // NS: <<< XFR {id} NS {server} U D
\r
2034 // for normal switchboard XFR
\r
2035 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
2036 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
2037 @list($ip, $port) = @explode(':', $server);
\r
2038 if ($server_type != 'SB') {
\r
2040 // this connection will close after XFR
\r
2041 $this->NSLogout();
\r
2044 if(count($this->MessageQueue))
\r
2046 foreach($this->MessageQueue as $User => $Message)
\r
2048 //$this->ChildProcess[$ChildPid]
\r
2049 $this->log_message("*** XFR SB $User");
\r
2050 $pid=pcntl_fork();
\r
2054 $this->ChildProcess[$pid]=$User;
\r
2059 $this->log_message("*** Fork Error $User");
\r
2065 $this->log_message("*** Child Process Start for $User");
\r
2066 unset($Message['XFRSent']);
\r
2067 unset($Message['ReqTime']);
\r
2068 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
2069 if ($bSBresult === false)
\r
2071 // error for switchboard
\r
2072 $this->log_message("!!! error for sending message to ".$User);
\r
2077 unset($this->MessageQueue[$User]);
\r
2080 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
2081 if ($bSBresult === false) {
\r
2082 // error for switchboard
\r
2083 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
2084 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
2088 // NS: <<< QNG {time}
\r
2089 @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
2090 if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
2091 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
2092 //Mod by Ricky Set Online
\r
2096 if($this->PhotoStickerFile!==false)
\r
2097 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2099 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2100 // someone is trying to talk to us
\r
2101 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2102 $this->log_message("NS: <<< RNG $data");
\r
2103 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2104 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2105 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
2106 $this->addContact($email,1,$email, true);
\r
2107 $pid=pcntl_fork();
\r
2111 $this->ChildProcess[$pid]='RNG';
\r
2116 $this->log_message("*** Fork Error $User");
\r
2122 $this->log_message("*** Ring Child Process Start for $User");
\r
2123 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
2128 // force logout from NS
\r
2129 // NS: <<< OUT xxx
\r
2130 fclose($this->NSfp);
\r
2131 $this->log_message("*** LOGOUT from NS");
\r
2135 $code = substr($data,0,3);
\r
2136 if (is_numeric($code)) {
\r
2137 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2138 $this->debug_message("*** NS: $this->error");
\r
2140 return $this->NsLogout();
\r
2145 return $this->NsLogout();
\r
2148 /*public function SendMessage($Message, $To)
\r
2150 $FileName = MSN_CLASS_SPOOL_DIR.'/'.strftime('%Y%m%d%H%M%S',time()).'_'.posix_getpid().'_sendMessage.msn';
\r
2151 if(!is_array($To))
\r
2154 foreach($To as $Email)
\r
2156 list($name,$host,$network)=explode('@',$Email);
\r
2157 $network=$network==''?1:$network;
\r
2158 if($network==1 && $this->SwitchBoardProcess && $this->SwitchBoardSessionUser=="$name@$host" )
\r
2160 $this->debug_message("*** SendMessage to $Receiver use SB message queue.");
\r
2161 array_push($this->SwitchBoardMessageQueue,$Message);
\r
2164 $Receiver.="$name@$host@$network,";
\r
2166 if($Receiver=='') return;
\r
2167 $Receiver=substr($Receiver,0,-1);
\r
2168 $this->debug_message("*** SendMessage to $Receiver use File queue.");
\r
2169 file_put_contents($FileName,"TO: $Receiver\n$Message\n");
\r
2172 function getChallenge($code)
\r
2175 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
2176 // Step 1: The MD5 Hash
\r
2177 $md5Hash = md5($code.$this->prod_key);
\r
2178 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2179 for ($i = 0; $i < 4; $i++) {
\r
2180 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
2181 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
2184 // Step 2: A new string
\r
2185 $chl_id = $code.$this->prod_id;
\r
2186 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
2188 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
2189 for ($i = 0; $i < count($aID); $i++) {
\r
2190 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
2191 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
2194 // Step 3: The 64 bit key
\r
2195 $magic_num = 0x0E79A9C1;
\r
2196 $str7f = 0x7FFFFFFF;
\r
2199 for ($i = 0; $i < count($aID); $i += 2) {
\r
2201 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
2202 $temp = bcadd($temp, $high);
\r
2203 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
2204 $temp = bcmod($temp, $str7f);
\r
2206 $high = $aID[$i+1];
\r
2207 $high = bcmod(bcadd($high, $temp), $str7f);
\r
2208 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
2209 $high = bcmod($high, $str7f);
\r
2211 $low = bcadd(bcadd($low, $high), $temp);
\r
2214 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
2215 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
2217 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
2218 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
2219 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
2220 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
2221 // we need integer here
\r
2222 $high = 0+$new_high;
\r
2224 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
2225 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
2226 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
2227 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
2228 // we need integer here
\r
2229 $low = 0+$new_low;
\r
2231 // we just use 32 bits integer, don't need the key, just high/low
\r
2232 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
2234 // Step 4: Using the key
\r
2235 $md5Hash = md5($code.$this->prod_key);
\r
2236 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2239 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
2240 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
2241 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
2242 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
2247 private function getMessage($sMessage, $network = 1)
\r
2249 $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
2250 $msg_header_len = strlen($msg_header);
\r
2251 if ($network == 1)
\r
2252 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
2254 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
2255 $sMessage=str_replace("\r", '', $sMessage);
\r
2256 $msg=substr($sMessage,0,$maxlen);
\r
2257 return $msg_header.$msg;
\r
2261 * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息
\r
2265 private function DoSwitchBoard($Action,$Param)
\r
2267 $SessionEnd=false;
\r
2270 $LastActive=time();
\r
2271 stream_set_timeout($this->SBFp, $this->SBTimeout);
\r
2275 $cki_code=$Param['cki'];
\r
2276 $user=$Param['user'];
\r
2277 $this->SwitchBoardMessageQueue=$Param['Msg'];
\r
2278 // SB: >>> USR {id} {user} {cki}
\r
2279 $this->SB_writeln("USR $id $this->user $cki_code");
\r
2281 $this->SwitchBoardSessionUser=$user;
\r
2284 $ticket=$Param['ticket'];
\r
2285 $sid=$Param['sid'];
\r
2286 $user=$Param['user'];
\r
2287 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2288 $this->SB_writeln("ANS $id $this->user $ticket $sid");
\r
2290 $this->SwitchBoardSessionUser=$user;
\r
2295 while((!feof($this->SBFp))&&(!$SessionEnd))
\r
2297 $data = $this->SB_readln();
\r
2298 if($this->kill_me)
\r
2300 $this->log_message("*** SB Okay, kill me now!");
\r
2303 if($data === false)
\r
2305 if(time()-$LastActive > $this->SBIdleTimeout)
\r
2307 $this->debug_message("*** SB Idle Timeout!");
\r
2310 if(!$Joined) continue;
\r
2311 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2313 if($Message=='') continue;
\r
2314 $aMessage = $this->getMessage($Message);
\r
2316 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2317 if($MsnObjDefine!=='')
\r
2319 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2320 $len = strlen($SendString);
\r
2321 $this->SB_writeln("MSG $id N $len");
\r
2323 $this->SB_writedata($SendString);
\r
2326 $len = strlen($aMessage);
\r
2327 $this->SB_writeln("MSG $id N $len");
\r
2329 $this->SB_writedata($aMessage);
\r
2331 $this->SwitchBoardMessageQueue=array();
\r
2332 $LastActive=time();
\r
2335 $code = substr($data, 0, 3);
\r
2339 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
2340 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
2341 $this->log_message("*** $email join us");
\r
2345 $this->log_message("*** Quit for BYE");
\r
2349 // SB: <<< USR {id} OK {user} {alias}
\r
2350 // we don't need the data, just ignore it
\r
2351 // request user to join this switchboard
\r
2352 // SB: >>> CAL {id} {user}
\r
2353 $this->SB_writeln("CAL $id $user");
\r
2357 // SB: <<< CAL {id} RINGING {?}
\r
2358 // we don't need this, just ignore, and wait for other response
\r
2362 // SB: <<< JOI {user} {alias} {clientid?}
\r
2363 // someone join us
\r
2364 // we don't need the data, just ignore it
\r
2365 // no more user here
\r
2369 // SB: <<< MSG {email} {alias} {len}
\r
2370 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
2371 $len = trim($len);
\r
2372 $data = $this->SB_readdata($len);
\r
2373 $aLines = @explode("\n", $data);
\r
2378 foreach ($aLines as $line)
\r
2380 $line = rtrim($line);
\r
2382 if ($line === '') {
\r
2386 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2387 // typing notification, just ignore
\r
2391 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
2392 // we don't handle any split message, just ignore
\r
2396 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
2397 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
2399 $p = strstr($data, "\n\n");
\r
2401 if ($p === false) {
\r
2402 $p = strstr($data, "\r\n\r\n");
\r
2404 $sMsg = substr($p, 4);
\r
2407 $sMsg = substr($p, 2);
\r
2410 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
2411 // ignore all application/x-... message
\r
2413 // application/x-ms-ink => ink message
\r
2417 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
2418 // ignore all text/x-... message
\r
2420 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
2421 // text/x-mms-animemoticon => customized animemotion word
\r
2433 $this->log_message("*** ingnore from $from_email: $line");
\r
2438 // we will ignore any p2p message after sending acknowledgement
\r
2440 $len = strlen($sMsg);
\r
2441 $this->log_message("*** p2p message from $from_email, size $len");
\r
2442 // header = 48 bytes
\r
2443 // content >= 0 bytes
\r
2444 // footer = 4 bytes
\r
2445 // so it need to >= 52 bytes
\r
2446 /*if ($len < 52) {
\r
2447 $this->log_message("*** p2p: size error, less than 52!");
\r
2450 $aDwords = @unpack("V12dword", $sMsg);
\r
2451 if (!is_array($aDwords)) {
\r
2452 $this->log_message("*** p2p: header unpack error!");
\r
2455 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
2456 $hdr_SessionID = $aDwords['dword1'];
\r
2457 $hdr_Identifier = $aDwords['dword2'];
\r
2458 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
2459 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
2460 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
2461 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
2462 $hdr_MessageLength = $aDwords['dword7'];
\r
2463 $hdr_Flag = $aDwords['dword8'];
\r
2464 $hdr_AckID = $aDwords['dword9'];
\r
2465 $hdr_AckUID = $aDwords['dword10'];
\r
2466 $hdr_AckSizeLow = $aDwords['dword11'];
\r
2467 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
2468 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
2469 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
2470 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
2471 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
2472 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
2473 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
2474 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
2475 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
2476 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
2477 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
2478 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
2479 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
2480 if($hdr_Flag==2) {
\r
2481 //This is an ACK from SB ignore....
\r
2482 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
2485 $MsgBody=$this->linetoArray(substr($sMsg,48,-4));
\r
2486 $this->debug_message("*** p2p: body".print_r($MsgBody,true));
\r
2487 if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))
\r
2491 if($this->SB_readln()===false) break;
\r
2493 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));
\r
2494 preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);
\r
2495 $BranchGUID=$Matches[1];
\r
2496 //it's an invite to send a display picture.
\r
2497 $new_id = ~$hdr_Identifier;
\r
2498 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2501 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2506 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2507 $footer = pack("L", 0);
\r
2508 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2509 $len = strlen($message);
\r
2510 $this->SB_writeln("MSG $id D $len");
\r
2512 $this->SB_writedata($message);
\r
2513 $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
2514 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
2515 $this->SB_readln();//Read ACK;
\r
2516 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
2518 //Send 200 OK message
\r
2519 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
2521 "MSNSLP/1.0 200 OK\r\n".
\r
2522 "To: <msnmsgr:".$from_email.">\r\n".
\r
2523 "From: <msnmsgr:".$this->user.">\r\n".
\r
2524 "Via: ".$MsgBody['Via']."\r\n".
\r
2525 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
2526 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2527 "Max-Forwards: 0\r\n".
\r
2528 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
2529 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
2531 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2532 $hdr_TotalDataSizeHigh=0;
\r
2533 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2536 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2537 strlen($MessagePayload),
\r
2544 "MIME-Version: 1.0\r\n".
\r
2545 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2546 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2547 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2549 $this->SB_writedata($message);
\r
2550 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
2551 $this->SB_readln();//Read ACK;
\r
2553 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
2554 //send Data preparation message
\r
2555 //send 4 null bytes as data
\r
2556 $hdr_TotalDataSizeLow=4;
\r
2557 $hdr_TotalDataSizeHigh=0;
\r
2559 $hdr = pack("LLLLLLLLLLLL",
\r
2560 $MsgBody['SessionID'],
\r
2563 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2564 $hdr_TotalDataSizeLow,
\r
2570 "MIME-Version: 1.0\r\n".
\r
2571 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2572 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";
\r
2573 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2575 $this->SB_writedata($message);
\r
2576 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
2577 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
2578 $this->SB_readln();//Read ACK;
\r
2580 //send Data Content..
\r
2581 $footer=pack('N',1);
\r
2583 $FileSize=filesize($PictureFilePath);
\r
2584 if($hTitle=fopen($PictureFilePath,'rb'))
\r
2588 while(!feof($hTitle))
\r
2590 $FileContent=fread($hTitle,1024);
\r
2591 $FileContentSize=strlen($FileContent);
\r
2592 $hdr = pack("LLLLLLLLLLLL",
\r
2593 $MsgBody['SessionID'],
\r
2604 "MIME-Version: 1.0\r\n".
\r
2605 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2606 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
2607 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2609 $this->SB_writedata($message);
\r
2610 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
2611 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
2612 //$this->SB_readln();//Read ACK;
\r
2613 $Offset+=$FileContentSize;
\r
2618 $MessageContent="\r\n".pack("C", 0);
\r
2620 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
2621 "To: <msnmsgr:$from_email>\r\n".
\r
2622 "From: <msnmsgr:".$this->user.">\r\n".
\r
2623 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
2625 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2626 "Max-Forwards: 0\r\n".
\r
2627 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
2628 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
2629 $footer=pack('N',0);
\r
2630 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2631 $hdr_TotalDataSizeHigh=0;
\r
2633 $hdr = pack("LLLLLLLLLLLL",
\r
2637 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2644 "MIME-Version: 1.0\r\n".
\r
2645 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2646 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2647 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2649 $this->SB_writedata($message);
\r
2650 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
2655 //if ($hdr_Flag == 2) {
\r
2656 // just send ACK...
\r
2657 // $this->SB_writeln("ACK $id");
\r
2660 if ($hdr_SessionID == 4) {
\r
2662 $this->debug_message("*** p2p: ignore flag 4");
\r
2665 $finished = false;
\r
2666 if ($hdr_TotalDataSizeHigh == 0) {
\r
2667 // only 32 bites size
\r
2668 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
2672 // we won't accept any file transfer
\r
2673 // so I think we won't get any message size need to use 64 bits
\r
2674 // 64 bits size here, can't count directly...
\r
2675 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
2676 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
2677 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
2678 $now_size = bcadd($dataoffset, $messagelength);
\r
2679 if (bccomp($now_size, $totalsize) >= 0)
\r
2683 // ignore not finished split packet
\r
2684 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
2687 //$new_id = ~$hdr_Identifier;
\r
2690 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2693 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2698 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2699 $footer = pack("L", 0);
\r
2700 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2701 $len = strlen($message);
\r
2702 $this->SB_writeln("MSG $id D $len");
\r
2704 $this->SB_writedata($message);
\r
2705 $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
2706 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2710 $this->log_message("*** MSG from $from_email: $sMsg");
\r
2711 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2714 $this->log_message("*** User $user is offline. Try OIM.");
\r
2715 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2716 $this->SendMessage($Message,"$user@Offline");
\r
2720 if (is_numeric($code))
\r
2722 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2723 $this->debug_message("*** SB: $this->error");
\r
2728 $LastActive = time();
\r
2730 if (feof($this->SBFp))
\r
2732 // lost connection? error? try OIM later
\r
2733 @fclose($this->SBFp);
\r
2736 $this->SB_writeln("OUT");
\r
2737 @fclose($this->SBFp);
\r
2740 private function switchboard_control($ip, $port, $cki_code, $user, $Messages)
\r
2742 $this->SwitchBoardProcess=1;
\r
2743 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2744 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2747 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2750 return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));
\r
2752 private function switchboard_ring($ip, $port, $sid, $ticket,$user)
\r
2754 $this->SwitchBoardProcess=2;
\r
2755 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2756 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2759 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2762 return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));
\r
2765 private function sendOIM($to, $sMessage, $lockkey)
\r
2767 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2768 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2769 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2770 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
2772 <From memberName="'.$this->user.'"
\r
2773 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
2776 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
2777 msnpVer="'.$this->protocol.'"
\r
2778 buildVer="'.$this->buildver.'"/>
\r
2779 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2780 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
2781 appid="'.$this->prod_id.'"
\r
2782 lockkey="'.$lockkey.'"
\r
2783 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2784 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
2785 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
2786 <MessageNumber>1</MessageNumber>
\r
2790 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
2791 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
2792 Content-Type: text/plain; charset=UTF-8
\r
2793 Content-Transfer-Encoding: base64
\r
2794 X-OIM-Message-Type: OfflineMessage
\r
2795 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
2796 X-OIM-Sequence-Num: 1
\r
2798 '.chunk_split(base64_encode($sMessage)).'
\r
2801 </soap:Envelope>';
\r
2803 $header_array = array(
\r
2804 'SOAPAction: '.$this->oim_send_soap,
\r
2805 'Content-Type: text/xml',
\r
2806 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
2809 $this->debug_message("*** URL: $this->oim_send_url");
\r
2810 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2811 $curl = curl_init();
\r
2812 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
2813 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2814 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2815 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2816 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2817 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2818 curl_setopt($curl, CURLOPT_POST, 1);
\r
2819 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2820 $data = curl_exec($curl);
\r
2821 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2822 curl_close($curl);
\r
2823 $this->debug_message("*** Get Result:\n$data");
\r
2825 if ($http_code == 200) {
\r
2826 $this->debug_message("*** OIM sent for $to");
\r
2830 $challenge = false;
\r
2831 $auth_policy = false;
\r
2832 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
2833 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
2834 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
2835 if (count($matches) != 0) {
\r
2836 // yes, we get new LockKeyChallenge
\r
2837 $challenge = $matches[2];
\r
2838 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
2840 // auth policy error
\r
2841 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
2842 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
2843 if (count($matches) != 0) {
\r
2844 $auth_policy = $matches[2];
\r
2845 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
2847 if ($auth_policy === false && $challenge === false) {
\r
2848 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
2849 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
2850 if (count($matches) == 0) {
\r
2851 // no error, we assume the OIM is sent
\r
2852 $this->debug_message("*** OIM sent for $to");
\r
2855 $err_code = $matches[2];
\r
2856 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
2857 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
2858 if (count($matches) > 0)
\r
2859 $err_msg = $matches[1];
\r
2862 $this->debug_message("*** OIM failed for $to");
\r
2863 $this->debug_message("*** OIM Error code: $err_code");
\r
2864 $this->debug_message("*** OIM Error Message: $err_msg");
\r
2867 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
2870 // read data for specified size
\r
2871 private function ns_readdata($size) {
\r
2874 while (!feof($this->NSfp)) {
\r
2875 $buf = @fread($this->NSfp, $size - $count);
\r
2877 $count += strlen($buf);
\r
2878 if ($count >= $size) break;
\r
2880 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2885 private function ns_readln() {
\r
2886 $data = @fgets($this->NSfp, 4096);
\r
2887 if ($data !== false) {
\r
2888 $data = trim($data);
\r
2889 $this->debug_message("NS: <<< $data");
\r
2894 // write to server, append \r\n, also increase id
\r
2895 private function ns_writeln($data) {
\r
2896 @fwrite($this->NSfp, $data."\r\n");
\r
2897 $this->debug_message("NS: >>> $data");
\r
2902 // write data to server
\r
2903 private function ns_writedata($data) {
\r
2904 @fwrite($this->NSfp, $data);
\r
2905 $this->debug_message("NS: >>> $data");
\r
2909 // read data for specified size for SB
\r
2910 private function sb_readdata($size) {
\r
2913 while (!feof($this->SBFp)) {
\r
2914 $buf = @fread($this->SBFp, $size - $count);
\r
2916 $count += strlen($buf);
\r
2917 if ($count >= $size) break;
\r
2919 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2923 // read one line for SB
\r
2924 private function sb_readln() {
\r
2925 $data = @fgets($this->SBFp, 4096);
\r
2926 if ($data !== false) {
\r
2927 $data = trim($data);
\r
2928 $this->debug_message("SB: <<< $data");
\r
2933 // write to server for SB, append \r\n, also increase id
\r
2934 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
2935 private function sb_writeln($data) {
\r
2936 @fwrite($this->SBFp, $data."\r\n");
\r
2937 $this->debug_message("SB: >>> $data");
\r
2942 // write data to server
\r
2943 private function sb_writedata($data) {
\r
2944 @fwrite($this->SBFp, $data);
\r
2945 $this->debug_message("SB: >>> $data");
\r
2949 // show debug information
\r
2950 function debug_message($str) {
\r
2951 if (!$this->debug) return;
\r
2952 if($this->debug===STDOUT) echo $str."\n";
\r
2953 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
2954 $fp = fopen($fname, 'at');
\r
2956 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2960 // still show debug information, if we can't open log_file
\r
2965 function dump_binary($str) {
\r
2969 $len = strlen($str);
\r
2970 for ($i = 0; $i < $len; $i++) {
\r
2971 if (($i % 16) == 0) {
\r
2972 if ($buf !== '') {
\r
2973 $buf .= "$h_str $a_str\n";
\r
2975 $buf .= sprintf("%04X:", $i);
\r
2979 $ch = ord($str[$i]);
\r
2983 $a_str .= chr($ch);
\r
2984 $h_str .= sprintf(" %02X", $ch);
\r
2986 if ($h_str !== '')
\r
2987 $buf .= "$h_str $a_str\n";
\r
2992 function log_message($str) {
\r
2993 /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';
\r
2994 $fp = fopen($fname, 'at');
\r
2996 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2999 $this->debug_message($str);
\r
3004 * @param $FilePath 圖檔路徑
\r
3005 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
3008 private function MsnObj($FilePath,$Type=3)
\r
3010 if(!($FileSize=filesize($FilePath))) return '';
\r
3011 $Location=md5($FilePath);
\r
3012 $Friendly=md5($FilePath.$Type);
\r
3013 if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
3014 $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));
\r
3015 $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
3016 $this->MsnObjArray[$Location]=$FilePath;
\r
3017 $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
3018 $this->MsnObjMap[$Location]=$MsnObj;
\r
3019 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
3022 private function linetoArray($lines) {
\r
3023 $lines=str_replace("\r",'',$lines);
\r
3024 $lines=explode("\n",$lines);
\r
3025 foreach($lines as $line) {
\r
3026 if(!isset($line{3})) continue;
\r
3027 list($Key,$Val)=explode(':',$line);
\r
3028 $Data[trim($Key)]=trim($Val);
\r
3032 private function GetPictureFilePath($Context)
\r
3034 $MsnObj=base64_decode($Context);
\r
3035 if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))
\r
3036 $location=$Match[1];
\r
3037 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
3038 if($location&&(isset($this->MsnObjArray[$location])))
\r
3039 return $this->MsnObjArray[$location];
\r
3042 private function GetMsnObjDefine($Message)
\r
3045 if(is_array($this->Emotions))
\r
3046 foreach($this->Emotions as $Pattern => $FilePath)
\r
3048 if(strpos($Message,$Pattern)!==false)
\r
3049 $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";
\r
3051 return $DefineString;
\r
3054 * Receive Message Overload Function
\r
3057 * @param $Network 1 => msn , 32 =>yahoo
\r
3059 * @return unknown_type
\r
3061 protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}
\r
3063 * Remove Us From Member List Overload Function
\r
3066 * @param $Network 1 => msn , 32 =>yahoo
\r
3067 * @return unknown_type
\r
3069 protected function RemoveUsFromMemberList($User,$Network){}
\r
3071 * Add Us to Member List Overload Function
\r
3074 * @param $Network 1 => msn , 32 =>yahoo
\r
3075 * @return unknown_type
\r
3077 protected function AddUsToMemberList($User,$Network){}
\r
3079 public function signon() {
\r
3080 $this->log_message("*** try to connect to MSN network");
\r
3081 while(!$this->connect($this->user, $this->password))
\r
3083 $this->log_message("!!! Can't connect to server: $this->error");
\r
3084 if(!$this->NSRetryWait($this->retry_wait)) return;
\r
3086 $this->UpdateContacts();
\r
3087 $this->LastPing=time();
\r
3088 $this->log_message("*** connected, wait for command");
\r
3089 $start_tm = time();
\r
3090 $ping_tm = time();
\r
3091 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
3092 $this->aContactList = $this->getMembershipList(true);
\r
3093 if ($this->update_pending) {
\r
3094 if (is_array($this->aContactList)) {
\r
3095 $pending = 'Pending';
\r
3096 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3097 foreach ($aUserList as $u_name => $aNetworks) {
\r
3098 foreach ($aNetworks as $network => $aData) {
\r
3099 if (isset($aData[$pending])) {
\r
3102 foreach (array('Allow', 'Reverse') as $list) {
\r
3103 if (isset($aData[$list]))
\r
3106 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
3107 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3113 $id = $aData[$pending];
\r
3114 // we can delete it from pending now
\r
3115 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
3116 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
3121 foreach (array('Allow', 'Reverse') as $list) {
\r
3122 if (!isset($aData[$list])) {
\r
3123 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3124 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3136 if (is_array($this->aContactList)) {
\r
3137 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3138 $str = '<d n="'.$u_domain.'">';
\r
3139 $len += strlen($str);
\r
3140 if ($len > 7400) {
\r
3141 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3144 $len = strlen($str);
\r
3147 foreach ($aUserList as $u_name => $aNetworks) {
\r
3148 foreach ($aNetworks as $network => $status) {
\r
3149 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
3150 $len += strlen($str);
\r
3151 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
3153 if ($len > 7475) {
\r
3155 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3157 $sList = '<d n="'.$u_domain.'">'.$str;
\r
3158 $len = strlen($sList);
\r
3167 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3168 // NS: >>> BLP {id} BL
\r
3169 $this->ns_writeln("BLP $this->id BL");
\r
3170 foreach ($aADL as $str) {
\r
3171 $len = strlen($str);
\r
3172 // NS: >>> ADL {id} {size}
\r
3173 $this->ns_writeln("ADL $this->id $len");
\r
3174 $this->ns_writedata($str);
\r
3176 // NS: >>> PRP {id} MFN name
\r
3177 if ($this->alias == '') $this->alias = $user;
\r
3178 $aliasname = rawurlencode($this->alias);
\r
3179 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
3181 //$MsnObj=$this->PhotoStckObj();
\r
3182 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
3183 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3184 if($this->PhotoStickerFile!==false)
\r
3185 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3186 // NS: >>> UUX {id} length
\r
3187 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
3188 $len = strlen($str);
\r
3189 $this->ns_writeln("UUX $this->id $len");
\r
3190 $this->ns_writedata($str);
\r
3193 public function NSreceive() {
\r
3194 $this->log_message("*** startup ***");
\r
3198 // Sign in again if not signed in or socket failed
\r
3199 if (!is_resource($this->NSfp) || feof($this->NSfp)) {
\r
3203 $data = $this->ns_readln();
\r
3204 /*if($data===false)
\r
3206 //If No NS Message Process SendMessageFileQueue
\r
3207 if (time()-$this->LastPing > $this->ping_wait)
\r
3210 $this->ns_writeln("PNG");
\r
3211 $this->LastPing = time();
\r
3213 if(count($this->ChildProcess)<$this->MAXChildProcess)
\r
3216 foreach($this->MessageQueue as $User => $Message)
\r
3218 if(!trim($User)) continue;
\r
3219 if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;
\r
3220 if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))
\r
3222 $this->MessageQueue[$User]['XFRSent']=true;
\r
3223 $this->MessageQueue[$User]['ReqTime']=time();
\r
3224 $this->log_message("*** Request SB for $User");
\r
3225 $this->ns_writeln("XFR $this->id SB");
\r
3230 if($this->ProcessSendMessageFileQueue()) continue;
\r
3233 switch (substr($data,0,3))
\r
3236 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
3237 // NS: <<< SBS 0 null
\r
3242 // NS: <<< RFS ???
\r
3243 // refresh ADL, so we re-send it again
\r
3244 if (is_array($aADL)) {
\r
3245 foreach ($aADL as $str) {
\r
3246 $len = strlen($str);
\r
3247 // NS: >>> ADL {id} {size}
\r
3248 $this->ns_writeln("ADL $this->id $len");
\r
3249 $this->ns_writedata($str);
\r
3255 // NS: <<< LST {email} {alias} 11 0
\r
3256 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
3257 @list($u_name, $u_domain) = @explode('@', $email);
\r
3258 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
3259 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
3260 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
3265 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
3266 // NS: <<< ADL 0 {size}
\r
3267 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
3268 if (is_numeric($size) && $size > 0)
\r
3270 $data = $this->ns_readdata($size);
\r
3271 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3272 if (is_array($matches) && count($matches) > 0)
\r
3274 $u_domain = $matches[1];
\r
3275 $u_name = $matches[2];
\r
3276 $network = $matches[4];
\r
3277 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3278 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
3281 $re_login = false;
\r
3283 foreach (array('Allow', 'Reverse') as $list)
\r
3285 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3288 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3291 $aTickets = $this->get_passport_ticket();
\r
3292 if (!$aTickets || !is_array($aTickets)) {
\r
3293 // failed to login? ignore it
\r
3294 $this->log_message("*** can't re-login, something wrong here");
\r
3295 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3299 $this->ticket = $aTickets;
\r
3300 $this->log_message("**** get new ticket, try it again");
\r
3301 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3303 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3307 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3310 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
3312 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
3313 $len = strlen($str);
\r
3316 $this->log_message("*** someone add us to their list: $data");
\r
3317 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
3322 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
3323 // NS: <<< RML 0 {size}
\r
3324 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
3325 if (is_numeric($size) && $size > 0)
\r
3327 $data = $this->ns_readdata($size);
\r
3328 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3329 if (is_array($matches) && count($matches) > 0)
\r
3331 $u_domain = $matches[1];
\r
3332 $u_name = $matches[2];
\r
3333 $network = $matches[4];
\r
3334 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3336 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
3337 foreach ($aData as $list => $id)
\r
3338 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
3339 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
3340 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
3343 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
3344 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
3347 $this->log_message("*** someone remove us from their list: $data");
\r
3352 // randomly, we get MSG notification from server
\r
3353 // NS: <<< MSG Hotmail Hotmail {size}
\r
3354 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
3355 if (is_numeric($size) && $size > 0) {
\r
3356 $data = $this->ns_readdata($size);
\r
3357 $aLines = @explode("\n", $data);
\r
3361 foreach ($aLines as $line) {
\r
3362 $line = rtrim($line);
\r
3364 if ($line === '') {
\r
3368 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
3369 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
3370 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
3371 // we just need text/x-msmsgsinitialmdatanotification
\r
3372 // or text/x-msmsgsoimnotification
\r
3379 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
3380 $maildata = trim(substr($line, 10));
\r
3385 $this->log_message("*** ingnore MSG for: $line");
\r
3388 if ($maildata == '') {
\r
3389 $this->log_message("*** ingnore MSG not for OIM");
\r
3392 $re_login = false;
\r
3393 if (strcasecmp($maildata, 'too-large') == 0) {
\r
3394 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
3395 $maildata = $this->getOIM_maildata();
\r
3396 if ($maildata === false) {
\r
3397 $this->log_message("*** can't get mail-data via SOAP");
\r
3398 // maybe we need to re-login again
\r
3399 $aTickets = $this->get_passport_ticket();
\r
3400 if (!$aTickets || !is_array($aTickets)) {
\r
3401 // failed to login? ignore it
\r
3402 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3406 $this->ticket = $aTickets;
\r
3407 $this->log_message("**** get new ticket, try it again");
\r
3408 $maildata = $this->getOIM_maildata();
\r
3409 if ($maildata === false) {
\r
3410 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
3415 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
3419 $start = strpos($p, '<M>');
\r
3420 $end = strpos($p, '</M>');
\r
3421 if ($start === false || $end === false || $start > $end) break;
\r
3423 $sOIM = substr($p, $start, $end - $start);
\r
3425 $p = substr($p, $end);
\r
3427 if (count($aOIMs) == 0) {
\r
3428 $this->log_message("*** ingnore empty OIM");
\r
3431 foreach ($aOIMs as $maildata) {
\r
3432 // T: 11 for MSN, 13 for Yahoo
\r
3433 // S: 6 for MSN, 7 for Yahoo
\r
3434 // RT: the datetime received by server
\r
3435 // RS: already read or not
\r
3436 // SZ: size of message
\r
3439 // F: always 00000000-0000-0000-0000-000000000009
\r
3440 // N: sender alias
\r
3441 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
3442 if (count($matches) == 0) {
\r
3443 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
3446 $oim_type = $matches[1];
\r
3447 if ($oim_type = 13)
\r
3451 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
3452 if (count($matches) == 0) {
\r
3453 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
3456 $oim_sender = $matches[1];
\r
3457 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
3458 if (count($matches) == 0) {
\r
3459 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
3462 $oim_msgid = $matches[1];
\r
3463 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
3464 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
3465 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
3466 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
3467 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
3468 $sMsg = $this->getOIM_message($oim_msgid);
\r
3469 if ($sMsg === false) {
\r
3470 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
3472 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3475 $aTickets = $this->get_passport_ticket();
\r
3476 if (!$aTickets || !is_array($aTickets)) {
\r
3477 // failed to login? ignore it
\r
3478 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3482 $this->ticket = $aTickets;
\r
3483 $this->log_message("**** get new ticket, try it again");
\r
3484 $sMsg = $this->getOIM_message($oim_msgid);
\r
3485 if ($sMsg === false) {
\r
3486 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3490 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
3492 //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
3493 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
3499 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
3500 // NS: <<< UBM {email} $network $type {size}
\r
3501 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
3502 if (is_numeric($size) && $size > 0)
\r
3504 $data = $this->ns_readdata($size);
\r
3505 $aLines = @explode("\n", $data);
\r
3509 foreach ($aLines as $line) {
\r
3510 $line = rtrim($line);
\r
3512 if ($line === '') {
\r
3516 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
3522 $aSubLines = @explode("\r", $line);
\r
3523 foreach ($aSubLines as $str) {
\r
3531 $this->log_message("*** ingnore from $from_email: $line");
\r
3534 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
3535 //$this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
3536 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
3541 // randomly, we get UBX notification from server
\r
3542 // NS: <<< UBX email {network} {size}
\r
3543 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
3544 // we don't need the notification data, so just ignore it
\r
3545 if (is_numeric($size) && $size > 0)
\r
3546 $this->ns_readdata($size);
\r
3550 // randomly, we'll get challenge from server
\r
3551 // NS: <<< CHL 0 {code}
\r
3552 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
3553 $fingerprint = $this->getChallenge($chl_code);
\r
3554 // NS: >>> QRY {id} {product_id} 32
\r
3555 // NS: >>> fingerprint
\r
3556 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
3557 $this->ns_writedata($fingerprint);
\r
3558 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3559 if($this->PhotoStickerFile!==false)
\r
3560 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3563 // NS: <<< CHG {id} {status} {code}
\r
3565 // change our status to online first
\r
3569 // sometimes, NS will redirect to another NS
\r
3571 // NS: <<< XFR {id} NS {server} 0 {server}
\r
3573 // NS: <<< XFR {id} NS {server} U D
\r
3574 // for normal switchboard XFR
\r
3575 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
3576 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
3577 @list($ip, $port) = @explode(':', $server);
\r
3578 if ($server_type != 'SB') {
\r
3580 // this connection will close after XFR
\r
3581 $this->NSLogout();
\r
3584 if(count($this->MessageQueue))
\r
3586 foreach($this->MessageQueue as $User => $Message)
\r
3588 //$this->ChildProcess[$ChildPid]
\r
3589 $this->log_message("*** XFR SB $User");
\r
3590 $pid=pcntl_fork();
\r
3594 $this->ChildProcess[$pid]=$User;
\r
3599 $this->log_message("*** Fork Error $User");
\r
3605 $this->log_message("*** Child Process Start for $User");
\r
3606 unset($Message['XFRSent']);
\r
3607 unset($Message['ReqTime']);
\r
3608 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
3609 if ($bSBresult === false)
\r
3611 // error for switchboard
\r
3612 $this->log_message("!!! error for sending message to ".$User);
\r
3617 unset($this->MessageQueue[$User]);
\r
3620 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
3621 if ($bSBresult === false) {
\r
3622 // error for switchboard
\r
3623 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
3624 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
3628 // NS: <<< QNG {time}
\r
3629 @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
3630 if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
3631 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
3632 //Mod by Ricky Set Online
\r
3636 if($this->PhotoStickerFile!==false)
\r
3637 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3639 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3640 // someone is trying to talk to us
\r
3641 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
3642 $this->log_message("NS: <<< RNG $data");
\r
3643 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
3644 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
3645 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
3646 $this->addContact($email,1,$email, true);
\r
3647 $pid=pcntl_fork();
\r
3651 $this->ChildProcess[$pid]='RNG';
\r
3656 $this->log_message("*** Fork Error $User");
\r
3662 $this->log_message("*** Ring Child Process Start for $User");
\r
3663 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
3668 // force logout from NS
\r
3669 // NS: <<< OUT xxx
\r
3670 $this->log_message("*** LOGOUT from NS");
\r
3671 return $this->NsLogout();
\r
3674 $code = substr($data,0,3);
\r
3675 if (is_numeric($code)) {
\r
3676 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
3677 $this->debug_message("*** NS: $this->error");
\r
3679 return $this->NsLogout();
\r
3685 public function SendMessage($Message, $To) {
\r
3686 if(!is_array($To))
\r
3689 foreach($To as $Email)
\r
3691 list($name,$host,$network)=explode('@',$Email);
\r
3692 $network=$network==''?1:$network;
\r
3693 if($network==1 && isset($this->switchBoardSessions[$Email]) ) {
\r
3694 $this->debug_message("*** SendMessage to $Receiver use SB message queue.");
\r
3695 array_push($this->SwitchBoardMessageQueue,$Message);
\r
3698 $Receiver.="$name@$host@$network,";
\r
3700 if($Receiver=='') return;
\r
3701 $Receiver=substr($Receiver,0,-1);
\r
3702 $this->debug_message("*** SendMessage to $Receiver use File queue.");
\r
3703 file_put_contents($FileName,"TO: $Receiver\n$Message\n");
\r
3706 public function getNSSocket() {
\r
3707 return $this->NSfp;
\r
3710 public function getSBSocket() {
\r
3711 return $this->SBfp;
\r
3714 public function getSockets() {
\r
3715 return array($this->NSfp, $this->SBfp);
\r
3719 * Calls User Handler
\r
3721 * Calls registered handler for a specific event.
\r
3723 * @param String $event Command (event) name (Rvous etc)
\r
3724 * @param String $data Raw message from server
\r
3725 * @see registerHandler
\r
3728 private function callHandler($event, $data) {
\r
3729 if (isset($this->myEventHandlers[$event])) {
\r
3730 call_user_func($this->myEventHandlers[$event], $data);
\r
3732 $this->noHandler($data);
\r
3737 * Registers a user handler
\r
3742 * @param String $event Event name
\r
3743 * @param String $handler User function to call
\r
3744 * @see callHandler
\r
3745 * @return boolean Returns true if successful
\r
3747 public function registerHandler($event, $handler) {
\r
3748 if (is_callable($handler)) {
\r
3749 $this->myEventHandlers[$event] = $handler;
\r