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
63 private $IgnoreList;
\r
65 public $server = 'messenger.hotmail.com';
\r
66 public $port = 1863;
\r
69 public $clientid = '';
\r
71 public $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
72 public $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
\r
73 public $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
74 public $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
\r
75 public $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
76 public $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
\r
78 public $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
79 public $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';
\r
81 public $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
82 public $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';
\r
84 public $addcontact_url = 'https://contacts.msn.com/abservice/abservice.asmx';
\r
85 public $addcontact_soap = 'http://www.msn.com/webservices/AddressBook/ABContactAdd';
\r
87 public $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
88 public $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
\r
93 public $authed = false;
\r
95 public $oim_try = 3;
\r
97 public $log_file = '';
\r
99 public $log_path = false;
\r
101 public $font_fn = 'Arial';
\r
102 public $font_co = '333333';
\r
103 public $font_ef = '';
\r
106 // the message length (include header) is limited (maybe since WLM 8.5 released)
\r
107 // for WLM: 1664 bytes
\r
108 // for YIM: 518 bytes
\r
109 public $max_msn_message_len = 1664;
\r
110 public $max_yahoo_message_len = 518;
\r
112 private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)
\r
115 foreach($Array as $Key => $Val)
\r
117 if($Key{0}==':') continue;
\r
119 if(is_array($Val[':']))
\r
121 foreach($Val[':'] as $AttribName => $AttribVal)
\r
122 $Attrib.=" $AttribName='$AttribVal'";
\r
127 $Key=substr($Key,1);
\r
128 foreach($Val as $ListKey => $ListVal)
\r
130 if($ListKey{0}==':') continue;
\r
131 if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);
\r
132 elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';
\r
133 $ArrayString.="<$Key$Attrib>$ListVal</$Key>";
\r
137 if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);
\r
138 elseif(is_bool($Val)) $Val=$Val?'true':'false';
\r
139 $ArrayString.="<$Key$Attrib>$Val</$Key>";
\r
141 if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);
\r
142 return $ArrayString;
\r
145 public function End()
\r
147 $this->log_message("*** someone kill me ***");
\r
148 $this->kill_me=true;
\r
150 private function IsIgnoreMail($Email)
\r
152 if($this->IgnoreList==false) return false;
\r
153 foreach($this->IgnoreList as $Pattern)
\r
155 if(preg_match($Pattern,$Email)) return true;
\r
159 public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)
\r
161 $this->user = $Configs['user'];
\r
162 $this->password = $Configs['password'];
\r
163 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
164 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
165 $my_add_function = isset($Configs['add_user_function']) ? $Configs['add_user_function'] : false;
\r
166 $my_rem_function = isset($Configs['remove_user_function']) ? $Configs['remove_user_function'] : false;
\r
167 $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;
\r
168 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
169 $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;
\r
170 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
171 $this->PhotoStickerFile=$Configs['PhotoSticker'];
\r
172 $this->IgnoreList=isset($Configs['IgnoreList'])?$Configs['IgnoreList']:false;
\r
173 if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)
\r
175 foreach($this->Emotions as $EmotionFilePath)
\r
176 $this->MsnObj($EmotionFilePath,$Type=2);
\r
178 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
179 $this->timeout = $timeout;
\r
181 if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");
\r
182 if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");
\r
183 if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");
\r
185 if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");
\r
186 if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");
\r
189 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
191 normal MSN 8.1 clientid is:
\r
192 01110110 01001100 11000000 00101100
\r
195 we just use following:
\r
196 * 0x04: Your client can send/receive Ink (GIF format)
\r
197 * 0x08: Your client can send/recieve Ink (ISF format)
\r
198 * 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
199 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
202 $this->clientid = $client_id;
\r
203 $this->windows =(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
\r
204 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
207 private function get_passport_ticket($url = '')
\r
209 $user = $this->user;
\r
210 $password = htmlspecialchars($this->password);
\r
213 $passport_url = $this->passport_url;
\r
215 $passport_url = $url;
\r
217 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
218 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
219 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
220 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
221 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
222 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
223 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
224 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
225 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
227 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
228 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
229 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
230 <ps:UIVersion>1</ps:UIVersion>
\r
231 <ps:Cookies></ps:Cookies>
\r
232 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
235 <wsse:UsernameToken Id="user">
\r
236 <wsse:Username>'.$user.'</wsse:Username>
\r
237 <wsse:Password>'.$password.'</wsse:Password>
\r
238 </wsse:UsernameToken>
\r
242 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
243 <wst:RequestSecurityToken Id="RST0">
\r
244 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
246 <wsa:EndpointReference>
\r
247 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
248 </wsa:EndpointReference>
\r
250 </wst:RequestSecurityToken>
\r
251 <wst:RequestSecurityToken Id="RST1">
\r
252 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
254 <wsa:EndpointReference>
\r
255 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
256 </wsa:EndpointReference>
\r
258 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
259 </wst:RequestSecurityToken>
\r
260 <wst:RequestSecurityToken Id="RST2">
\r
261 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
263 <wsa:EndpointReference>
\r
264 <wsa:Address>messenger.msn.com</wsa:Address>
\r
265 </wsa:EndpointReference>
\r
267 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
268 </wst:RequestSecurityToken>
\r
269 <wst:RequestSecurityToken Id="RST3">
\r
270 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
272 <wsa:EndpointReference>
\r
273 <wsa:Address>contacts.msn.com</wsa:Address>
\r
274 </wsa:EndpointReference>
\r
276 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
277 </wst:RequestSecurityToken>
\r
278 <wst:RequestSecurityToken Id="RST4">
\r
279 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
281 <wsa:EndpointReference>
\r
282 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
283 </wsa:EndpointReference>
\r
285 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
286 </wst:RequestSecurityToken>
\r
287 <wst:RequestSecurityToken Id="RST5">
\r
288 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
290 <wsa:EndpointReference>
\r
291 <wsa:Address>spaces.live.com</wsa:Address>
\r
292 </wsa:EndpointReference>
\r
294 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
295 </wst:RequestSecurityToken>
\r
296 <wst:RequestSecurityToken Id="RST6">
\r
297 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
299 <wsa:EndpointReference>
\r
300 <wsa:Address>storage.msn.com</wsa:Address>
\r
301 </wsa:EndpointReference>
\r
303 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
304 </wst:RequestSecurityToken>
\r
305 </ps:RequestMultipleSecurityTokens>
\r
309 $this->debug_message("*** URL: $passport_url");
\r
310 $this->debug_message("*** Sending SOAP:\n$XML");
\r
311 $curl = curl_init();
\r
312 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
313 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
314 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
315 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
316 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
317 curl_setopt($curl, CURLOPT_POST, 1);
\r
318 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
319 $data = curl_exec($curl);
\r
320 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
322 $this->debug_message("*** Get Result:\n$data");
\r
324 if ($http_code != 200) {
\r
325 // sometimes, rediret to another URL
\r
327 //<faultcode>psf:Redirect</faultcode>
\r
328 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
329 //<faultstring>Authentication Failure</faultstring>
\r
330 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
331 $this->debug_message("*** Can't get passport ticket! http code = $http_code");
\r
334 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
335 if (count($matches) == 0) {
\r
336 $this->debug_message("*** redirect, but can't get redirect URL!");
\r
339 $redirect_url = $matches[1];
\r
340 if ($redirect_url == $passport_url) {
\r
341 $this->debug_message("*** redirect, but redirect to same URL!");
\r
344 $this->debug_message("*** redirect to $redirect_url");
\r
345 return $this->get_passport_ticket($redirect_url);
\r
348 // sometimes, rediret to another URL, also return 200
\r
350 //<faultcode>psf:Redirect</faultcode>
\r
351 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
352 //<faultstring>Authentication Failure</faultstring>
\r
353 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
354 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
355 if (count($matches) != 0) {
\r
356 $redirect_url = $matches[1];
\r
357 if ($redirect_url == $passport_url) {
\r
358 $this->debug_message("*** redirect, but redirect to same URL!");
\r
361 $this->debug_message("*** redirect to $redirect_url");
\r
362 return $this->get_passport_ticket($redirect_url);
\r
366 // no Redurect faultcode or URL
\r
367 // we should get the ticket here
\r
369 // we need ticket and secret code
\r
370 // RST1: messengerclear.live.com
\r
371 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
372 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
373 // RST2: messenger.msn.com
\r
374 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
375 // RST3: contacts.msn.com
\r
376 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
377 // RST4: messengersecure.live.com
\r
378 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
379 // RST5: spaces.live.com
\r
380 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
381 // RST6: storage.msn.com
\r
382 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
384 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
385 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
386 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
387 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
388 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
389 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
390 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
394 // no ticket found!
\r
395 if (count($matches) == 0) {
\r
396 $this->debug_message("*** Can't get passport ticket!");
\r
400 //$this->debug_message(var_export($matches, true));
\r
401 // matches[0]: all data
\r
402 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
404 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
406 // matches[5]: RST2 (messenger.msn.com) ticket
\r
408 // matches[7]: RST3 (contacts.msn.com) ticket
\r
410 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
411 // matches[10]: ...
\r
412 // matches[11]: RST5 (spaces.live.com) ticket
\r
413 // matches[12]: ...
\r
414 // matches[13]: RST6 (storage.live.com) ticket
\r
415 // matches[14]: ...
\r
418 // ticket => $matches[1]
\r
419 // secret => $matches[3]
\r
420 // web_ticket => $matches[5]
\r
421 // contact_ticket => $matches[7]
\r
422 // oim_ticket => $matches[9]
\r
423 // space_ticket => $matches[11]
\r
424 // storage_ticket => $matches[13]
\r
426 // yes, we get ticket
\r
428 'ticket' => html_entity_decode($matches[1]),
\r
429 'secret' => html_entity_decode($matches[3]),
\r
430 'web_ticket' => html_entity_decode($matches[5]),
\r
431 'contact_ticket' => html_entity_decode($matches[7]),
\r
432 'oim_ticket' => html_entity_decode($matches[9]),
\r
433 'space_ticket' => html_entity_decode($matches[11]),
\r
434 'storage_ticket' => html_entity_decode($matches[13])
\r
436 $this->ticket=$aTickets;
\r
437 $this->debug_message(var_export($aTickets, true));
\r
438 $ABAuthHeaderArray=array(
\r
439 'ABAuthHeader'=>array(
\r
440 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
441 'ManagedGroupRequest'=>false,
\r
442 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),
\r
445 $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));
\r
446 file_put_contents('/tmp/STTicket.txt',htmlspecialchars($this->ticket['storage_ticket']));
\r
447 //$this->debug_message("StorageTicket:\n",htmlspecialchars($this->ticket['storage_ticket']));
\r
450 private function UpdateContacts()
\r
452 $ABApplicationHeaderArray=array(
\r
453 'ABApplicationHeader'=>array(
\r
454 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
455 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
456 'IsMigration'=>false,
\r
457 'PartnerScenario'=>'ContactSave'
\r
460 $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
461 $ABFindAllArray=array(
\r
462 'ABFindAll'=>array(
\r
463 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
464 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
466 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',
\r
469 $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');
\r
470 $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));
\r
471 $this->Contacts=array();
\r
474 $this->debug_message("*** Update Contacts...");
\r
475 $Result=$this->ABService->ABFindAll($ABFindAll);
\r
476 $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());
\r
477 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
478 $this->Contacts[$Contact->contactInfo->passportName]=$Contact;
\r
480 catch(Exception $e)
\r
482 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
485 protected function addContact($email, $network, $display = '', $sendADL = false)
\r
487 if ($network != 1) return true;
\r
488 if(isset($this->Contacts[$email])) return true;
\r
490 $ABContactAddArray=array(
\r
491 'ABContactAdd'=>array(
\r
492 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
493 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
496 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
497 'contactInfo'=>array(
\r
498 'contactType'=>'LivePending',
\r
499 'passportName'=>$email,
\r
500 'isMessengerUser'=>true,
\r
501 'MessengerMemberInfo'=>array(
\r
502 'DisplayName'=>$email
\r
508 'EnableAllowListManagement'=>true
\r
512 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
515 $this->debug_message("*** Add Contacts $email...");
\r
516 $this->ABService->ABContactAdd($ABContactAdd);
\r
518 catch(Exception $e)
\r
520 $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
522 if ($sendADL && !feof($this->NSfp)) {
\r
523 @list($u_name, $u_domain) = @explode('@', $email);
\r
524 foreach (array('1', '2') as $l) {
\r
525 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
526 $len = strlen($str);
\r
527 // NS: >>> ADL {id} {size}
\r
528 $this->ns_writeln("ADL $this->id $len");
\r
529 $this->ns_writedata($str);
\r
532 $this->UpdateContacts();
\r
536 function delMemberFromList($memberID, $email, $network, $list) {
\r
537 if ($network != 1 && $network != 32) return true;
\r
538 if ($memberID === false) return true;
\r
540 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
542 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
543 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
544 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
545 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
546 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
548 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
549 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
550 <IsMigration>false</IsMigration>
\r
551 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
552 </ABApplicationHeader>
\r
553 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
554 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
555 <TicketToken>'.$ticket.'</TicketToken>
\r
559 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
562 <Type>Messenger</Type>
\r
563 <ForeignId></ForeignId>
\r
567 <MemberRole>'.$list.'</MemberRole>
\r
569 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
570 <Type>Passport</Type>
\r
571 <MembershipId>'.$memberID.'</MembershipId>
\r
572 <State>Accepted</State>
\r
581 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
582 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
583 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
584 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
585 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
587 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
588 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
589 <IsMigration>false</IsMigration>
\r
590 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
591 </ABApplicationHeader>
\r
592 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
593 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
594 <TicketToken>'.$ticket.'</TicketToken>
\r
598 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
601 <Type>Messenger</Type>
\r
602 <ForeignId></ForeignId>
\r
606 <MemberRole>'.$list.'</MemberRole>
\r
608 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
610 <MembershipId>'.$memberID.'</MembershipId>
\r
611 <State>Accepted</State>
\r
620 $header_array = array(
\r
621 'SOAPAction: '.$this->delmember_soap,
\r
622 'Content-Type: text/xml; charset=utf-8',
\r
623 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
626 $this->debug_message("*** URL: $this->delmember_url");
\r
627 $this->debug_message("*** Sending SOAP:\n$XML");
\r
628 $curl = curl_init();
\r
629 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
630 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
631 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
632 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
633 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
634 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
635 curl_setopt($curl, CURLOPT_POST, 1);
\r
636 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
637 $data = curl_exec($curl);
\r
638 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
640 $this->debug_message("*** Get Result:\n$data");
\r
642 if ($http_code != 200) {
\r
643 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
644 if (count($matches) == 0) {
\r
645 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
\r
648 $faultcode = trim($matches[1]);
\r
649 $faultstring = trim($matches[2]);
\r
650 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
651 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
\r
654 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
\r
657 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
\r
661 function addMemberToList($email, $network, $list) {
\r
662 if ($network != 1 && $network != 32) return true;
\r
663 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
667 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
668 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
669 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
670 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
671 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
673 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
674 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
675 <IsMigration>false</IsMigration>
\r
676 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
677 </ABApplicationHeader>
\r
678 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
679 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
680 <TicketToken>'.$ticket.'</TicketToken>
\r
684 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
687 <Type>Messenger</Type>
\r
688 <ForeignId></ForeignId>
\r
692 <MemberRole>'.$list.'</MemberRole>
\r
694 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
695 <Type>Passport</Type>
\r
696 <State>Accepted</State>
\r
697 <PassportName>'.$user.'</PassportName>
\r
706 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
707 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
708 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
709 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
710 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
712 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
713 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
714 <IsMigration>false</IsMigration>
\r
715 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
716 </ABApplicationHeader>
\r
717 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
718 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
719 <TicketToken>'.$ticket.'</TicketToken>
\r
723 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
726 <Type>Messenger</Type>
\r
727 <ForeignId></ForeignId>
\r
731 <MemberRole>'.$list.'</MemberRole>
\r
733 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
735 <State>Accepted</State>
\r
736 <Email>'.$user.'</Email>
\r
739 <Name>MSN.IM.BuddyType</Name>
\r
740 <Value>32:YAHOO</Value>
\r
750 $header_array = array(
\r
751 'SOAPAction: '.$this->addmember_soap,
\r
752 'Content-Type: text/xml; charset=utf-8',
\r
753 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
756 $this->debug_message("*** URL: $this->addmember_url");
\r
757 $this->debug_message("*** Sending SOAP:\n$XML");
\r
758 $curl = curl_init();
\r
759 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
760 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
761 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
762 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
763 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
764 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
765 curl_setopt($curl, CURLOPT_POST, 1);
\r
766 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
767 $data = curl_exec($curl);
\r
768 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
770 $this->debug_message("*** Get Result:\n$data");
\r
772 if ($http_code != 200) {
\r
773 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
774 if (count($matches) == 0) {
\r
775 $this->log_message("*** can't add member (network: $network) $email to $list");
\r
778 $faultcode = trim($matches[1]);
\r
779 $faultstring = trim($matches[2]);
\r
780 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
781 $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
\r
784 $this->log_message("*** add member (network: $network) $email to $list, already exist!");
\r
787 $this->log_message("*** add member (network: $network) $email to $list");
\r
791 function getMembershipList($returnData=false) {
\r
792 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
793 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
794 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
795 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
796 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
797 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
799 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
800 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
801 <IsMigration>false</IsMigration>
\r
802 <PartnerScenario>Initial</PartnerScenario>
\r
803 </ABApplicationHeader>
\r
804 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
805 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
806 <TicketToken>'.$ticket.'</TicketToken>
\r
810 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
813 <ServiceType>Messenger</ServiceType>
\r
814 <ServiceType>Invitation</ServiceType>
\r
815 <ServiceType>SocialNetwork</ServiceType>
\r
816 <ServiceType>Space</ServiceType>
\r
817 <ServiceType>Profile</ServiceType>
\r
823 $header_array = array(
\r
824 'SOAPAction: '.$this->membership_soap,
\r
825 'Content-Type: text/xml; charset=utf-8',
\r
826 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
828 $this->debug_message("*** URL: $this->membership_url");
\r
829 $this->debug_message("*** Sending SOAP:\n$XML");
\r
830 $curl = curl_init();
\r
831 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
832 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
833 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
834 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
835 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
836 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
837 curl_setopt($curl, CURLOPT_POST, 1);
\r
838 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
839 $data = curl_exec($curl);
\r
840 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
842 $this->debug_message("*** Get Result:\n$data");
\r
843 if($http_code != 200) return array();
\r
845 $aMemberships = array();
\r
847 //$this->debug_message("search p = $p");
\r
848 $start = strpos($p, '<Membership>');
\r
849 $end = strpos($p, '</Membership>');
\r
850 if ($start === false || $end === false || $start > $end) break;
\r
851 //$this->debug_message("start = $start, end = $end");
\r
853 $sMembership = substr($p, $start, $end - $start);
\r
854 $aMemberships[] = $sMembership;
\r
855 //$this->debug_message("add sMembership = $sMembership");
\r
856 $p = substr($p, $end);
\r
858 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
860 $aContactList = array();
\r
861 foreach ($aMemberships as $sMembership) {
\r
862 //$this->debug_message("sMembership = $sMembership");
\r
863 if (isset($matches)) unset($matches);
\r
864 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
865 if (count($matches) == 0) continue;
\r
866 $sMemberRole = $matches[1];
\r
867 //$this->debug_message("MemberRole = $sMemberRole");
\r
868 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
870 if (isset($aMembers)) unset($aMembers);
\r
871 $aMembers = array();
\r
873 //$this->debug_message("search p = $p");
\r
874 $start = strpos($p, '<Member xsi:type="');
\r
875 $end = strpos($p, '</Member>');
\r
876 if ($start === false || $end === false || $start > $end) break;
\r
877 //$this->debug_message("start = $start, end = $end");
\r
879 $sMember = substr($p, $start, $end - $start);
\r
880 $aMembers[] = $sMember;
\r
881 //$this->debug_message("add sMember = $sMember");
\r
882 $p = substr($p, $end);
\r
884 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
885 foreach ($aMembers as $sMember) {
\r
886 //$this->debug_message("sMember = $sMember");
\r
887 if (isset($matches)) unset($matches);
\r
888 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
889 if (count($matches) == 0) continue;
\r
890 $sMemberType = $matches[1];
\r
891 //$this->debug_message("MemberType = $sMemberType");
\r
893 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
894 if (count($matches) == 0) continue;
\r
896 if ($sMemberType == 'PassportMember') {
\r
897 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
899 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
901 else if ($sMemberType == 'EmailMember') {
\r
902 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
903 // Value is 32: or 32:YAHOO
\r
904 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
905 if (count($matches) == 0) continue;
\r
906 if ($matches[1] != 32) continue;
\r
908 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
910 if ($network == -1) continue;
\r
911 if (count($matches) > 0) {
\r
912 $email = $matches[1];
\r
913 @list($u_name, $u_domain) = @explode('@', $email);
\r
914 if ($u_domain == NULL) continue;
\r
915 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
916 $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
920 return $aContactList;
\r
923 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
925 if ($redirect_server === '') {
\r
926 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
\r
927 if (!$this->NSfp) {
\r
928 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
\r
933 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);
\r
934 if (!$this->NSfp) {
\r
935 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
940 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
941 $this->authed = false;
\r
943 // NS: >> VER {id} MSNP9 CVR0
\r
945 // NS: >>> VER {id} MSNP15 CVR0
\r
946 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
948 $start_tm = time();
\r
949 while (!feof($this->NSfp))
\r
951 $data = $this->ns_readln();
\r
953 if ($data === false) {
\r
954 if ($this->timeout > 0) {
\r
956 $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
\r
957 if ($used_time > $this->timeout) {
\r
960 $this->ns_writeln("OUT");
\r
961 fclose($this->NSfp);
\r
962 $this->error = 'Timeout, maybe protocol changed!';
\r
963 $this->debug_message("*** $this->error");
\r
969 $code = substr($data, 0, 3);
\r
970 $start_tm = time();
\r
975 // NS: <<< VER {id} MSNP9 CVR0
\r
976 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
978 // NS: <<< VER {id} MSNP15 CVR0
\r
979 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
980 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
985 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
986 // NS: >>> USR {id} TWN I {user}
\r
988 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
989 // NS: >>> USR {id} SSO I {user}
\r
990 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
994 // already login for passport site, finish the login process now.
\r
995 // NS: <<< USR {id} OK {user} {verify} 0
\r
996 if ($this->authed) return true;
\r
997 // max. 16 digits for password
\r
998 if (strlen($password) > 16)
\r
999 $password = substr($password, 0, 16);
\r
1001 $this->user = $user;
\r
1002 $this->password = $password;
\r
1003 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1004 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1006 $this->passport_policy = $policy;
\r
1007 $aTickets = $this->get_passport_ticket();
\r
1008 if (!$aTickets || !is_array($aTickets)) {
\r
1011 $this->ns_writeln("OUT");
\r
1012 fclose($this->NSfp);
\r
1013 $this->error = 'Passport authenticated fail!';
\r
1014 $this->debug_message("*** $this->error");
\r
1018 $ticket = $aTickets['ticket'];
\r
1019 $secret = $aTickets['secret'];
\r
1020 $this->ticket = $aTickets;
\r
1021 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1023 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1024 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1025 $this->authed = true;
\r
1029 // main login server will redirect to anther NS after USR command
\r
1031 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1033 // NS: <<< XFR {id} NS {server} U D
\r
1034 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1035 if($Type!='NS') break;
\r
1036 @list($ip, $port) = @explode(':', $server);
\r
1037 // this connection will close after XFR
\r
1038 fclose($this->NSfp);
\r
1040 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
1041 if (!$this->NSfp) {
\r
1042 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1043 $this->debug_message("*** $this->error");
\r
1047 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1049 // NS: >> VER {id} MSNP9 CVR0
\r
1051 // NS: >>> VER {id} MSNP15 CVR0
\r
1052 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1056 // return some policy data after 'USR {id} SSO I {user}' command
\r
1057 // NS: <<< GCF 0 {size}
\r
1058 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1059 // we don't need the data, just read it and drop
\r
1060 if (is_numeric($size) && $size > 0)
\r
1061 $this->ns_readdata($size);
\r
1065 // we'll quit if got any error
\r
1066 if (is_numeric($code)) {
\r
1069 $this->ns_writeln("OUT");
\r
1070 fclose($this->NSfp);
\r
1071 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1072 $this->debug_message("*** $this->error");
\r
1075 // unknown response from server, just ignore it
\r
1079 // never goto here
\r
1082 function derive_key($key, $magic) {
\r
1083 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1084 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1085 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1086 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1087 return $hash2.substr($hash4, 0, 4);
\r
1090 function generateLoginBLOB($key, $challenge) {
\r
1091 $key1 = base64_decode($key);
\r
1092 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1093 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1095 // get hash of challenge using key2
\r
1096 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1098 // get 8 bytes random data
\r
1099 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1101 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1103 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1108 return base64_encode($blob);
\r
1111 function getOIM_maildata() {
\r
1112 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1113 if (count($matches) == 0) {
\r
1114 $this->debug_message('*** no web ticket?');
\r
1117 $t = htmlspecialchars($matches[1]);
\r
1118 $p = htmlspecialchars($matches[2]);
\r
1119 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1120 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1121 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1122 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1124 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1130 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1132 </soap:Envelope>';
\r
1134 $header_array = array(
\r
1135 'SOAPAction: '.$this->oim_maildata_soap,
\r
1136 'Content-Type: text/xml; charset=utf-8',
\r
1137 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1140 $this->debug_message("*** URL: $this->oim_maildata_url");
\r
1141 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1142 $curl = curl_init();
\r
1143 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1144 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1145 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1146 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1147 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1148 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1149 curl_setopt($curl, CURLOPT_POST, 1);
\r
1150 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1151 $data = curl_exec($curl);
\r
1152 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1153 curl_close($curl);
\r
1154 $this->debug_message("*** Get Result:\n$data");
\r
1156 if ($http_code != 200) {
\r
1157 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
\r
1161 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1162 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1163 if (count($matches) == 0) {
\r
1164 $this->debug_message("*** Can't get OIM maildata");
\r
1167 return $matches[2];
\r
1170 function getOIM_message($msgid) {
\r
1171 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1172 if (count($matches) == 0) {
\r
1173 $this->debug_message('*** no web ticket?');
\r
1176 $t = htmlspecialchars($matches[1]);
\r
1177 $p = htmlspecialchars($matches[2]);
\r
1180 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1181 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1182 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1183 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1185 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1191 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1192 <messageId>'.$msgid.'</messageId>
\r
1193 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1196 </soap:Envelope>';
\r
1198 $header_array = array(
\r
1199 'SOAPAction: '.$this->oim_read_soap,
\r
1200 'Content-Type: text/xml; charset=utf-8',
\r
1201 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1204 $this->debug_message("*** URL: $this->oim_read_url");
\r
1205 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1206 $curl = curl_init();
\r
1207 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1208 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1209 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1210 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1211 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1212 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1213 curl_setopt($curl, CURLOPT_POST, 1);
\r
1214 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1215 $data = curl_exec($curl);
\r
1216 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1217 curl_close($curl);
\r
1218 $this->debug_message("*** Get Result:\n$data");
\r
1220 if ($http_code != 200) {
\r
1221 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1225 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1227 $start = strpos($data, '<GetMessageResult>');
\r
1228 $end = strpos($data, '</GetMessageResult>');
\r
1229 if ($start === false || $end === false || $start > $end) {
\r
1230 $this->debug_message("*** Can't get OIM: $msgid");
\r
1233 $lines = substr($data, $start + 18, $end - $start);
\r
1234 $aLines = @explode("\n", $lines);
\r
1238 foreach ($aLines as $line) {
\r
1239 $line = rtrim($line);
\r
1241 if ($line === '') {
\r
1247 // stop at empty lines
\r
1248 if ($line === '') break;
\r
1251 $sMsg = base64_decode($sOIM);
\r
1252 $this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1255 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1256 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1257 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1258 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1260 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1266 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1268 <messageId>'.$msgid.'</messageId>
\r
1272 </soap:Envelope>';
\r
1274 $header_array = array(
\r
1275 'SOAPAction: '.$this->oim_del_soap,
\r
1276 'Content-Type: text/xml; charset=utf-8',
\r
1277 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1280 $this->debug_message("*** URL: $this->oim_del_url");
\r
1281 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1282 $curl = curl_init();
\r
1283 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1284 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1285 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1286 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1287 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1288 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1289 curl_setopt($curl, CURLOPT_POST, 1);
\r
1290 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1291 $data = curl_exec($curl);
\r
1292 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1293 curl_close($curl);
\r
1294 $this->debug_message("*** Get Result:\n$data");
\r
1296 if ($http_code != 200)
\r
1297 $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
\r
1299 $this->debug_message("*** OIM ($msgid) deleted");
\r
1302 private function NSLogout() {
\r
1303 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1306 $this->ns_writeln("OUT");
\r
1307 fclose($this->NSfp);
\r
1308 $this->NSfp = false;
\r
1309 $this->log_message("*** logout now!");
\r
1313 private function NSRetryWait($Wait) {
\r
1314 $this->log_message("*** wait for $Wait seconds");
\r
1315 for($i=0;$i<$Wait;$i++) {
\r
1317 if($this->kill_me) return false;
\r
1321 public function ProcessSendMessageFileQueue() {
\r
1322 $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');
\r
1323 if (!is_array($aFiles)) return true;
\r
1325 foreach ($aFiles as $filename) {
\r
1326 $fp = fopen($filename, 'rt');
\r
1327 if (!$fp) continue;
\r
1330 $buf = trim(fgets($fp));
\r
1331 if (substr($buf, 0, 3) == 'TO:') {
\r
1332 $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));
\r
1333 while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";
\r
1336 if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')
\r
1337 $this->log_message("!!! message format error? delete $filename");
\r
1340 foreach($aTo as $To)
\r
1342 @list($user, $domain, $network) = @explode('@', $To);
\r
1343 $MessageList[$network]["$user@$domain"]=$sMessage;
\r
1346 if($this->backup_file)
\r
1348 $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';
\r
1349 if (!file_exists($backup_dir)) @mkdir($backup_dir);
\r
1350 $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);
\r
1351 if (@rename($filename, $backup_name))
\r
1352 $this->log_message("*** move file to $backup_name");
\r
1354 else @unlink($filename);
\r
1356 foreach ($MessageList as $network => $Messages)
\r
1358 switch(trim($network))
\r
1362 // okay, try to ask a switchboard (SB) for sending message
\r
1363 // NS: >>> XFR {id} SB
\r
1364 // $this->ns_writeln("XFR $this->id SB");
\r
1365 foreach($Messages as $User => $Message)
\r
1366 $this->MessageQueue[$User][]=$Message;
\r
1368 case 'Offline': //MSN
\r
1370 //FIXME: 修正Send OIM
\r
1371 foreach($Messages as $To => $Message)
\r
1374 for ($i = 0; $i < $this->oim_try; $i++)
\r
1376 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
1377 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
1378 // need challenge lockkey
\r
1379 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
1380 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
1383 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
1387 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
1390 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
1391 // maybe we need to re-login again
\r
1392 if(!$this->get_passport_ticket())
\r
1394 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1397 $this->log_message("**** get new ticket, try it again");
\r
1404 foreach($Messages as $To => $Message) {
\r
1405 $Message=$this->getMessage($Message, $network);
\r
1406 $len = strlen($Message);
\r
1407 $this->ns_writeln("UUM $this->id $To $network 1 $len");
\r
1408 $this->ns_writedata($Message);
\r
1409 $this->log_message("*** sent to $To (network: $network):\n$Message");
\r
1413 if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))
\r
1415 $this->MessageQueue[$User]['XFRSent']=false;
\r
1416 $this->MessageQueue[$User]['ReqTime']=false;
\r
1420 public function SignalFunction($signal)
\r
1430 $ChildPid=pcntl_wait($status,WUNTRACED);
\r
1433 $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);
\r
1434 unset($this->ChildProcess[$ChildPid]);
\r
1440 public function Run()
\r
1442 $this->log_message("*** startup ***");
\r
1443 if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");
\r
1444 if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");
\r
1445 if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");
\r
1446 $process_file = false;
\r
1449 $aContactList = array();
\r
1452 if($this->kill_me)
\r
1454 $this->log_message("*** Okay, kill me now!");
\r
1455 return $this->NSLogout();
\r
1457 if (!is_resource($this->NSfp) || feof($this->NSfp))
\r
1459 $this->log_message("*** try to connect to MSN network");
\r
1460 if (!$this->connect($this->user, $this->password))
\r
1462 $this->log_message("!!! Can't connect to server: $this->error");
\r
1463 if(!$this->NSRetryWait($this->retry_wait)) continue;
\r
1465 $this->UpdateContacts();
\r
1466 $this->LastPing=time();
\r
1467 $this->log_message("*** connected, wait for command");
\r
1468 $start_tm = time();
\r
1469 $ping_tm = time();
\r
1470 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1471 $aContactList = $this->getMembershipList();
\r
1472 if ($this->update_pending) {
\r
1473 if (is_array($aContactList)) {
\r
1474 $pending = 'Pending';
\r
1475 foreach ($aContactList as $u_domain => $aUserList) {
\r
1476 foreach ($aUserList as $u_name => $aNetworks) {
\r
1477 foreach ($aNetworks as $network => $aData) {
\r
1478 if (isset($aData[$pending])) {
\r
1481 foreach (array('Allow', 'Reverse') as $list) {
\r
1482 if (isset($aData[$list]))
\r
1485 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1486 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1492 $id = $aData[$pending];
\r
1493 // we can delete it from pending now
\r
1494 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1495 unset($aContactList[$u_domain][$u_name][$network][$pending]);
\r
1500 foreach (array('Allow', 'Reverse') as $list) {
\r
1501 if (!isset($aData[$list])) {
\r
1502 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1503 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1515 if (is_array($aContactList)) {
\r
1516 foreach ($aContactList as $u_domain => $aUserList) {
\r
1517 $str = '<d n="'.$u_domain.'">';
\r
1518 $len += strlen($str);
\r
1519 if ($len > 7400) {
\r
1520 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1523 $len = strlen($str);
\r
1526 foreach ($aUserList as $u_name => $aNetworks) {
\r
1527 foreach ($aNetworks as $network => $status) {
\r
1528 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1529 $len += strlen($str);
\r
1530 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1532 if ($len > 7475) {
\r
1534 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1536 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1537 $len = strlen($sList);
\r
1546 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1547 // NS: >>> BLP {id} BL
\r
1548 $this->ns_writeln("BLP $this->id BL");
\r
1549 foreach ($aADL as $str) {
\r
1550 $len = strlen($str);
\r
1551 // NS: >>> ADL {id} {size}
\r
1552 $this->ns_writeln("ADL $this->id $len");
\r
1553 $this->ns_writedata($str);
\r
1555 // NS: >>> PRP {id} MFN name
\r
1556 if ($this->alias == '') $this->alias = $user;
\r
1557 $aliasname = rawurlencode($this->alias);
\r
1558 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1560 //$MsnObj=$this->PhotoStckObj();
\r
1561 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1562 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1563 if($this->PhotoStickerFile!==false)
\r
1564 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1565 // NS: >>> UUX {id} length
\r
1566 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1567 $len = strlen($str);
\r
1568 $this->ns_writeln("UUX $this->id $len");
\r
1569 $this->ns_writedata($str);
\r
1571 $data = $this->ns_readln();
\r
1574 //If No NS Message Process SendMessageFileQueue
\r
1575 if (time()-$this->LastPing > $this->ping_wait)
\r
1578 $this->ns_writeln("PNG");
\r
1579 $this->LastPing = time();
\r
1581 if(count($this->ChildProcess)<$this->MAXChildProcess)
\r
1584 foreach($this->MessageQueue as $User => $Message)
\r
1586 if(!trim($User)) continue;
\r
1587 if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;
\r
1588 if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))
\r
1590 $this->MessageQueue[$User]['XFRSent']=true;
\r
1591 $this->MessageQueue[$User]['ReqTime']=time();
\r
1592 $this->log_message("*** Request SB for $User");
\r
1593 $this->ns_writeln("XFR $this->id SB");
\r
1598 if($this->ProcessSendMessageFileQueue()) continue;
\r
1601 switch (substr($data,0,3))
\r
1604 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
1605 // NS: <<< SBS 0 null
\r
1610 // NS: <<< RFS ???
\r
1611 // refresh ADL, so we re-send it again
\r
1612 if (is_array($aADL)) {
\r
1613 foreach ($aADL as $str) {
\r
1614 $len = strlen($str);
\r
1615 // NS: >>> ADL {id} {size}
\r
1616 $this->ns_writeln("ADL $this->id $len");
\r
1617 $this->ns_writedata($str);
\r
1623 // NS: <<< LST {email} {alias} 11 0
\r
1624 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
1625 @list($u_name, $u_domain) = @explode('@', $email);
\r
1626 if (!isset($aContactList[$u_domain][$u_name][1])) {
\r
1627 $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
1628 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
1633 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
1634 // NS: <<< ADL 0 {size}
\r
1635 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
1636 if (is_numeric($size) && $size > 0)
\r
1638 $data = $this->ns_readdata($size);
\r
1639 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1640 if (is_array($matches) && count($matches) > 0)
\r
1642 $u_domain = $matches[1];
\r
1643 $u_name = $matches[2];
\r
1644 $network = $matches[4];
\r
1645 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1646 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
1649 $re_login = false;
\r
1651 foreach (array('Allow', 'Reverse') as $list)
\r
1653 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1656 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1659 $aTickets = $this->get_passport_ticket();
\r
1660 if (!$aTickets || !is_array($aTickets)) {
\r
1661 // failed to login? ignore it
\r
1662 $this->log_message("*** can't re-login, something wrong here");
\r
1663 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1667 $this->ticket = $aTickets;
\r
1668 $this->log_message("**** get new ticket, try it again");
\r
1669 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1671 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1675 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1678 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
1680 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
1681 $len = strlen($str);
\r
1684 $this->log_message("*** someone add us to their list: $data");
\r
1685 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
1690 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
1691 // NS: <<< RML 0 {size}
\r
1692 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
1693 if (is_numeric($size) && $size > 0)
\r
1695 $data = $this->ns_readdata($size);
\r
1696 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1697 if (is_array($matches) && count($matches) > 0)
\r
1699 $u_domain = $matches[1];
\r
1700 $u_name = $matches[2];
\r
1701 $network = $matches[4];
\r
1702 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1704 $aData = $aContactList[$u_domain][$u_name][$network];
\r
1705 foreach ($aData as $list => $id)
\r
1706 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
1707 unset($aContactList[$u_domain][$u_name][$network]);
\r
1708 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
1711 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
1712 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
1715 $this->log_message("*** someone remove us from their list: $data");
\r
1720 // randomly, we get MSG notification from server
\r
1721 // NS: <<< MSG Hotmail Hotmail {size}
\r
1722 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
1723 if (is_numeric($size) && $size > 0) {
\r
1724 $data = $this->ns_readdata($size);
\r
1725 $aLines = @explode("\n", $data);
\r
1729 foreach ($aLines as $line) {
\r
1730 $line = rtrim($line);
\r
1732 if ($line === '') {
\r
1736 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
1737 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
1738 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
1739 // we just need text/x-msmsgsinitialmdatanotification
\r
1740 // or text/x-msmsgsoimnotification
\r
1747 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
1748 $maildata = trim(substr($line, 10));
\r
1753 $this->log_message("*** ingnore MSG for: $line");
\r
1756 if ($maildata == '') {
\r
1757 $this->log_message("*** ingnore MSG not for OIM");
\r
1760 $re_login = false;
\r
1761 if (strcasecmp($maildata, 'too-large') == 0) {
\r
1762 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
1763 $maildata = $this->getOIM_maildata();
\r
1764 if ($maildata === false) {
\r
1765 $this->log_message("*** can't get mail-data via SOAP");
\r
1766 // maybe we need to re-login again
\r
1767 $aTickets = $this->get_passport_ticket();
\r
1768 if (!$aTickets || !is_array($aTickets)) {
\r
1769 // failed to login? ignore it
\r
1770 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1774 $this->ticket = $aTickets;
\r
1775 $this->log_message("**** get new ticket, try it again");
\r
1776 $maildata = $this->getOIM_maildata();
\r
1777 if ($maildata === false) {
\r
1778 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
1783 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
1787 $start = strpos($p, '<M>');
\r
1788 $end = strpos($p, '</M>');
\r
1789 if ($start === false || $end === false || $start > $end) break;
\r
1791 $sOIM = substr($p, $start, $end - $start);
\r
1793 $p = substr($p, $end);
\r
1795 if (count($aOIMs) == 0) {
\r
1796 $this->log_message("*** ingnore empty OIM");
\r
1799 foreach ($aOIMs as $maildata) {
\r
1800 // T: 11 for MSN, 13 for Yahoo
\r
1801 // S: 6 for MSN, 7 for Yahoo
\r
1802 // RT: the datetime received by server
\r
1803 // RS: already read or not
\r
1804 // SZ: size of message
\r
1807 // F: always 00000000-0000-0000-0000-000000000009
\r
1808 // N: sender alias
\r
1809 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
1810 if (count($matches) == 0) {
\r
1811 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
1814 $oim_type = $matches[1];
\r
1815 if ($oim_type = 13)
\r
1819 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
1820 if (count($matches) == 0) {
\r
1821 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
1824 $oim_sender = $matches[1];
\r
1825 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
1826 if (count($matches) == 0) {
\r
1827 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
1830 $oim_msgid = $matches[1];
\r
1831 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
1832 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
1833 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
1834 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
1835 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
1836 $sMsg = $this->getOIM_message($oim_msgid);
\r
1837 if ($sMsg === false) {
\r
1838 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
1840 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1843 $aTickets = $this->get_passport_ticket();
\r
1844 if (!$aTickets || !is_array($aTickets)) {
\r
1845 // failed to login? ignore it
\r
1846 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1850 $this->ticket = $aTickets;
\r
1851 $this->log_message("**** get new ticket, try it again");
\r
1852 $sMsg = $this->getOIM_message($oim_msgid);
\r
1853 if ($sMsg === false) {
\r
1854 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1858 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
1860 $this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
1866 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
1867 // NS: <<< UBM {email} $network $type {size}
\r
1868 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
1869 if (is_numeric($size) && $size > 0)
\r
1871 $data = $this->ns_readdata($size);
\r
1872 $aLines = @explode("\n", $data);
\r
1876 foreach ($aLines as $line) {
\r
1877 $line = rtrim($line);
\r
1879 if ($line === '') {
\r
1883 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
1889 $aSubLines = @explode("\r", $line);
\r
1890 foreach ($aSubLines as $str) {
\r
1898 $this->log_message("*** ingnore from $from_email: $line");
\r
1901 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
1902 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
1907 // randomly, we get UBX notification from server
\r
1908 // NS: <<< UBX email {network} {size}
\r
1909 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
1910 // we don't need the notification data, so just ignore it
\r
1911 if (is_numeric($size) && $size > 0)
\r
1912 $this->ns_readdata($size);
\r
1916 // randomly, we'll get challenge from server
\r
1917 // NS: <<< CHL 0 {code}
\r
1918 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
1919 $fingerprint = $this->getChallenge($chl_code);
\r
1920 // NS: >>> QRY {id} {product_id} 32
\r
1921 // NS: >>> fingerprint
\r
1922 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
1923 $this->ns_writedata($fingerprint);
\r
1924 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1925 if($this->PhotoStickerFile!==false)
\r
1926 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1929 // NS: <<< CHG {id} {status} {code}
\r
1931 // change our status to online first
\r
1935 // sometimes, NS will redirect to another NS
\r
1937 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1939 // NS: <<< XFR {id} NS {server} U D
\r
1940 // for normal switchboard XFR
\r
1941 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
1942 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
1943 @list($ip, $port) = @explode(':', $server);
\r
1944 if ($server_type != 'SB') {
\r
1946 // this connection will close after XFR
\r
1947 $this->NSLogout();
\r
1950 if(count($this->MessageQueue))
\r
1952 foreach($this->MessageQueue as $User => $Message)
\r
1954 //$this->ChildProcess[$ChildPid]
\r
1955 $this->log_message("*** XFR SB $User");
\r
1956 $pid=pcntl_fork();
\r
1960 $this->ChildProcess[$pid]=$User;
\r
1965 $this->log_message("*** Fork Error $User");
\r
1971 $this->log_message("*** Child Process Start for $User");
\r
1972 unset($Message['XFRSent']);
\r
1973 unset($Message['ReqTime']);
\r
1974 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
1975 if ($bSBresult === false)
\r
1977 // error for switchboard
\r
1978 $this->log_message("!!! error for sending message to ".$User);
\r
1983 unset($this->MessageQueue[$User]);
\r
1986 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
1987 if ($bSBresult === false) {
\r
1988 // error for switchboard
\r
1989 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
1990 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
1994 // NS: <<< QNG {time}
\r
1995 @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
1996 if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
1997 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
1998 //Mod by Ricky Set Online
\r
2002 if($this->PhotoStickerFile!==false)
\r
2003 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2005 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2006 // someone is trying to talk to us
\r
2007 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2008 $this->log_message("NS: <<< RNG $data");
\r
2009 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2010 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2011 if($this->IsIgnoreMail($email))
\r
2013 $this->log_message("*** Ignore RNG from $email");
\r
2016 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
2017 $this->addContact($email,1,$email, true);
\r
2018 $pid=pcntl_fork();
\r
2022 $this->ChildProcess[$pid]='RNG';
\r
2027 $this->log_message("*** Fork Error $User");
\r
2033 $this->log_message("*** Ring Child Process Start for $User");
\r
2034 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
2039 // force logout from NS
\r
2040 // NS: <<< OUT xxx
\r
2041 fclose($this->NSfp);
\r
2042 $this->log_message("*** LOGOUT from NS");
\r
2046 $code = substr($data,0,3);
\r
2047 if (is_numeric($code)) {
\r
2048 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2049 $this->debug_message("*** NS: $this->error");
\r
2051 return $this->NsLogout();
\r
2056 return $this->NsLogout();
\r
2059 /*public function SendMessage($Message, $To)
\r
2061 $FileName = MSN_CLASS_SPOOL_DIR.'/'.strftime('%Y%m%d%H%M%S',time()).'_'.posix_getpid().'_sendMessage.msn';
\r
2062 if(!is_array($To))
\r
2065 foreach($To as $Email)
\r
2067 list($name,$host,$network)=explode('@',$Email);
\r
2068 $network=$network==''?1:$network;
\r
2069 if($network==1 && $this->SwitchBoardProcess && $this->SwitchBoardSessionUser=="$name@$host" )
\r
2071 $this->debug_message("*** SendMessage to $Receiver use SB message queue.");
\r
2072 array_push($this->SwitchBoardMessageQueue,$Message);
\r
2075 $Receiver.="$name@$host@$network,";
\r
2077 if($Receiver=='') return;
\r
2078 $Receiver=substr($Receiver,0,-1);
\r
2079 $this->debug_message("*** SendMessage to $Receiver use File queue.");
\r
2080 file_put_contents($FileName,"TO: $Receiver\n$Message\n");
\r
2083 function getChallenge($code)
\r
2086 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
2087 // Step 1: The MD5 Hash
\r
2088 $md5Hash = md5($code.$this->prod_key);
\r
2089 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2090 for ($i = 0; $i < 4; $i++) {
\r
2091 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
2092 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
2095 // Step 2: A new string
\r
2096 $chl_id = $code.$this->prod_id;
\r
2097 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
2099 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
2100 for ($i = 0; $i < count($aID); $i++) {
\r
2101 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
2102 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
2105 // Step 3: The 64 bit key
\r
2106 $magic_num = 0x0E79A9C1;
\r
2107 $str7f = 0x7FFFFFFF;
\r
2110 for ($i = 0; $i < count($aID); $i += 2) {
\r
2112 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
2113 $temp = bcadd($temp, $high);
\r
2114 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
2115 $temp = bcmod($temp, $str7f);
\r
2117 $high = $aID[$i+1];
\r
2118 $high = bcmod(bcadd($high, $temp), $str7f);
\r
2119 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
2120 $high = bcmod($high, $str7f);
\r
2122 $low = bcadd(bcadd($low, $high), $temp);
\r
2125 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
2126 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
2128 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
2129 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
2130 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
2131 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
2132 // we need integer here
\r
2133 $high = 0+$new_high;
\r
2135 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
2136 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
2137 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
2138 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
2139 // we need integer here
\r
2140 $low = 0+$new_low;
\r
2142 // we just use 32 bits integer, don't need the key, just high/low
\r
2143 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
2145 // Step 4: Using the key
\r
2146 $md5Hash = md5($code.$this->prod_key);
\r
2147 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2150 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
2151 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
2152 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
2153 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
2158 private function getMessage($sMessage, $network = 1)
\r
2160 $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
2161 $msg_header_len = strlen($msg_header);
\r
2162 if ($network == 1)
\r
2163 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
2165 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
2166 $sMessage=str_replace("\r", '', $sMessage);
\r
2167 $msg=substr($sMessage,0,$maxlen);
\r
2168 return $msg_header.$msg;
\r
2172 * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息
\r
2176 private function DoSwitchBoard($Action,$Param)
\r
2178 $SessionEnd=false;
\r
2181 $LastActive=time();
\r
2182 stream_set_timeout($this->SBFp, $this->SBTimeout);
\r
2186 $cki_code=$Param['cki'];
\r
2187 $user=$Param['user'];
\r
2188 $this->SwitchBoardMessageQueue=$Param['Msg'];
\r
2189 // SB: >>> USR {id} {user} {cki}
\r
2190 $this->SB_writeln("USR $id $this->user $cki_code");
\r
2192 $this->SwitchBoardSessionUser=$user;
\r
2195 $ticket=$Param['ticket'];
\r
2196 $sid=$Param['sid'];
\r
2197 $user=$Param['user'];
\r
2198 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2199 $this->SB_writeln("ANS $id $this->user $ticket $sid");
\r
2201 $this->SwitchBoardSessionUser=$user;
\r
2206 while((!feof($this->SBFp))&&(!$SessionEnd))
\r
2208 $data = $this->SB_readln();
\r
2209 if($this->kill_me)
\r
2211 $this->log_message("*** SB Okay, kill me now!");
\r
2214 if($data === false)
\r
2216 if(time()-$LastActive > $this->SBIdleTimeout)
\r
2218 $this->debug_message("*** SB Idle Timeout!");
\r
2221 if(!$Joined) continue;
\r
2222 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2224 if($Message=='') continue;
\r
2225 $aMessage = $this->getMessage($Message);
\r
2227 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2228 if($MsnObjDefine!=='')
\r
2230 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2231 $len = strlen($SendString);
\r
2232 $this->SB_writeln("MSG $id N $len");
\r
2234 $this->SB_writedata($SendString);
\r
2237 $len = strlen($aMessage);
\r
2238 $this->SB_writeln("MSG $id N $len");
\r
2240 $this->SB_writedata($aMessage);
\r
2242 $this->SwitchBoardMessageQueue=array();
\r
2243 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2246 $code = substr($data, 0, 3);
\r
2250 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
2251 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
2252 $this->log_message("*** $email join us");
\r
2256 $this->log_message("*** Quit for BYE");
\r
2260 // SB: <<< USR {id} OK {user} {alias}
\r
2261 // we don't need the data, just ignore it
\r
2262 // request user to join this switchboard
\r
2263 // SB: >>> CAL {id} {user}
\r
2264 $this->SB_writeln("CAL $id $user");
\r
2268 // SB: <<< CAL {id} RINGING {?}
\r
2269 // we don't need this, just ignore, and wait for other response
\r
2273 // SB: <<< JOI {user} {alias} {clientid?}
\r
2274 // someone join us
\r
2275 // we don't need the data, just ignore it
\r
2276 // no more user here
\r
2280 // SB: <<< MSG {email} {alias} {len}
\r
2281 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
2282 $len = trim($len);
\r
2283 $data = $this->SB_readdata($len);
\r
2284 $aLines = @explode("\n", $data);
\r
2289 foreach ($aLines as $line)
\r
2291 $line = rtrim($line);
\r
2293 if ($line === '') {
\r
2297 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2298 // typing notification, just ignore
\r
2302 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
2303 // we don't handle any split message, just ignore
\r
2307 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
2308 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
2310 $p = strstr($data, "\n\n");
\r
2312 if ($p === false) {
\r
2313 $p = strstr($data, "\r\n\r\n");
\r
2315 $sMsg = substr($p, 4);
\r
2318 $sMsg = substr($p, 2);
\r
2321 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
2322 // ignore all application/x-... message
\r
2324 // application/x-ms-ink => ink message
\r
2328 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
2329 // ignore all text/x-... message
\r
2331 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
2332 // text/x-mms-animemoticon => customized animemotion word
\r
2344 $this->log_message("*** ingnore from $from_email: $line");
\r
2349 // we will ignore any p2p message after sending acknowledgement
\r
2351 $len = strlen($sMsg);
\r
2352 $this->log_message("*** p2p message from $from_email, size $len");
\r
2353 // header = 48 bytes
\r
2354 // content >= 0 bytes
\r
2355 // footer = 4 bytes
\r
2356 // so it need to >= 52 bytes
\r
2357 /*if ($len < 52) {
\r
2358 $this->log_message("*** p2p: size error, less than 52!");
\r
2361 $aDwords = @unpack("V12dword", $sMsg);
\r
2362 if (!is_array($aDwords)) {
\r
2363 $this->log_message("*** p2p: header unpack error!");
\r
2366 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
2367 $hdr_SessionID = $aDwords['dword1'];
\r
2368 $hdr_Identifier = $aDwords['dword2'];
\r
2369 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
2370 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
2371 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
2372 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
2373 $hdr_MessageLength = $aDwords['dword7'];
\r
2374 $hdr_Flag = $aDwords['dword8'];
\r
2375 $hdr_AckID = $aDwords['dword9'];
\r
2376 $hdr_AckUID = $aDwords['dword10'];
\r
2377 $hdr_AckSizeLow = $aDwords['dword11'];
\r
2378 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
2379 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
2380 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
2381 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
2382 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
2383 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
2384 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
2385 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
2386 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
2387 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
2388 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
2389 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
2390 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
2391 if($hdr_Flag==2) {
\r
2392 //This is an ACK from SB ignore....
\r
2393 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
2396 $MsgBody=$this->linetoArray(substr($sMsg,48,-4));
\r
2397 $this->debug_message("*** p2p: body".print_r($MsgBody,true));
\r
2398 if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))
\r
2402 if($this->SB_readln()===false) break;
\r
2404 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));
\r
2405 preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);
\r
2406 $BranchGUID=$Matches[1];
\r
2407 //it's an invite to send a display picture.
\r
2408 $new_id = ~$hdr_Identifier;
\r
2409 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2412 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2417 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2418 $footer = pack("L", 0);
\r
2419 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2420 $len = strlen($message);
\r
2421 $this->SB_writeln("MSG $id D $len");
\r
2423 $this->SB_writedata($message);
\r
2424 $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
2425 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
2426 $this->SB_readln();//Read ACK;
\r
2427 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
2429 //Send 200 OK message
\r
2430 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
2432 "MSNSLP/1.0 200 OK\r\n".
\r
2433 "To: <msnmsgr:".$from_email.">\r\n".
\r
2434 "From: <msnmsgr:".$this->user.">\r\n".
\r
2435 "Via: ".$MsgBody['Via']."\r\n".
\r
2436 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
2437 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2438 "Max-Forwards: 0\r\n".
\r
2439 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
2440 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
2442 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2443 $hdr_TotalDataSizeHigh=0;
\r
2444 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2447 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2448 strlen($MessagePayload),
\r
2455 "MIME-Version: 1.0\r\n".
\r
2456 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2457 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2458 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2460 $this->SB_writedata($message);
\r
2461 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
2462 $this->SB_readln();//Read ACK;
\r
2464 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
2465 //send Data preparation message
\r
2466 //send 4 null bytes as data
\r
2467 $hdr_TotalDataSizeLow=4;
\r
2468 $hdr_TotalDataSizeHigh=0;
\r
2470 $hdr = pack("LLLLLLLLLLLL",
\r
2471 $MsgBody['SessionID'],
\r
2474 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2475 $hdr_TotalDataSizeLow,
\r
2481 "MIME-Version: 1.0\r\n".
\r
2482 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2483 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";
\r
2484 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2486 $this->SB_writedata($message);
\r
2487 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
2488 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
2489 $this->SB_readln();//Read ACK;
\r
2491 //send Data Content..
\r
2492 $footer=pack('N',1);
\r
2494 $FileSize=filesize($PictureFilePath);
\r
2495 if($hTitle=fopen($PictureFilePath,'rb'))
\r
2499 while(!feof($hTitle))
\r
2501 $FileContent=fread($hTitle,1024);
\r
2502 $FileContentSize=strlen($FileContent);
\r
2503 $hdr = pack("LLLLLLLLLLLL",
\r
2504 $MsgBody['SessionID'],
\r
2515 "MIME-Version: 1.0\r\n".
\r
2516 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2517 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
2518 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2520 $this->SB_writedata($message);
\r
2521 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
2522 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
2523 //$this->SB_readln();//Read ACK;
\r
2524 $Offset+=$FileContentSize;
\r
2529 $MessageContent="\r\n".pack("C", 0);
\r
2531 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
2532 "To: <msnmsgr:$from_email>\r\n".
\r
2533 "From: <msnmsgr:".$this->user.">\r\n".
\r
2534 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
2536 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2537 "Max-Forwards: 0\r\n".
\r
2538 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
2539 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
2540 $footer=pack('N',0);
\r
2541 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2542 $hdr_TotalDataSizeHigh=0;
\r
2544 $hdr = pack("LLLLLLLLLLLL",
\r
2548 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2555 "MIME-Version: 1.0\r\n".
\r
2556 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2557 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2558 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2560 $this->SB_writedata($message);
\r
2561 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
2566 //if ($hdr_Flag == 2) {
\r
2567 // just send ACK...
\r
2568 // $this->SB_writeln("ACK $id");
\r
2571 if ($hdr_SessionID == 4) {
\r
2573 $this->debug_message("*** p2p: ignore flag 4");
\r
2576 $finished = false;
\r
2577 if ($hdr_TotalDataSizeHigh == 0) {
\r
2578 // only 32 bites size
\r
2579 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
2583 // we won't accept any file transfer
\r
2584 // so I think we won't get any message size need to use 64 bits
\r
2585 // 64 bits size here, can't count directly...
\r
2586 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
2587 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
2588 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
2589 $now_size = bcadd($dataoffset, $messagelength);
\r
2590 if (bccomp($now_size, $totalsize) >= 0)
\r
2594 // ignore not finished split packet
\r
2595 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
2598 //$new_id = ~$hdr_Identifier;
\r
2601 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2604 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2609 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2610 $footer = pack("L", 0);
\r
2611 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2612 $len = strlen($message);
\r
2613 $this->SB_writeln("MSG $id D $len");
\r
2615 $this->SB_writedata($message);
\r
2616 $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
2617 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2621 $this->log_message("*** MSG from $from_email: $sMsg");
\r
2622 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2625 $this->log_message("*** User $user is offline. Try OIM.");
\r
2626 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2627 $this->SendMessage($Message,"$user@Offline");
\r
2631 if (is_numeric($code))
\r
2633 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2634 $this->debug_message("*** SB: $this->error");
\r
2639 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2641 if (feof($this->SBFp))
\r
2643 // lost connection? error? try OIM later
\r
2644 @fclose($this->SBFp);
\r
2647 $this->SB_writeln("OUT");
\r
2648 @fclose($this->SBFp);
\r
2651 private function switchboard_control($ip, $port, $cki_code, $user, $Messages)
\r
2653 $this->SwitchBoardProcess=1;
\r
2654 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2655 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2658 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2661 return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));
\r
2663 private function switchboard_ring($ip, $port, $sid, $ticket,$user)
\r
2665 $this->SwitchBoardProcess=2;
\r
2666 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2667 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2670 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2673 return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));
\r
2676 private function sendOIM($to, $sMessage, $lockkey)
\r
2678 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2679 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2680 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2681 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
2683 <From memberName="'.$this->user.'"
\r
2684 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
2687 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
2688 msnpVer="'.$this->protocol.'"
\r
2689 buildVer="'.$this->buildver.'"/>
\r
2690 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2691 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
2692 appid="'.$this->prod_id.'"
\r
2693 lockkey="'.$lockkey.'"
\r
2694 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2695 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
2696 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
2697 <MessageNumber>1</MessageNumber>
\r
2701 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
2702 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
2703 Content-Type: text/plain; charset=UTF-8
\r
2704 Content-Transfer-Encoding: base64
\r
2705 X-OIM-Message-Type: OfflineMessage
\r
2706 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
2707 X-OIM-Sequence-Num: 1
\r
2709 '.chunk_split(base64_encode($sMessage)).'
\r
2712 </soap:Envelope>';
\r
2714 $header_array = array(
\r
2715 'SOAPAction: '.$this->oim_send_soap,
\r
2716 'Content-Type: text/xml',
\r
2717 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
2720 $this->debug_message("*** URL: $this->oim_send_url");
\r
2721 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2722 $curl = curl_init();
\r
2723 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
2724 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2725 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2726 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2727 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2728 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2729 curl_setopt($curl, CURLOPT_POST, 1);
\r
2730 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2731 $data = curl_exec($curl);
\r
2732 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2733 curl_close($curl);
\r
2734 $this->debug_message("*** Get Result:\n$data");
\r
2736 if ($http_code == 200) {
\r
2737 $this->debug_message("*** OIM sent for $to");
\r
2741 $challenge = false;
\r
2742 $auth_policy = false;
\r
2743 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
2744 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
2745 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
2746 if (count($matches) != 0) {
\r
2747 // yes, we get new LockKeyChallenge
\r
2748 $challenge = $matches[2];
\r
2749 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
2751 // auth policy error
\r
2752 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
2753 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
2754 if (count($matches) != 0) {
\r
2755 $auth_policy = $matches[2];
\r
2756 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
2758 if ($auth_policy === false && $challenge === false) {
\r
2759 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
2760 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
2761 if (count($matches) == 0) {
\r
2762 // no error, we assume the OIM is sent
\r
2763 $this->debug_message("*** OIM sent for $to");
\r
2766 $err_code = $matches[2];
\r
2767 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
2768 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
2769 if (count($matches) > 0)
\r
2770 $err_msg = $matches[1];
\r
2773 $this->debug_message("*** OIM failed for $to");
\r
2774 $this->debug_message("*** OIM Error code: $err_code");
\r
2775 $this->debug_message("*** OIM Error Message: $err_msg");
\r
2778 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
2781 // read data for specified size
\r
2782 private function ns_readdata($size) {
\r
2785 while (!feof($this->NSfp)) {
\r
2786 $buf = @fread($this->NSfp, $size - $count);
\r
2788 $count += strlen($buf);
\r
2789 if ($count >= $size) break;
\r
2791 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2796 private function ns_readln() {
\r
2797 $data = @fgets($this->NSfp, 4096);
\r
2798 if ($data !== false) {
\r
2799 $data = trim($data);
\r
2800 $this->debug_message("NS: <<< $data");
\r
2805 // write to server, append \r\n, also increase id
\r
2806 private function ns_writeln($data) {
\r
2807 @fwrite($this->NSfp, $data."\r\n");
\r
2808 $this->debug_message("NS: >>> $data");
\r
2813 // write data to server
\r
2814 private function ns_writedata($data) {
\r
2815 @fwrite($this->NSfp, $data);
\r
2816 $this->debug_message("NS: >>> $data");
\r
2820 // read data for specified size for SB
\r
2821 private function sb_readdata($size) {
\r
2824 while (!feof($this->SBFp)) {
\r
2825 $buf = @fread($this->SBFp, $size - $count);
\r
2827 $count += strlen($buf);
\r
2828 if ($count >= $size) break;
\r
2830 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2834 // read one line for SB
\r
2835 private function sb_readln() {
\r
2836 $data = @fgets($this->SBFp, 4096);
\r
2837 if ($data !== false) {
\r
2838 $data = trim($data);
\r
2839 $this->debug_message("SB: <<< $data");
\r
2844 // write to server for SB, append \r\n, also increase id
\r
2845 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
2846 private function sb_writeln($data) {
\r
2847 @fwrite($this->SBFp, $data."\r\n");
\r
2848 $this->debug_message("SB: >>> $data");
\r
2853 // write data to server
\r
2854 private function sb_writedata($data) {
\r
2855 @fwrite($this->SBFp, $data);
\r
2856 $this->debug_message("SB: >>> $data");
\r
2860 // show debug information
\r
2861 function debug_message($str) {
\r
2862 if (!$this->debug) return;
\r
2863 if($this->debug===STDOUT) echo $str."\n";
\r
2864 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
2865 $fp = fopen($fname, 'at');
\r
2867 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2871 // still show debug information, if we can't open log_file
\r
2876 function dump_binary($str) {
\r
2880 $len = strlen($str);
\r
2881 for ($i = 0; $i < $len; $i++) {
\r
2882 if (($i % 16) == 0) {
\r
2883 if ($buf !== '') {
\r
2884 $buf .= "$h_str $a_str\n";
\r
2886 $buf .= sprintf("%04X:", $i);
\r
2890 $ch = ord($str[$i]);
\r
2894 $a_str .= chr($ch);
\r
2895 $h_str .= sprintf(" %02X", $ch);
\r
2897 if ($h_str !== '')
\r
2898 $buf .= "$h_str $a_str\n";
\r
2903 function log_message($str) {
\r
2904 /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';
\r
2905 $fp = fopen($fname, 'at');
\r
2907 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2910 $this->debug_message($str);
\r
2915 * @param $FilePath 圖檔路徑
\r
2916 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
2919 private function MsnObj($FilePath,$Type=3)
\r
2921 if(!($FileSize=filesize($FilePath))) return '';
\r
2922 $Location=md5($FilePath);
\r
2923 $Friendly=md5($FilePath.$Type);
\r
2924 if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
2925 $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));
\r
2926 $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
2927 $this->MsnObjArray[$Location]=$FilePath;
\r
2928 $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
2929 $this->MsnObjMap[$Location]=$MsnObj;
\r
2930 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
2933 private function linetoArray($lines) {
\r
2934 $lines=str_replace("\r",'',$lines);
\r
2935 $lines=explode("\n",$lines);
\r
2936 foreach($lines as $line) {
\r
2937 if(!isset($line{3})) continue;
\r
2938 list($Key,$Val)=explode(':',$line);
\r
2939 $Data[trim($Key)]=trim($Val);
\r
2943 private function GetPictureFilePath($Context)
\r
2945 $MsnObj=base64_decode($Context);
\r
2946 if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))
\r
2947 $location=$Match[1];
\r
2948 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
2949 if($location&&(isset($this->MsnObjArray[$location])))
\r
2950 return $this->MsnObjArray[$location];
\r
2953 private function GetMsnObjDefine($Message)
\r
2956 if(is_array($this->Emotions))
\r
2957 foreach($this->Emotions as $Pattern => $FilePath)
\r
2959 if(strpos($Message,$Pattern)!==false)
\r
2960 $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";
\r
2962 return $DefineString;
\r
2965 * Receive Message Overload Function
\r
2968 * @param $Network 1 => msn , 32 =>yahoo
\r
2970 * @return unknown_type
\r
2972 protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}
\r
2974 * Remove Us From Member List Overload Function
\r
2977 * @param $Network 1 => msn , 32 =>yahoo
\r
2978 * @return unknown_type
\r
2980 protected function RemoveUsFromMemberList($User,$Network){}
\r
2982 * Add Us to Member List Overload Function
\r
2985 * @param $Network 1 => msn , 32 =>yahoo
\r
2986 * @return unknown_type
\r
2988 protected function AddUsToMemberList($User,$Network){}
\r
2990 public function signon() {
\r
2991 $this->log_message("*** try to connect to MSN network");
\r
2992 while(!$this->connect($this->user, $this->password))
\r
2994 $this->log_message("!!! Can't connect to server: $this->error");
\r
2995 if(!$this->NSRetryWait($this->retry_wait)) return;
\r
2997 $this->UpdateContacts();
\r
2998 $this->LastPing=time();
\r
2999 $this->log_message("*** connected, wait for command");
\r
3000 $start_tm = time();
\r
3001 $ping_tm = time();
\r
3002 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
3003 $this->aContactList = $this->getMembershipList();
\r
3004 if ($this->update_pending) {
\r
3005 if (is_array($this->aContactList)) {
\r
3006 $pending = 'Pending';
\r
3007 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3008 foreach ($aUserList as $u_name => $aNetworks) {
\r
3009 foreach ($aNetworks as $network => $aData) {
\r
3010 if (isset($aData[$pending])) {
\r
3013 foreach (array('Allow', 'Reverse') as $list) {
\r
3014 if (isset($aData[$list]))
\r
3017 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
3018 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3024 $id = $aData[$pending];
\r
3025 // we can delete it from pending now
\r
3026 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
3027 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
3032 foreach (array('Allow', 'Reverse') as $list) {
\r
3033 if (!isset($aData[$list])) {
\r
3034 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3035 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3047 if (is_array($this->aContactList)) {
\r
3048 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3049 $str = '<d n="'.$u_domain.'">';
\r
3050 $len += strlen($str);
\r
3051 if ($len > 7400) {
\r
3052 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3055 $len = strlen($str);
\r
3058 foreach ($aUserList as $u_name => $aNetworks) {
\r
3059 foreach ($aNetworks as $network => $status) {
\r
3060 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
3061 $len += strlen($str);
\r
3062 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
3064 if ($len > 7475) {
\r
3066 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3068 $sList = '<d n="'.$u_domain.'">'.$str;
\r
3069 $len = strlen($sList);
\r
3078 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3079 // NS: >>> BLP {id} BL
\r
3080 $this->ns_writeln("BLP $this->id BL");
\r
3081 foreach ($aADL as $str) {
\r
3082 $len = strlen($str);
\r
3083 // NS: >>> ADL {id} {size}
\r
3084 $this->ns_writeln("ADL $this->id $len");
\r
3085 $this->ns_writedata($str);
\r
3087 // NS: >>> PRP {id} MFN name
\r
3088 if ($this->alias == '') $this->alias = $user;
\r
3089 $aliasname = rawurlencode($this->alias);
\r
3090 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
3092 //$MsnObj=$this->PhotoStckObj();
\r
3093 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
3094 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3095 if($this->PhotoStickerFile!==false)
\r
3096 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3097 // NS: >>> UUX {id} length
\r
3098 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
3099 $len = strlen($str);
\r
3100 $this->ns_writeln("UUX $this->id $len");
\r
3101 $this->ns_writedata($str);
\r
3104 public function NSreceive() {
\r
3105 $this->log_message("*** startup ***");
\r
3109 // Sign in again if not signed in or socket failed
\r
3110 if (!is_resource($this->NSfp) || feof($this->NSfp)) {
\r
3114 $data = $this->ns_readln();
\r
3115 if($data === false) {
\r
3116 // There was no data / an error when reading from the socket so reconnect
\r
3119 switch (substr($data,0,3))
\r
3122 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
3123 // NS: <<< SBS 0 null
\r
3128 // NS: <<< RFS ???
\r
3129 // refresh ADL, so we re-send it again
\r
3130 if (is_array($aADL)) {
\r
3131 foreach ($aADL as $str) {
\r
3132 $len = strlen($str);
\r
3133 // NS: >>> ADL {id} {size}
\r
3134 $this->ns_writeln("ADL $this->id $len");
\r
3135 $this->ns_writedata($str);
\r
3141 // NS: <<< LST {email} {alias} 11 0
\r
3142 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
3143 @list($u_name, $u_domain) = @explode('@', $email);
\r
3144 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
3145 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
3146 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
3151 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
3152 // NS: <<< ADL 0 {size}
\r
3153 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
3154 if (is_numeric($size) && $size > 0)
\r
3156 $data = $this->ns_readdata($size);
\r
3157 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3158 if (is_array($matches) && count($matches) > 0)
\r
3160 $u_domain = $matches[1];
\r
3161 $u_name = $matches[2];
\r
3162 $network = $matches[4];
\r
3163 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3164 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
3167 $re_login = false;
\r
3169 foreach (array('Allow', 'Reverse') as $list)
\r
3171 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3174 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3177 $aTickets = $this->get_passport_ticket();
\r
3178 if (!$aTickets || !is_array($aTickets)) {
\r
3179 // failed to login? ignore it
\r
3180 $this->log_message("*** can't re-login, something wrong here");
\r
3181 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3185 $this->ticket = $aTickets;
\r
3186 $this->log_message("**** get new ticket, try it again");
\r
3187 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3189 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3193 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3196 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
3198 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
3199 $len = strlen($str);
\r
3202 $this->log_message("*** someone add us to their list: $data");
\r
3203 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
3208 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
3209 // NS: <<< RML 0 {size}
\r
3210 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
3211 if (is_numeric($size) && $size > 0)
\r
3213 $data = $this->ns_readdata($size);
\r
3214 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3215 if (is_array($matches) && count($matches) > 0)
\r
3217 $u_domain = $matches[1];
\r
3218 $u_name = $matches[2];
\r
3219 $network = $matches[4];
\r
3220 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3222 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
3223 foreach ($aData as $list => $id)
\r
3224 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
3225 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
3226 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
3229 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
3230 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
3233 $this->log_message("*** someone remove us from their list: $data");
\r
3238 // randomly, we get MSG notification from server
\r
3239 // NS: <<< MSG Hotmail Hotmail {size}
\r
3240 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
3241 if (is_numeric($size) && $size > 0) {
\r
3242 $data = $this->ns_readdata($size);
\r
3243 $aLines = @explode("\n", $data);
\r
3247 foreach ($aLines as $line) {
\r
3248 $line = rtrim($line);
\r
3250 if ($line === '') {
\r
3254 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
3255 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
3256 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
3257 // we just need text/x-msmsgsinitialmdatanotification
\r
3258 // or text/x-msmsgsoimnotification
\r
3265 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
3266 $maildata = trim(substr($line, 10));
\r
3271 $this->log_message("*** ingnore MSG for: $line");
\r
3274 if ($maildata == '') {
\r
3275 $this->log_message("*** ingnore MSG not for OIM");
\r
3278 $re_login = false;
\r
3279 if (strcasecmp($maildata, 'too-large') == 0) {
\r
3280 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
3281 $maildata = $this->getOIM_maildata();
\r
3282 if ($maildata === false) {
\r
3283 $this->log_message("*** can't get mail-data via SOAP");
\r
3284 // maybe we need to re-login again
\r
3285 $aTickets = $this->get_passport_ticket();
\r
3286 if (!$aTickets || !is_array($aTickets)) {
\r
3287 // failed to login? ignore it
\r
3288 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3292 $this->ticket = $aTickets;
\r
3293 $this->log_message("**** get new ticket, try it again");
\r
3294 $maildata = $this->getOIM_maildata();
\r
3295 if ($maildata === false) {
\r
3296 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
3301 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
3305 $start = strpos($p, '<M>');
\r
3306 $end = strpos($p, '</M>');
\r
3307 if ($start === false || $end === false || $start > $end) break;
\r
3309 $sOIM = substr($p, $start, $end - $start);
\r
3311 $p = substr($p, $end);
\r
3313 if (count($aOIMs) == 0) {
\r
3314 $this->log_message("*** ingnore empty OIM");
\r
3317 foreach ($aOIMs as $maildata) {
\r
3318 // T: 11 for MSN, 13 for Yahoo
\r
3319 // S: 6 for MSN, 7 for Yahoo
\r
3320 // RT: the datetime received by server
\r
3321 // RS: already read or not
\r
3322 // SZ: size of message
\r
3325 // F: always 00000000-0000-0000-0000-000000000009
\r
3326 // N: sender alias
\r
3327 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
3328 if (count($matches) == 0) {
\r
3329 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
3332 $oim_type = $matches[1];
\r
3333 if ($oim_type = 13)
\r
3337 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
3338 if (count($matches) == 0) {
\r
3339 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
3342 $oim_sender = $matches[1];
\r
3343 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
3344 if (count($matches) == 0) {
\r
3345 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
3348 $oim_msgid = $matches[1];
\r
3349 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
3350 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
3351 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
3352 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
3353 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
3354 $sMsg = $this->getOIM_message($oim_msgid);
\r
3355 if ($sMsg === false) {
\r
3356 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
3358 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3361 $aTickets = $this->get_passport_ticket();
\r
3362 if (!$aTickets || !is_array($aTickets)) {
\r
3363 // failed to login? ignore it
\r
3364 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3368 $this->ticket = $aTickets;
\r
3369 $this->log_message("**** get new ticket, try it again");
\r
3370 $sMsg = $this->getOIM_message($oim_msgid);
\r
3371 if ($sMsg === false) {
\r
3372 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3376 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
3378 //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
3379 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
3385 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
3386 // NS: <<< UBM {email} $network $type {size}
\r
3387 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
3388 if (is_numeric($size) && $size > 0)
\r
3390 $data = $this->ns_readdata($size);
\r
3391 $aLines = @explode("\n", $data);
\r
3395 foreach ($aLines as $line) {
\r
3396 $line = rtrim($line);
\r
3398 if ($line === '') {
\r
3402 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
3408 $aSubLines = @explode("\r", $line);
\r
3409 foreach ($aSubLines as $str) {
\r
3417 $this->log_message("*** ingnore from $from_email: $line");
\r
3420 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
3421 //$this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
3422 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
3427 // randomly, we get UBX notification from server
\r
3428 // NS: <<< UBX email {network} {size}
\r
3429 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
3430 // we don't need the notification data, so just ignore it
\r
3431 if (is_numeric($size) && $size > 0)
\r
3432 $this->ns_readdata($size);
\r
3436 // randomly, we'll get challenge from server
\r
3437 // NS: <<< CHL 0 {code}
\r
3438 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
3439 $fingerprint = $this->getChallenge($chl_code);
\r
3440 // NS: >>> QRY {id} {product_id} 32
\r
3441 // NS: >>> fingerprint
\r
3442 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
3443 $this->ns_writedata($fingerprint);
\r
3444 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3445 if($this->PhotoStickerFile!==false)
\r
3446 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3449 // NS: <<< CHG {id} {status} {code}
\r
3451 // change our status to online first
\r
3455 // sometimes, NS will redirect to another NS
\r
3457 // NS: <<< XFR {id} NS {server} 0 {server}
\r
3459 // NS: <<< XFR {id} NS {server} U D
\r
3460 // for normal switchboard XFR
\r
3461 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
3462 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
3463 @list($ip, $port) = @explode(':', $server);
\r
3464 if ($server_type != 'SB') {
\r
3466 // this connection will close after XFR
\r
3467 $this->NSLogout();
\r
3470 if(count($this->MessageQueue))
\r
3472 foreach($this->MessageQueue as $User => $Message)
\r
3474 //$this->ChildProcess[$ChildPid]
\r
3475 $this->log_message("*** XFR SB $User");
\r
3476 $pid=pcntl_fork();
\r
3480 $this->ChildProcess[$pid]=$User;
\r
3485 $this->log_message("*** Fork Error $User");
\r
3491 $this->log_message("*** Child Process Start for $User");
\r
3492 unset($Message['XFRSent']);
\r
3493 unset($Message['ReqTime']);
\r
3494 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
3495 if ($bSBresult === false)
\r
3497 // error for switchboard
\r
3498 $this->log_message("!!! error for sending message to ".$User);
\r
3503 unset($this->MessageQueue[$User]);
\r
3506 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
3507 if ($bSBresult === false) {
\r
3508 // error for switchboard
\r
3509 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
3510 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
3514 // NS: <<< QNG {time}
\r
3515 //@list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
3516 //if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
3517 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
3518 //Mod by Ricky Set Online
\r
3522 if($this->PhotoStickerFile!==false)
\r
3523 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3525 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3526 // someone is trying to talk to us
\r
3527 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
3528 $this->log_message("NS: <<< RNG $data");
\r
3529 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
3530 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
3531 if($this->IsIgnoreMail($email))
\r
3533 $this->log_message("*** Ignore RNG from $email");
\r
3536 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
3537 $this->addContact($email,1,$email, true);
\r
3538 $pid=pcntl_fork();
\r
3542 $this->ChildProcess[$pid]='RNG';
\r
3547 $this->log_message("*** Fork Error $User");
\r
3553 $this->log_message("*** Ring Child Process Start for $User");
\r
3554 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
3559 // force logout from NS
\r
3560 // NS: <<< OUT xxx
\r
3561 $this->log_message("*** LOGOUT from NS");
\r
3562 return $this->NsLogout();
\r
3565 $code = substr($data,0,3);
\r
3566 if (is_numeric($code)) {
\r
3567 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
3568 $this->debug_message("*** NS: $this->error");
\r
3570 return $this->NsLogout();
\r
3577 public function sendMessageViaSB($message, $to) {
\r
3578 $socket = $this->switchBoardSessions[$to]['socket'];
\r
3579 $lastActive = $this->switchBoardSessions[$to]['lastActive'];
\r
3580 $joined = $this->switchBoardSessions[$to]['joined'];
\r
3582 //FIXME Probably not needed (we're not running in a loop anymore)
\r
3583 /*if($this->kill_me)
\r
3585 $this->log_message("*** SB Okay, kill me now!");
\r
3586 endSBSession($socket);
\r
3590 // If our participant has not joined the session yet we can't message them!
\r
3591 //TODO Check the behaviour of the queue runner when we return false
\r
3595 $aMessage = $this->getMessage($Message);
\r
3597 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
3598 if($MsnObjDefine !== '')
\r
3600 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
3601 $len = strlen($SendString);
\r
3602 $this->SB_writeln("MSG $id N $len");
\r
3604 $this->SB_writedata($SendString);
\r
3607 $len = strlen($aMessage);
\r
3608 $this->SB_writeln("MSG $id N $len");
\r
3610 // Increment the trID
\r
3611 $this->switchBoardSessions[$to]['id']++;
\r
3613 $this->SB_writedata($aMessage);
\r
3615 if (feof($this->SBFp))
\r
3617 // lost connection? error? try OIM later
\r
3618 @fclose($this->SBFp);
\r
3619 //TODO introduce callback to add offline message to queue?
\r
3622 $this->SB_writeln("OUT");
\r
3623 @fclose($this->SBFp);
\r
3627 //FIXME Not sure if this is needed?
\r
3628 private function endSBSession($socket) {
\r
3629 if (feof($this->SBFp))
\r
3631 // lost connection? error? try OIM later
\r
3632 @fclose($this->SBFp);
\r
3635 $this->SB_writeln("OUT");
\r
3636 @fclose($this->SBFp);
\r
3640 public function sendMessage($message, $to) {
\r
3641 if($message != '') {
\r
3642 list($name,$host,$network)=explode('@',$to);
\r
3643 $network=$network==''?1:$network;
\r
3645 if($network === 1 && isset($this->switchBoardSessions[$to])) {
\r
3646 $recipient = $name . $host;
\r
3647 $this->debug_message("*** Sending Message to $recipient using existing SB session");
\r
3648 return $this->sendMessageViaSB($message, $recipient);
\r
3650 $this->debug_message("*** Not MSN network or no existing SB session");
\r
3651 //TODO implement creation of SB session etc
\r
3658 * Sends a ping command
\r
3660 * Should be called about every 50 seconds
\r
3662 public function send_ping() {
\r
3664 $this->ns_writeln("PNG");
\r
3667 public function getNSSocket() {
\r
3668 return $this->NSfp;
\r
3671 // TODO Allow for multiple SB session sockets
\r
3672 public function getSBSocket() {
\r
3673 return $this->SBfp;
\r
3676 public function getSockets() {
\r
3677 return array($this->NSfp, $this->SBfp);
\r
3681 * Calls User Handler
\r
3683 * Calls registered handler for a specific event.
\r
3685 * @param String $event Command (event) name (Rvous etc)
\r
3686 * @param String $data Raw message from server
\r
3687 * @see registerHandler
\r
3690 private function callHandler($event, $data) {
\r
3691 if (isset($this->myEventHandlers[$event])) {
\r
3692 call_user_func($this->myEventHandlers[$event], $data);
\r
3694 $this->noHandler($data);
\r
3699 * Registers a user handler
\r
3704 * @param String $event Event name
\r
3705 * @param String $handler User function to call
\r
3706 * @see callHandler
\r
3707 * @return boolean Returns true if successful
\r
3709 public function registerHandler($event, $handler) {
\r
3710 if (is_callable($handler)) {
\r
3711 $this->myEventHandlers[$event] = $handler;
\r