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 // Begin added for StatusNet
\r
114 private $aContactList = array();
\r
115 private $aADL = array();
\r
116 private $switchBoardSessions = array();
\r
119 * Event Handler Functions
\r
121 private $myEventHandlers = array();
\r
123 // End added for StatusNet
\r
125 private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)
\r
128 foreach($Array as $Key => $Val)
\r
130 if($Key{0}==':') continue;
\r
132 if(is_array($Val[':']))
\r
134 foreach($Val[':'] as $AttribName => $AttribVal)
\r
135 $Attrib.=" $AttribName='$AttribVal'";
\r
140 $Key=substr($Key,1);
\r
141 foreach($Val as $ListKey => $ListVal)
\r
143 if($ListKey{0}==':') continue;
\r
144 if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);
\r
145 elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';
\r
146 $ArrayString.="<$Key$Attrib>$ListVal</$Key>";
\r
150 if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);
\r
151 elseif(is_bool($Val)) $Val=$Val?'true':'false';
\r
152 $ArrayString.="<$Key$Attrib>$Val</$Key>";
\r
154 if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);
\r
155 return $ArrayString;
\r
158 public function End()
\r
160 $this->log_message("*** someone kill me ***");
\r
161 $this->kill_me=true;
\r
163 private function IsIgnoreMail($Email)
\r
165 if($this->IgnoreList==false) return false;
\r
166 foreach($this->IgnoreList as $Pattern)
\r
168 if(preg_match($Pattern,$Email)) return true;
\r
172 public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)
\r
174 $this->user = $Configs['user'];
\r
175 $this->password = $Configs['password'];
\r
176 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
177 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
178 $my_add_function = isset($Configs['add_user_function']) ? $Configs['add_user_function'] : false;
\r
179 $my_rem_function = isset($Configs['remove_user_function']) ? $Configs['remove_user_function'] : false;
\r
180 $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;
\r
181 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
182 $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;
\r
183 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
184 $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;
\r
185 $this->IgnoreList=isset($Configs['IgnoreList'])?$Configs['IgnoreList']:false;
\r
186 if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)
\r
188 foreach($this->Emotions as $EmotionFilePath)
\r
189 $this->MsnObj($EmotionFilePath,$Type=2);
\r
191 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
192 $this->timeout = $timeout;
\r
194 if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");
\r
195 if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");
\r
196 if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");
\r
198 if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");
\r
199 if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");
\r
202 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
204 normal MSN 8.1 clientid is:
\r
205 01110110 01001100 11000000 00101100
\r
208 we just use following:
\r
209 * 0x04: Your client can send/receive Ink (GIF format)
\r
210 * 0x08: Your client can send/recieve Ink (ISF format)
\r
211 * 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
212 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
215 $this->clientid = $client_id;
\r
216 $this->windows =(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
\r
217 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
220 private function get_passport_ticket($url = '')
\r
222 $user = $this->user;
\r
223 $password = htmlspecialchars($this->password);
\r
226 $passport_url = $this->passport_url;
\r
228 $passport_url = $url;
\r
230 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
231 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
232 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
233 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
234 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
235 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
236 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
237 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
238 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
240 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
241 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
242 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
243 <ps:UIVersion>1</ps:UIVersion>
\r
244 <ps:Cookies></ps:Cookies>
\r
245 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
248 <wsse:UsernameToken Id="user">
\r
249 <wsse:Username>'.$user.'</wsse:Username>
\r
250 <wsse:Password>'.$password.'</wsse:Password>
\r
251 </wsse:UsernameToken>
\r
255 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
256 <wst:RequestSecurityToken Id="RST0">
\r
257 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
259 <wsa:EndpointReference>
\r
260 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
261 </wsa:EndpointReference>
\r
263 </wst:RequestSecurityToken>
\r
264 <wst:RequestSecurityToken Id="RST1">
\r
265 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
267 <wsa:EndpointReference>
\r
268 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
269 </wsa:EndpointReference>
\r
271 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
272 </wst:RequestSecurityToken>
\r
273 <wst:RequestSecurityToken Id="RST2">
\r
274 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
276 <wsa:EndpointReference>
\r
277 <wsa:Address>messenger.msn.com</wsa:Address>
\r
278 </wsa:EndpointReference>
\r
280 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
281 </wst:RequestSecurityToken>
\r
282 <wst:RequestSecurityToken Id="RST3">
\r
283 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
285 <wsa:EndpointReference>
\r
286 <wsa:Address>contacts.msn.com</wsa:Address>
\r
287 </wsa:EndpointReference>
\r
289 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
290 </wst:RequestSecurityToken>
\r
291 <wst:RequestSecurityToken Id="RST4">
\r
292 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
294 <wsa:EndpointReference>
\r
295 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
296 </wsa:EndpointReference>
\r
298 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
299 </wst:RequestSecurityToken>
\r
300 <wst:RequestSecurityToken Id="RST5">
\r
301 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
303 <wsa:EndpointReference>
\r
304 <wsa:Address>spaces.live.com</wsa:Address>
\r
305 </wsa:EndpointReference>
\r
307 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
308 </wst:RequestSecurityToken>
\r
309 <wst:RequestSecurityToken Id="RST6">
\r
310 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
312 <wsa:EndpointReference>
\r
313 <wsa:Address>storage.msn.com</wsa:Address>
\r
314 </wsa:EndpointReference>
\r
316 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
317 </wst:RequestSecurityToken>
\r
318 </ps:RequestMultipleSecurityTokens>
\r
322 $this->debug_message("*** URL: $passport_url");
\r
323 $this->debug_message("*** Sending SOAP:\n$XML");
\r
324 $curl = curl_init();
\r
325 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
326 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
327 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
328 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
329 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
330 curl_setopt($curl, CURLOPT_POST, 1);
\r
331 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
332 $data = curl_exec($curl);
\r
333 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
335 $this->debug_message("*** Get Result:\n$data");
\r
337 if ($http_code != 200) {
\r
338 // sometimes, rediret to another URL
\r
340 //<faultcode>psf:Redirect</faultcode>
\r
341 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
342 //<faultstring>Authentication Failure</faultstring>
\r
343 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
344 $this->debug_message("*** Can't get passport ticket! http code = $http_code");
\r
347 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
348 if (count($matches) == 0) {
\r
349 $this->debug_message("*** redirect, but can't get redirect URL!");
\r
352 $redirect_url = $matches[1];
\r
353 if ($redirect_url == $passport_url) {
\r
354 $this->debug_message("*** redirect, but redirect to same URL!");
\r
357 $this->debug_message("*** redirect to $redirect_url");
\r
358 return $this->get_passport_ticket($redirect_url);
\r
361 // sometimes, rediret to another URL, also return 200
\r
363 //<faultcode>psf:Redirect</faultcode>
\r
364 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
365 //<faultstring>Authentication Failure</faultstring>
\r
366 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
367 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
368 if (count($matches) != 0) {
\r
369 $redirect_url = $matches[1];
\r
370 if ($redirect_url == $passport_url) {
\r
371 $this->debug_message("*** redirect, but redirect to same URL!");
\r
374 $this->debug_message("*** redirect to $redirect_url");
\r
375 return $this->get_passport_ticket($redirect_url);
\r
379 // no Redurect faultcode or URL
\r
380 // we should get the ticket here
\r
382 // we need ticket and secret code
\r
383 // RST1: messengerclear.live.com
\r
384 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
385 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
386 // RST2: messenger.msn.com
\r
387 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
388 // RST3: contacts.msn.com
\r
389 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
390 // RST4: messengersecure.live.com
\r
391 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
392 // RST5: spaces.live.com
\r
393 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
394 // RST6: storage.msn.com
\r
395 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
397 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
398 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
399 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
400 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
401 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
402 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
403 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
407 // no ticket found!
\r
408 if (count($matches) == 0) {
\r
409 $this->debug_message("*** Can't get passport ticket!");
\r
413 //$this->debug_message(var_export($matches, true));
\r
414 // matches[0]: all data
\r
415 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
417 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
419 // matches[5]: RST2 (messenger.msn.com) ticket
\r
421 // matches[7]: RST3 (contacts.msn.com) ticket
\r
423 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
424 // matches[10]: ...
\r
425 // matches[11]: RST5 (spaces.live.com) ticket
\r
426 // matches[12]: ...
\r
427 // matches[13]: RST6 (storage.live.com) ticket
\r
428 // matches[14]: ...
\r
431 // ticket => $matches[1]
\r
432 // secret => $matches[3]
\r
433 // web_ticket => $matches[5]
\r
434 // contact_ticket => $matches[7]
\r
435 // oim_ticket => $matches[9]
\r
436 // space_ticket => $matches[11]
\r
437 // storage_ticket => $matches[13]
\r
439 // yes, we get ticket
\r
441 'ticket' => html_entity_decode($matches[1]),
\r
442 'secret' => html_entity_decode($matches[3]),
\r
443 'web_ticket' => html_entity_decode($matches[5]),
\r
444 'contact_ticket' => html_entity_decode($matches[7]),
\r
445 'oim_ticket' => html_entity_decode($matches[9]),
\r
446 'space_ticket' => html_entity_decode($matches[11]),
\r
447 'storage_ticket' => html_entity_decode($matches[13])
\r
449 $this->ticket=$aTickets;
\r
450 $this->debug_message(var_export($aTickets, true));
\r
451 $ABAuthHeaderArray=array(
\r
452 'ABAuthHeader'=>array(
\r
453 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
454 'ManagedGroupRequest'=>false,
\r
455 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),
\r
458 $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));
\r
459 file_put_contents('/tmp/STTicket.txt',htmlspecialchars($this->ticket['storage_ticket']));
\r
460 //$this->debug_message("StorageTicket:\n",htmlspecialchars($this->ticket['storage_ticket']));
\r
463 private function UpdateContacts()
\r
465 $ABApplicationHeaderArray=array(
\r
466 'ABApplicationHeader'=>array(
\r
467 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
468 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
469 'IsMigration'=>false,
\r
470 'PartnerScenario'=>'ContactSave'
\r
473 $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
474 $ABFindAllArray=array(
\r
475 'ABFindAll'=>array(
\r
476 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
477 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
479 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',
\r
482 $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');
\r
483 $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));
\r
484 $this->Contacts=array();
\r
487 $this->debug_message("*** Update Contacts...");
\r
488 $Result=$this->ABService->ABFindAll($ABFindAll);
\r
489 $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());
\r
490 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
491 $this->Contacts[$Contact->contactInfo->passportName]=$Contact;
\r
493 catch(Exception $e)
\r
495 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
498 protected function addContact($email, $network, $display = '', $sendADL = false)
\r
500 if ($network != 1) return true;
\r
501 if(isset($this->Contacts[$email])) return true;
\r
503 $ABContactAddArray=array(
\r
504 'ABContactAdd'=>array(
\r
505 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
506 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
509 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
510 'contactInfo'=>array(
\r
511 'contactType'=>'LivePending',
\r
512 'passportName'=>$email,
\r
513 'isMessengerUser'=>true,
\r
514 'MessengerMemberInfo'=>array(
\r
515 'DisplayName'=>$email
\r
521 'EnableAllowListManagement'=>true
\r
525 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
528 $this->debug_message("*** Add Contacts $email...");
\r
529 $this->ABService->ABContactAdd($ABContactAdd);
\r
531 catch(Exception $e)
\r
533 $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
535 if ($sendADL && !feof($this->NSfp)) {
\r
536 @list($u_name, $u_domain) = @explode('@', $email);
\r
537 foreach (array('1', '2') as $l) {
\r
538 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
539 $len = strlen($str);
\r
540 // NS: >>> ADL {id} {size}
\r
541 $this->ns_writeln("ADL $this->id $len");
\r
542 $this->ns_writedata($str);
\r
545 $this->UpdateContacts();
\r
549 function delMemberFromList($memberID, $email, $network, $list) {
\r
550 if ($network != 1 && $network != 32) return true;
\r
551 if ($memberID === false) return true;
\r
553 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
555 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
556 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
557 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
558 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
559 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
561 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
562 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
563 <IsMigration>false</IsMigration>
\r
564 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
565 </ABApplicationHeader>
\r
566 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
567 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
568 <TicketToken>'.$ticket.'</TicketToken>
\r
572 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
575 <Type>Messenger</Type>
\r
576 <ForeignId></ForeignId>
\r
580 <MemberRole>'.$list.'</MemberRole>
\r
582 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
583 <Type>Passport</Type>
\r
584 <MembershipId>'.$memberID.'</MembershipId>
\r
585 <State>Accepted</State>
\r
594 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
595 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
596 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
597 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
598 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
600 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
601 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
602 <IsMigration>false</IsMigration>
\r
603 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
604 </ABApplicationHeader>
\r
605 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
606 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
607 <TicketToken>'.$ticket.'</TicketToken>
\r
611 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
614 <Type>Messenger</Type>
\r
615 <ForeignId></ForeignId>
\r
619 <MemberRole>'.$list.'</MemberRole>
\r
621 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
623 <MembershipId>'.$memberID.'</MembershipId>
\r
624 <State>Accepted</State>
\r
633 $header_array = array(
\r
634 'SOAPAction: '.$this->delmember_soap,
\r
635 'Content-Type: text/xml; charset=utf-8',
\r
636 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
639 $this->debug_message("*** URL: $this->delmember_url");
\r
640 $this->debug_message("*** Sending SOAP:\n$XML");
\r
641 $curl = curl_init();
\r
642 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
643 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
644 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
645 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
646 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
647 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
648 curl_setopt($curl, CURLOPT_POST, 1);
\r
649 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
650 $data = curl_exec($curl);
\r
651 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
653 $this->debug_message("*** Get Result:\n$data");
\r
655 if ($http_code != 200) {
\r
656 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
657 if (count($matches) == 0) {
\r
658 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
\r
661 $faultcode = trim($matches[1]);
\r
662 $faultstring = trim($matches[2]);
\r
663 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
664 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
\r
667 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
\r
670 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
\r
674 function addMemberToList($email, $network, $list) {
\r
675 if ($network != 1 && $network != 32) return true;
\r
676 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
680 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
681 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
682 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
683 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
684 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
686 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
687 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
688 <IsMigration>false</IsMigration>
\r
689 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
690 </ABApplicationHeader>
\r
691 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
692 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
693 <TicketToken>'.$ticket.'</TicketToken>
\r
697 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
700 <Type>Messenger</Type>
\r
701 <ForeignId></ForeignId>
\r
705 <MemberRole>'.$list.'</MemberRole>
\r
707 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
708 <Type>Passport</Type>
\r
709 <State>Accepted</State>
\r
710 <PassportName>'.$user.'</PassportName>
\r
719 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
720 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
721 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
722 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
723 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
725 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
726 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
727 <IsMigration>false</IsMigration>
\r
728 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
729 </ABApplicationHeader>
\r
730 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
731 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
732 <TicketToken>'.$ticket.'</TicketToken>
\r
736 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
739 <Type>Messenger</Type>
\r
740 <ForeignId></ForeignId>
\r
744 <MemberRole>'.$list.'</MemberRole>
\r
746 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
748 <State>Accepted</State>
\r
749 <Email>'.$user.'</Email>
\r
752 <Name>MSN.IM.BuddyType</Name>
\r
753 <Value>32:YAHOO</Value>
\r
763 $header_array = array(
\r
764 'SOAPAction: '.$this->addmember_soap,
\r
765 'Content-Type: text/xml; charset=utf-8',
\r
766 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
769 $this->debug_message("*** URL: $this->addmember_url");
\r
770 $this->debug_message("*** Sending SOAP:\n$XML");
\r
771 $curl = curl_init();
\r
772 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
773 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
774 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
775 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
776 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
777 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
778 curl_setopt($curl, CURLOPT_POST, 1);
\r
779 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
780 $data = curl_exec($curl);
\r
781 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
783 $this->debug_message("*** Get Result:\n$data");
\r
785 if ($http_code != 200) {
\r
786 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
787 if (count($matches) == 0) {
\r
788 $this->log_message("*** can't add member (network: $network) $email to $list");
\r
791 $faultcode = trim($matches[1]);
\r
792 $faultstring = trim($matches[2]);
\r
793 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
794 $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
\r
797 $this->log_message("*** add member (network: $network) $email to $list, already exist!");
\r
800 $this->log_message("*** add member (network: $network) $email to $list");
\r
804 function getMembershipList($returnData=false) {
\r
805 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
806 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
807 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
808 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
809 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
810 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
812 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
813 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
814 <IsMigration>false</IsMigration>
\r
815 <PartnerScenario>Initial</PartnerScenario>
\r
816 </ABApplicationHeader>
\r
817 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
818 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
819 <TicketToken>'.$ticket.'</TicketToken>
\r
823 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
826 <ServiceType>Messenger</ServiceType>
\r
827 <ServiceType>Invitation</ServiceType>
\r
828 <ServiceType>SocialNetwork</ServiceType>
\r
829 <ServiceType>Space</ServiceType>
\r
830 <ServiceType>Profile</ServiceType>
\r
836 $header_array = array(
\r
837 'SOAPAction: '.$this->membership_soap,
\r
838 'Content-Type: text/xml; charset=utf-8',
\r
839 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
841 $this->debug_message("*** URL: $this->membership_url");
\r
842 $this->debug_message("*** Sending SOAP:\n$XML");
\r
843 $curl = curl_init();
\r
844 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
845 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
846 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
847 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
848 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
849 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
850 curl_setopt($curl, CURLOPT_POST, 1);
\r
851 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
852 $data = curl_exec($curl);
\r
853 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
855 $this->debug_message("*** Get Result:\n$data");
\r
856 if($http_code != 200) return array();
\r
858 $aMemberships = array();
\r
860 //$this->debug_message("search p = $p");
\r
861 $start = strpos($p, '<Membership>');
\r
862 $end = strpos($p, '</Membership>');
\r
863 if ($start === false || $end === false || $start > $end) break;
\r
864 //$this->debug_message("start = $start, end = $end");
\r
866 $sMembership = substr($p, $start, $end - $start);
\r
867 $aMemberships[] = $sMembership;
\r
868 //$this->debug_message("add sMembership = $sMembership");
\r
869 $p = substr($p, $end);
\r
871 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
873 $aContactList = array();
\r
874 foreach ($aMemberships as $sMembership) {
\r
875 //$this->debug_message("sMembership = $sMembership");
\r
876 if (isset($matches)) unset($matches);
\r
877 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
878 if (count($matches) == 0) continue;
\r
879 $sMemberRole = $matches[1];
\r
880 //$this->debug_message("MemberRole = $sMemberRole");
\r
881 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
883 if (isset($aMembers)) unset($aMembers);
\r
884 $aMembers = array();
\r
886 //$this->debug_message("search p = $p");
\r
887 $start = strpos($p, '<Member xsi:type="');
\r
888 $end = strpos($p, '</Member>');
\r
889 if ($start === false || $end === false || $start > $end) break;
\r
890 //$this->debug_message("start = $start, end = $end");
\r
892 $sMember = substr($p, $start, $end - $start);
\r
893 $aMembers[] = $sMember;
\r
894 //$this->debug_message("add sMember = $sMember");
\r
895 $p = substr($p, $end);
\r
897 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
898 foreach ($aMembers as $sMember) {
\r
899 //$this->debug_message("sMember = $sMember");
\r
900 if (isset($matches)) unset($matches);
\r
901 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
902 if (count($matches) == 0) continue;
\r
903 $sMemberType = $matches[1];
\r
904 //$this->debug_message("MemberType = $sMemberType");
\r
906 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
907 if (count($matches) == 0) continue;
\r
909 if ($sMemberType == 'PassportMember') {
\r
910 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
912 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
914 else if ($sMemberType == 'EmailMember') {
\r
915 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
916 // Value is 32: or 32:YAHOO
\r
917 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
918 if (count($matches) == 0) continue;
\r
919 if ($matches[1] != 32) continue;
\r
921 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
923 if ($network == -1) continue;
\r
924 if (count($matches) > 0) {
\r
925 $email = $matches[1];
\r
926 @list($u_name, $u_domain) = @explode('@', $email);
\r
927 if ($u_domain == NULL) continue;
\r
928 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
929 $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
933 return $aContactList;
\r
936 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
938 if ($redirect_server === '') {
\r
939 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
\r
940 if (!$this->NSfp) {
\r
941 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
\r
946 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);
\r
947 if (!$this->NSfp) {
\r
948 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
953 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
954 $this->authed = false;
\r
956 // NS: >> VER {id} MSNP9 CVR0
\r
958 // NS: >>> VER {id} MSNP15 CVR0
\r
959 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
961 $start_tm = time();
\r
962 while (!feof($this->NSfp))
\r
964 $data = $this->ns_readln();
\r
966 if ($data === false) {
\r
967 if ($this->timeout > 0) {
\r
969 $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
\r
970 if ($used_time > $this->timeout) {
\r
973 $this->ns_writeln("OUT");
\r
974 fclose($this->NSfp);
\r
975 $this->error = 'Timeout, maybe protocol changed!';
\r
976 $this->debug_message("*** $this->error");
\r
982 $code = substr($data, 0, 3);
\r
983 $start_tm = time();
\r
988 // NS: <<< VER {id} MSNP9 CVR0
\r
989 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
991 // NS: <<< VER {id} MSNP15 CVR0
\r
992 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
993 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
998 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
999 // NS: >>> USR {id} TWN I {user}
\r
1001 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
1002 // NS: >>> USR {id} SSO I {user}
\r
1003 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
1007 // already login for passport site, finish the login process now.
\r
1008 // NS: <<< USR {id} OK {user} {verify} 0
\r
1009 if ($this->authed) return true;
\r
1010 // max. 16 digits for password
\r
1011 if (strlen($password) > 16)
\r
1012 $password = substr($password, 0, 16);
\r
1014 $this->user = $user;
\r
1015 $this->password = $password;
\r
1016 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1017 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1019 $this->passport_policy = $policy;
\r
1020 $aTickets = $this->get_passport_ticket();
\r
1021 if (!$aTickets || !is_array($aTickets)) {
\r
1024 $this->ns_writeln("OUT");
\r
1025 fclose($this->NSfp);
\r
1026 $this->error = 'Passport authenticated fail!';
\r
1027 $this->debug_message("*** $this->error");
\r
1031 $ticket = $aTickets['ticket'];
\r
1032 $secret = $aTickets['secret'];
\r
1033 $this->ticket = $aTickets;
\r
1034 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1036 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1037 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1038 $this->authed = true;
\r
1042 // main login server will redirect to anther NS after USR command
\r
1044 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1046 // NS: <<< XFR {id} NS {server} U D
\r
1047 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1048 if($Type!='NS') break;
\r
1049 @list($ip, $port) = @explode(':', $server);
\r
1050 // this connection will close after XFR
\r
1051 fclose($this->NSfp);
\r
1053 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
1054 if (!$this->NSfp) {
\r
1055 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1056 $this->debug_message("*** $this->error");
\r
1060 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1062 // NS: >> VER {id} MSNP9 CVR0
\r
1064 // NS: >>> VER {id} MSNP15 CVR0
\r
1065 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1069 // return some policy data after 'USR {id} SSO I {user}' command
\r
1070 // NS: <<< GCF 0 {size}
\r
1071 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1072 // we don't need the data, just read it and drop
\r
1073 if (is_numeric($size) && $size > 0)
\r
1074 $this->ns_readdata($size);
\r
1078 // we'll quit if got any error
\r
1079 if (is_numeric($code)) {
\r
1082 $this->ns_writeln("OUT");
\r
1083 fclose($this->NSfp);
\r
1084 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1085 $this->debug_message("*** $this->error");
\r
1088 // unknown response from server, just ignore it
\r
1092 // never goto here
\r
1095 function derive_key($key, $magic) {
\r
1096 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1097 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1098 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1099 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1100 return $hash2.substr($hash4, 0, 4);
\r
1103 function generateLoginBLOB($key, $challenge) {
\r
1104 $key1 = base64_decode($key);
\r
1105 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1106 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1108 // get hash of challenge using key2
\r
1109 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1111 // get 8 bytes random data
\r
1112 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1114 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1116 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1121 return base64_encode($blob);
\r
1124 function getOIM_maildata() {
\r
1125 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1126 if (count($matches) == 0) {
\r
1127 $this->debug_message('*** no web ticket?');
\r
1130 $t = htmlspecialchars($matches[1]);
\r
1131 $p = htmlspecialchars($matches[2]);
\r
1132 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1133 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1134 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1135 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1137 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1143 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1145 </soap:Envelope>';
\r
1147 $header_array = array(
\r
1148 'SOAPAction: '.$this->oim_maildata_soap,
\r
1149 'Content-Type: text/xml; charset=utf-8',
\r
1150 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1153 $this->debug_message("*** URL: $this->oim_maildata_url");
\r
1154 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1155 $curl = curl_init();
\r
1156 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1157 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1158 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1159 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1160 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1161 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1162 curl_setopt($curl, CURLOPT_POST, 1);
\r
1163 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1164 $data = curl_exec($curl);
\r
1165 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1166 curl_close($curl);
\r
1167 $this->debug_message("*** Get Result:\n$data");
\r
1169 if ($http_code != 200) {
\r
1170 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
\r
1174 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1175 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1176 if (count($matches) == 0) {
\r
1177 $this->debug_message("*** Can't get OIM maildata");
\r
1180 return $matches[2];
\r
1183 function getOIM_message($msgid) {
\r
1184 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1185 if (count($matches) == 0) {
\r
1186 $this->debug_message('*** no web ticket?');
\r
1189 $t = htmlspecialchars($matches[1]);
\r
1190 $p = htmlspecialchars($matches[2]);
\r
1193 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1194 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1195 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1196 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1198 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1204 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1205 <messageId>'.$msgid.'</messageId>
\r
1206 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1209 </soap:Envelope>';
\r
1211 $header_array = array(
\r
1212 'SOAPAction: '.$this->oim_read_soap,
\r
1213 'Content-Type: text/xml; charset=utf-8',
\r
1214 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1217 $this->debug_message("*** URL: $this->oim_read_url");
\r
1218 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1219 $curl = curl_init();
\r
1220 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1221 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1222 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1223 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1224 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1225 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1226 curl_setopt($curl, CURLOPT_POST, 1);
\r
1227 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1228 $data = curl_exec($curl);
\r
1229 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1230 curl_close($curl);
\r
1231 $this->debug_message("*** Get Result:\n$data");
\r
1233 if ($http_code != 200) {
\r
1234 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1238 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1240 $start = strpos($data, '<GetMessageResult>');
\r
1241 $end = strpos($data, '</GetMessageResult>');
\r
1242 if ($start === false || $end === false || $start > $end) {
\r
1243 $this->debug_message("*** Can't get OIM: $msgid");
\r
1246 $lines = substr($data, $start + 18, $end - $start);
\r
1247 $aLines = @explode("\n", $lines);
\r
1251 foreach ($aLines as $line) {
\r
1252 $line = rtrim($line);
\r
1254 if ($line === '') {
\r
1260 // stop at empty lines
\r
1261 if ($line === '') break;
\r
1264 $sMsg = base64_decode($sOIM);
\r
1265 $this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1268 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1269 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1270 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1271 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1273 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1279 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1281 <messageId>'.$msgid.'</messageId>
\r
1285 </soap:Envelope>';
\r
1287 $header_array = array(
\r
1288 'SOAPAction: '.$this->oim_del_soap,
\r
1289 'Content-Type: text/xml; charset=utf-8',
\r
1290 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1293 $this->debug_message("*** URL: $this->oim_del_url");
\r
1294 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1295 $curl = curl_init();
\r
1296 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1297 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1298 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1299 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1300 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1301 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1302 curl_setopt($curl, CURLOPT_POST, 1);
\r
1303 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1304 $data = curl_exec($curl);
\r
1305 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1306 curl_close($curl);
\r
1307 $this->debug_message("*** Get Result:\n$data");
\r
1309 if ($http_code != 200)
\r
1310 $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
\r
1312 $this->debug_message("*** OIM ($msgid) deleted");
\r
1315 private function NSLogout() {
\r
1316 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1319 $this->ns_writeln("OUT");
\r
1320 fclose($this->NSfp);
\r
1321 $this->NSfp = false;
\r
1322 $this->log_message("*** logout now!");
\r
1326 private function NSRetryWait($Wait) {
\r
1327 $this->log_message("*** wait for $Wait seconds");
\r
1328 for($i=0;$i<$Wait;$i++) {
\r
1330 if($this->kill_me) return false;
\r
1334 public function ProcessSendMessageFileQueue() {
\r
1335 $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');
\r
1336 if (!is_array($aFiles)) return true;
\r
1338 foreach ($aFiles as $filename) {
\r
1339 $fp = fopen($filename, 'rt');
\r
1340 if (!$fp) continue;
\r
1343 $buf = trim(fgets($fp));
\r
1344 if (substr($buf, 0, 3) == 'TO:') {
\r
1345 $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));
\r
1346 while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";
\r
1349 if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')
\r
1350 $this->log_message("!!! message format error? delete $filename");
\r
1353 foreach($aTo as $To)
\r
1355 @list($user, $domain, $network) = @explode('@', $To);
\r
1356 $MessageList[$network]["$user@$domain"]=$sMessage;
\r
1359 if($this->backup_file)
\r
1361 $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';
\r
1362 if (!file_exists($backup_dir)) @mkdir($backup_dir);
\r
1363 $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);
\r
1364 if (@rename($filename, $backup_name))
\r
1365 $this->log_message("*** move file to $backup_name");
\r
1367 else @unlink($filename);
\r
1369 foreach ($MessageList as $network => $Messages)
\r
1371 switch(trim($network))
\r
1375 // okay, try to ask a switchboard (SB) for sending message
\r
1376 // NS: >>> XFR {id} SB
\r
1377 // $this->ns_writeln("XFR $this->id SB");
\r
1378 foreach($Messages as $User => $Message)
\r
1379 $this->MessageQueue[$User][]=$Message;
\r
1381 case 'Offline': //MSN
\r
1383 //FIXME: 修正Send OIM
\r
1384 foreach($Messages as $To => $Message)
\r
1387 for ($i = 0; $i < $this->oim_try; $i++)
\r
1389 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
1390 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
1391 // need challenge lockkey
\r
1392 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
1393 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
1396 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
1400 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
1403 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
1404 // maybe we need to re-login again
\r
1405 if(!$this->get_passport_ticket())
\r
1407 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1410 $this->log_message("**** get new ticket, try it again");
\r
1417 foreach($Messages as $To => $Message) {
\r
1418 $Message=$this->getMessage($Message, $network);
\r
1419 $len = strlen($Message);
\r
1420 $this->ns_writeln("UUM $this->id $To $network 1 $len");
\r
1421 $this->ns_writedata($Message);
\r
1422 $this->log_message("*** sent to $To (network: $network):\n$Message");
\r
1426 if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))
\r
1428 $this->MessageQueue[$User]['XFRSent']=false;
\r
1429 $this->MessageQueue[$User]['ReqTime']=false;
\r
1433 public function SignalFunction($signal)
\r
1443 $ChildPid=pcntl_wait($status,WUNTRACED);
\r
1446 $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);
\r
1447 unset($this->ChildProcess[$ChildPid]);
\r
1453 public function Run()
\r
1455 $this->log_message("*** startup ***");
\r
1456 if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");
\r
1457 if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");
\r
1458 if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");
\r
1459 $process_file = false;
\r
1462 $aContactList = array();
\r
1465 if($this->kill_me)
\r
1467 $this->log_message("*** Okay, kill me now!");
\r
1468 return $this->NSLogout();
\r
1470 if (!is_resource($this->NSfp) || feof($this->NSfp))
\r
1472 $this->log_message("*** try to connect to MSN network");
\r
1473 if (!$this->connect($this->user, $this->password))
\r
1475 $this->log_message("!!! Can't connect to server: $this->error");
\r
1476 if(!$this->NSRetryWait($this->retry_wait)) continue;
\r
1478 $this->UpdateContacts();
\r
1479 $this->LastPing=time();
\r
1480 $this->log_message("*** connected, wait for command");
\r
1481 $start_tm = time();
\r
1482 $ping_tm = time();
\r
1483 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1484 $aContactList = $this->getMembershipList();
\r
1485 if ($this->update_pending) {
\r
1486 if (is_array($aContactList)) {
\r
1487 $pending = 'Pending';
\r
1488 foreach ($aContactList as $u_domain => $aUserList) {
\r
1489 foreach ($aUserList as $u_name => $aNetworks) {
\r
1490 foreach ($aNetworks as $network => $aData) {
\r
1491 if (isset($aData[$pending])) {
\r
1494 foreach (array('Allow', 'Reverse') as $list) {
\r
1495 if (isset($aData[$list]))
\r
1498 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1499 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1505 $id = $aData[$pending];
\r
1506 // we can delete it from pending now
\r
1507 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1508 unset($aContactList[$u_domain][$u_name][$network][$pending]);
\r
1513 foreach (array('Allow', 'Reverse') as $list) {
\r
1514 if (!isset($aData[$list])) {
\r
1515 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1516 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1528 if (is_array($aContactList)) {
\r
1529 foreach ($aContactList as $u_domain => $aUserList) {
\r
1530 $str = '<d n="'.$u_domain.'">';
\r
1531 $len += strlen($str);
\r
1532 if ($len > 7400) {
\r
1533 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1536 $len = strlen($str);
\r
1539 foreach ($aUserList as $u_name => $aNetworks) {
\r
1540 foreach ($aNetworks as $network => $status) {
\r
1541 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1542 $len += strlen($str);
\r
1543 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1545 if ($len > 7475) {
\r
1547 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1549 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1550 $len = strlen($sList);
\r
1559 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1560 // NS: >>> BLP {id} BL
\r
1561 $this->ns_writeln("BLP $this->id BL");
\r
1562 foreach ($aADL as $str) {
\r
1563 $len = strlen($str);
\r
1564 // NS: >>> ADL {id} {size}
\r
1565 $this->ns_writeln("ADL $this->id $len");
\r
1566 $this->ns_writedata($str);
\r
1568 // NS: >>> PRP {id} MFN name
\r
1569 if ($this->alias == '') $this->alias = $user;
\r
1570 $aliasname = rawurlencode($this->alias);
\r
1571 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1573 //$MsnObj=$this->PhotoStckObj();
\r
1574 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1575 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1576 if($this->PhotoStickerFile!==false)
\r
1577 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1578 // NS: >>> UUX {id} length
\r
1579 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1580 $len = strlen($str);
\r
1581 $this->ns_writeln("UUX $this->id $len");
\r
1582 $this->ns_writedata($str);
\r
1584 $data = $this->ns_readln();
\r
1587 //If No NS Message Process SendMessageFileQueue
\r
1588 if (time()-$this->LastPing > $this->ping_wait)
\r
1591 $this->ns_writeln("PNG");
\r
1592 $this->LastPing = time();
\r
1594 if(count($this->ChildProcess)<$this->MAXChildProcess)
\r
1597 foreach($this->MessageQueue as $User => $Message)
\r
1599 if(!trim($User)) continue;
\r
1600 if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;
\r
1601 if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))
\r
1603 $this->MessageQueue[$User]['XFRSent']=true;
\r
1604 $this->MessageQueue[$User]['ReqTime']=time();
\r
1605 $this->log_message("*** Request SB for $User");
\r
1606 $this->ns_writeln("XFR $this->id SB");
\r
1611 if($this->ProcessSendMessageFileQueue()) continue;
\r
1614 switch (substr($data,0,3))
\r
1617 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
1618 // NS: <<< SBS 0 null
\r
1623 // NS: <<< RFS ???
\r
1624 // refresh ADL, so we re-send it again
\r
1625 if (is_array($aADL)) {
\r
1626 foreach ($aADL as $str) {
\r
1627 $len = strlen($str);
\r
1628 // NS: >>> ADL {id} {size}
\r
1629 $this->ns_writeln("ADL $this->id $len");
\r
1630 $this->ns_writedata($str);
\r
1636 // NS: <<< LST {email} {alias} 11 0
\r
1637 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
1638 @list($u_name, $u_domain) = @explode('@', $email);
\r
1639 if (!isset($aContactList[$u_domain][$u_name][1])) {
\r
1640 $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
1641 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
1646 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
1647 // NS: <<< ADL 0 {size}
\r
1648 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
1649 if (is_numeric($size) && $size > 0)
\r
1651 $data = $this->ns_readdata($size);
\r
1652 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1653 if (is_array($matches) && count($matches) > 0)
\r
1655 $u_domain = $matches[1];
\r
1656 $u_name = $matches[2];
\r
1657 $network = $matches[4];
\r
1658 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1659 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
1662 $re_login = false;
\r
1664 foreach (array('Allow', 'Reverse') as $list)
\r
1666 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1669 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1672 $aTickets = $this->get_passport_ticket();
\r
1673 if (!$aTickets || !is_array($aTickets)) {
\r
1674 // failed to login? ignore it
\r
1675 $this->log_message("*** can't re-login, something wrong here");
\r
1676 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1680 $this->ticket = $aTickets;
\r
1681 $this->log_message("**** get new ticket, try it again");
\r
1682 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1684 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1688 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1691 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
1693 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
1694 $len = strlen($str);
\r
1697 $this->log_message("*** someone add us to their list: $data");
\r
1698 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
1703 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
1704 // NS: <<< RML 0 {size}
\r
1705 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
1706 if (is_numeric($size) && $size > 0)
\r
1708 $data = $this->ns_readdata($size);
\r
1709 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1710 if (is_array($matches) && count($matches) > 0)
\r
1712 $u_domain = $matches[1];
\r
1713 $u_name = $matches[2];
\r
1714 $network = $matches[4];
\r
1715 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1717 $aData = $aContactList[$u_domain][$u_name][$network];
\r
1718 foreach ($aData as $list => $id)
\r
1719 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
1720 unset($aContactList[$u_domain][$u_name][$network]);
\r
1721 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
1724 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
1725 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
1728 $this->log_message("*** someone remove us from their list: $data");
\r
1733 // randomly, we get MSG notification from server
\r
1734 // NS: <<< MSG Hotmail Hotmail {size}
\r
1735 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
1736 if (is_numeric($size) && $size > 0) {
\r
1737 $data = $this->ns_readdata($size);
\r
1738 $aLines = @explode("\n", $data);
\r
1742 foreach ($aLines as $line) {
\r
1743 $line = rtrim($line);
\r
1745 if ($line === '') {
\r
1749 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
1750 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
1751 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
1752 // we just need text/x-msmsgsinitialmdatanotification
\r
1753 // or text/x-msmsgsoimnotification
\r
1760 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
1761 $maildata = trim(substr($line, 10));
\r
1766 $this->log_message("*** ingnore MSG for: $line");
\r
1769 if ($maildata == '') {
\r
1770 $this->log_message("*** ingnore MSG not for OIM");
\r
1773 $re_login = false;
\r
1774 if (strcasecmp($maildata, 'too-large') == 0) {
\r
1775 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
1776 $maildata = $this->getOIM_maildata();
\r
1777 if ($maildata === false) {
\r
1778 $this->log_message("*** can't get mail-data via SOAP");
\r
1779 // maybe we need to re-login again
\r
1780 $aTickets = $this->get_passport_ticket();
\r
1781 if (!$aTickets || !is_array($aTickets)) {
\r
1782 // failed to login? ignore it
\r
1783 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1787 $this->ticket = $aTickets;
\r
1788 $this->log_message("**** get new ticket, try it again");
\r
1789 $maildata = $this->getOIM_maildata();
\r
1790 if ($maildata === false) {
\r
1791 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
1796 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
1800 $start = strpos($p, '<M>');
\r
1801 $end = strpos($p, '</M>');
\r
1802 if ($start === false || $end === false || $start > $end) break;
\r
1804 $sOIM = substr($p, $start, $end - $start);
\r
1806 $p = substr($p, $end);
\r
1808 if (count($aOIMs) == 0) {
\r
1809 $this->log_message("*** ingnore empty OIM");
\r
1812 foreach ($aOIMs as $maildata) {
\r
1813 // T: 11 for MSN, 13 for Yahoo
\r
1814 // S: 6 for MSN, 7 for Yahoo
\r
1815 // RT: the datetime received by server
\r
1816 // RS: already read or not
\r
1817 // SZ: size of message
\r
1820 // F: always 00000000-0000-0000-0000-000000000009
\r
1821 // N: sender alias
\r
1822 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
1823 if (count($matches) == 0) {
\r
1824 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
1827 $oim_type = $matches[1];
\r
1828 if ($oim_type = 13)
\r
1832 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
1833 if (count($matches) == 0) {
\r
1834 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
1837 $oim_sender = $matches[1];
\r
1838 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
1839 if (count($matches) == 0) {
\r
1840 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
1843 $oim_msgid = $matches[1];
\r
1844 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
1845 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
1846 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
1847 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
1848 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
1849 $sMsg = $this->getOIM_message($oim_msgid);
\r
1850 if ($sMsg === false) {
\r
1851 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
1853 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1856 $aTickets = $this->get_passport_ticket();
\r
1857 if (!$aTickets || !is_array($aTickets)) {
\r
1858 // failed to login? ignore it
\r
1859 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1863 $this->ticket = $aTickets;
\r
1864 $this->log_message("**** get new ticket, try it again");
\r
1865 $sMsg = $this->getOIM_message($oim_msgid);
\r
1866 if ($sMsg === false) {
\r
1867 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1871 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
1873 $this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
1879 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
1880 // NS: <<< UBM {email} $network $type {size}
\r
1881 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
1882 if (is_numeric($size) && $size > 0)
\r
1884 $data = $this->ns_readdata($size);
\r
1885 $aLines = @explode("\n", $data);
\r
1889 foreach ($aLines as $line) {
\r
1890 $line = rtrim($line);
\r
1892 if ($line === '') {
\r
1896 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
1902 $aSubLines = @explode("\r", $line);
\r
1903 foreach ($aSubLines as $str) {
\r
1911 $this->log_message("*** ingnore from $from_email: $line");
\r
1914 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
1915 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
1920 // randomly, we get UBX notification from server
\r
1921 // NS: <<< UBX email {network} {size}
\r
1922 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
1923 // we don't need the notification data, so just ignore it
\r
1924 if (is_numeric($size) && $size > 0)
\r
1925 $this->ns_readdata($size);
\r
1929 // randomly, we'll get challenge from server
\r
1930 // NS: <<< CHL 0 {code}
\r
1931 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
1932 $fingerprint = $this->getChallenge($chl_code);
\r
1933 // NS: >>> QRY {id} {product_id} 32
\r
1934 // NS: >>> fingerprint
\r
1935 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
1936 $this->ns_writedata($fingerprint);
\r
1937 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1938 if($this->PhotoStickerFile!==false)
\r
1939 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1942 // NS: <<< CHG {id} {status} {code}
\r
1944 // change our status to online first
\r
1948 // sometimes, NS will redirect to another NS
\r
1950 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1952 // NS: <<< XFR {id} NS {server} U D
\r
1953 // for normal switchboard XFR
\r
1954 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
1955 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
1956 @list($ip, $port) = @explode(':', $server);
\r
1957 if ($server_type != 'SB') {
\r
1959 // this connection will close after XFR
\r
1960 $this->NSLogout();
\r
1963 if(count($this->MessageQueue))
\r
1965 foreach($this->MessageQueue as $User => $Message)
\r
1967 //$this->ChildProcess[$ChildPid]
\r
1968 $this->log_message("*** XFR SB $User");
\r
1969 $pid=pcntl_fork();
\r
1973 $this->ChildProcess[$pid]=$User;
\r
1978 $this->log_message("*** Fork Error $User");
\r
1984 $this->log_message("*** Child Process Start for $User");
\r
1985 unset($Message['XFRSent']);
\r
1986 unset($Message['ReqTime']);
\r
1987 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
1988 if ($bSBresult === false)
\r
1990 // error for switchboard
\r
1991 $this->log_message("!!! error for sending message to ".$User);
\r
1996 unset($this->MessageQueue[$User]);
\r
1999 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
2000 if ($bSBresult === false) {
\r
2001 // error for switchboard
\r
2002 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
2003 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
2007 // NS: <<< QNG {time}
\r
2008 @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
2009 if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
2010 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
2011 //Mod by Ricky Set Online
\r
2015 if($this->PhotoStickerFile!==false)
\r
2016 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2018 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2019 // someone is trying to talk to us
\r
2020 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2021 $this->log_message("NS: <<< RNG $data");
\r
2022 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2023 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2024 if($this->IsIgnoreMail($email))
\r
2026 $this->log_message("*** Ignore RNG from $email");
\r
2029 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
2030 $this->addContact($email,1,$email, true);
\r
2031 $pid=pcntl_fork();
\r
2035 $this->ChildProcess[$pid]='RNG';
\r
2040 $this->log_message("*** Fork Error $User");
\r
2046 $this->log_message("*** Ring Child Process Start for $User");
\r
2047 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
2052 // force logout from NS
\r
2053 // NS: <<< OUT xxx
\r
2054 fclose($this->NSfp);
\r
2055 $this->log_message("*** LOGOUT from NS");
\r
2059 $code = substr($data,0,3);
\r
2060 if (is_numeric($code)) {
\r
2061 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2062 $this->debug_message("*** NS: $this->error");
\r
2064 return $this->NsLogout();
\r
2069 return $this->NsLogout();
\r
2072 /*public function SendMessage($Message, $To)
\r
2074 $FileName = MSN_CLASS_SPOOL_DIR.'/'.strftime('%Y%m%d%H%M%S',time()).'_'.posix_getpid().'_sendMessage.msn';
\r
2075 if(!is_array($To))
\r
2078 foreach($To as $Email)
\r
2080 list($name,$host,$network)=explode('@',$Email);
\r
2081 $network=$network==''?1:$network;
\r
2082 if($network==1 && $this->SwitchBoardProcess && $this->SwitchBoardSessionUser=="$name@$host" )
\r
2084 $this->debug_message("*** SendMessage to $Receiver use SB message queue.");
\r
2085 array_push($this->SwitchBoardMessageQueue,$Message);
\r
2088 $Receiver.="$name@$host@$network,";
\r
2090 if($Receiver=='') return;
\r
2091 $Receiver=substr($Receiver,0,-1);
\r
2092 $this->debug_message("*** SendMessage to $Receiver use File queue.");
\r
2093 file_put_contents($FileName,"TO: $Receiver\n$Message\n");
\r
2096 function getChallenge($code)
\r
2099 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
2100 // Step 1: The MD5 Hash
\r
2101 $md5Hash = md5($code.$this->prod_key);
\r
2102 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2103 for ($i = 0; $i < 4; $i++) {
\r
2104 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
2105 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
2108 // Step 2: A new string
\r
2109 $chl_id = $code.$this->prod_id;
\r
2110 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
2112 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
2113 for ($i = 0; $i < count($aID); $i++) {
\r
2114 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
2115 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
2118 // Step 3: The 64 bit key
\r
2119 $magic_num = 0x0E79A9C1;
\r
2120 $str7f = 0x7FFFFFFF;
\r
2123 for ($i = 0; $i < count($aID); $i += 2) {
\r
2125 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
2126 $temp = bcadd($temp, $high);
\r
2127 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
2128 $temp = bcmod($temp, $str7f);
\r
2130 $high = $aID[$i+1];
\r
2131 $high = bcmod(bcadd($high, $temp), $str7f);
\r
2132 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
2133 $high = bcmod($high, $str7f);
\r
2135 $low = bcadd(bcadd($low, $high), $temp);
\r
2138 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
2139 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
2141 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
2142 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
2143 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
2144 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
2145 // we need integer here
\r
2146 $high = 0+$new_high;
\r
2148 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
2149 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
2150 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
2151 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
2152 // we need integer here
\r
2153 $low = 0+$new_low;
\r
2155 // we just use 32 bits integer, don't need the key, just high/low
\r
2156 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
2158 // Step 4: Using the key
\r
2159 $md5Hash = md5($code.$this->prod_key);
\r
2160 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2163 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
2164 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
2165 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
2166 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
2171 private function getMessage($sMessage, $network = 1)
\r
2173 $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
2174 $msg_header_len = strlen($msg_header);
\r
2175 if ($network == 1)
\r
2176 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
2178 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
2179 $sMessage=str_replace("\r", '', $sMessage);
\r
2180 $msg=substr($sMessage,0,$maxlen);
\r
2181 return $msg_header.$msg;
\r
2185 * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息
\r
2189 private function DoSwitchBoard($Action,$Param)
\r
2191 $SessionEnd=false;
\r
2194 $LastActive=time();
\r
2195 stream_set_timeout($this->SBFp, $this->SBTimeout);
\r
2199 $cki_code=$Param['cki'];
\r
2200 $user=$Param['user'];
\r
2201 $this->SwitchBoardMessageQueue=$Param['Msg'];
\r
2202 // SB: >>> USR {id} {user} {cki}
\r
2203 $this->SB_writeln("USR $id $this->user $cki_code");
\r
2205 $this->SwitchBoardSessionUser=$user;
\r
2208 $ticket=$Param['ticket'];
\r
2209 $sid=$Param['sid'];
\r
2210 $user=$Param['user'];
\r
2211 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2212 $this->SB_writeln("ANS $id $this->user $ticket $sid");
\r
2214 $this->SwitchBoardSessionUser=$user;
\r
2219 while((!feof($this->SBFp))&&(!$SessionEnd))
\r
2221 $data = $this->SB_readln();
\r
2222 if($this->kill_me)
\r
2224 $this->log_message("*** SB Okay, kill me now!");
\r
2227 if($data === false)
\r
2229 if(time()-$LastActive > $this->SBIdleTimeout)
\r
2231 $this->debug_message("*** SB Idle Timeout!");
\r
2234 if(!$Joined) continue;
\r
2235 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2237 if($Message=='') continue;
\r
2238 $aMessage = $this->getMessage($Message);
\r
2240 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2241 if($MsnObjDefine!=='')
\r
2243 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2244 $len = strlen($SendString);
\r
2245 $this->SB_writeln("MSG $id N $len");
\r
2247 $this->SB_writedata($SendString);
\r
2250 $len = strlen($aMessage);
\r
2251 $this->SB_writeln("MSG $id N $len");
\r
2253 $this->SB_writedata($aMessage);
\r
2255 $this->SwitchBoardMessageQueue=array();
\r
2256 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2259 $code = substr($data, 0, 3);
\r
2263 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
2264 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
2265 $this->log_message("*** $email join us");
\r
2269 $this->log_message("*** Quit for BYE");
\r
2273 // SB: <<< USR {id} OK {user} {alias}
\r
2274 // we don't need the data, just ignore it
\r
2275 // request user to join this switchboard
\r
2276 // SB: >>> CAL {id} {user}
\r
2277 $this->SB_writeln("CAL $id $user");
\r
2281 // SB: <<< CAL {id} RINGING {?}
\r
2282 // we don't need this, just ignore, and wait for other response
\r
2286 // SB: <<< JOI {user} {alias} {clientid?}
\r
2287 // someone join us
\r
2288 // we don't need the data, just ignore it
\r
2289 // no more user here
\r
2293 // SB: <<< MSG {email} {alias} {len}
\r
2294 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
2295 $len = trim($len);
\r
2296 $data = $this->SB_readdata($len);
\r
2297 $aLines = @explode("\n", $data);
\r
2302 foreach ($aLines as $line)
\r
2304 $line = rtrim($line);
\r
2306 if ($line === '') {
\r
2310 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2311 // typing notification, just ignore
\r
2315 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
2316 // we don't handle any split message, just ignore
\r
2320 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
2321 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
2323 $p = strstr($data, "\n\n");
\r
2325 if ($p === false) {
\r
2326 $p = strstr($data, "\r\n\r\n");
\r
2328 $sMsg = substr($p, 4);
\r
2331 $sMsg = substr($p, 2);
\r
2334 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
2335 // ignore all application/x-... message
\r
2337 // application/x-ms-ink => ink message
\r
2341 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
2342 // ignore all text/x-... message
\r
2344 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
2345 // text/x-mms-animemoticon => customized animemotion word
\r
2357 $this->log_message("*** ingnore from $from_email: $line");
\r
2362 // we will ignore any p2p message after sending acknowledgement
\r
2364 $len = strlen($sMsg);
\r
2365 $this->log_message("*** p2p message from $from_email, size $len");
\r
2366 // header = 48 bytes
\r
2367 // content >= 0 bytes
\r
2368 // footer = 4 bytes
\r
2369 // so it need to >= 52 bytes
\r
2370 /*if ($len < 52) {
\r
2371 $this->log_message("*** p2p: size error, less than 52!");
\r
2374 $aDwords = @unpack("V12dword", $sMsg);
\r
2375 if (!is_array($aDwords)) {
\r
2376 $this->log_message("*** p2p: header unpack error!");
\r
2379 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
2380 $hdr_SessionID = $aDwords['dword1'];
\r
2381 $hdr_Identifier = $aDwords['dword2'];
\r
2382 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
2383 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
2384 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
2385 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
2386 $hdr_MessageLength = $aDwords['dword7'];
\r
2387 $hdr_Flag = $aDwords['dword8'];
\r
2388 $hdr_AckID = $aDwords['dword9'];
\r
2389 $hdr_AckUID = $aDwords['dword10'];
\r
2390 $hdr_AckSizeLow = $aDwords['dword11'];
\r
2391 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
2392 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
2393 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
2394 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
2395 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
2396 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
2397 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
2398 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
2399 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
2400 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
2401 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
2402 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
2403 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
2404 if($hdr_Flag==2) {
\r
2405 //This is an ACK from SB ignore....
\r
2406 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
2409 $MsgBody=$this->linetoArray(substr($sMsg,48,-4));
\r
2410 $this->debug_message("*** p2p: body".print_r($MsgBody,true));
\r
2411 if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))
\r
2415 if($this->SB_readln()===false) break;
\r
2417 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));
\r
2418 preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);
\r
2419 $BranchGUID=$Matches[1];
\r
2420 //it's an invite to send a display picture.
\r
2421 $new_id = ~$hdr_Identifier;
\r
2422 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2425 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2430 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2431 $footer = pack("L", 0);
\r
2432 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2433 $len = strlen($message);
\r
2434 $this->SB_writeln("MSG $id D $len");
\r
2436 $this->SB_writedata($message);
\r
2437 $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
2438 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
2439 $this->SB_readln();//Read ACK;
\r
2440 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
2442 //Send 200 OK message
\r
2443 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
2445 "MSNSLP/1.0 200 OK\r\n".
\r
2446 "To: <msnmsgr:".$from_email.">\r\n".
\r
2447 "From: <msnmsgr:".$this->user.">\r\n".
\r
2448 "Via: ".$MsgBody['Via']."\r\n".
\r
2449 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
2450 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2451 "Max-Forwards: 0\r\n".
\r
2452 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
2453 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
2455 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2456 $hdr_TotalDataSizeHigh=0;
\r
2457 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2460 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2461 strlen($MessagePayload),
\r
2468 "MIME-Version: 1.0\r\n".
\r
2469 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2470 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2471 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2473 $this->SB_writedata($message);
\r
2474 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
2475 $this->SB_readln();//Read ACK;
\r
2477 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
2478 //send Data preparation message
\r
2479 //send 4 null bytes as data
\r
2480 $hdr_TotalDataSizeLow=4;
\r
2481 $hdr_TotalDataSizeHigh=0;
\r
2483 $hdr = pack("LLLLLLLLLLLL",
\r
2484 $MsgBody['SessionID'],
\r
2487 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2488 $hdr_TotalDataSizeLow,
\r
2494 "MIME-Version: 1.0\r\n".
\r
2495 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2496 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";
\r
2497 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2499 $this->SB_writedata($message);
\r
2500 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
2501 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
2502 $this->SB_readln();//Read ACK;
\r
2504 //send Data Content..
\r
2505 $footer=pack('N',1);
\r
2507 $FileSize=filesize($PictureFilePath);
\r
2508 if($hTitle=fopen($PictureFilePath,'rb'))
\r
2512 while(!feof($hTitle))
\r
2514 $FileContent=fread($hTitle,1024);
\r
2515 $FileContentSize=strlen($FileContent);
\r
2516 $hdr = pack("LLLLLLLLLLLL",
\r
2517 $MsgBody['SessionID'],
\r
2528 "MIME-Version: 1.0\r\n".
\r
2529 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2530 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
2531 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2533 $this->SB_writedata($message);
\r
2534 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
2535 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
2536 //$this->SB_readln();//Read ACK;
\r
2537 $Offset+=$FileContentSize;
\r
2542 $MessageContent="\r\n".pack("C", 0);
\r
2544 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
2545 "To: <msnmsgr:$from_email>\r\n".
\r
2546 "From: <msnmsgr:".$this->user.">\r\n".
\r
2547 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
2549 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2550 "Max-Forwards: 0\r\n".
\r
2551 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
2552 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
2553 $footer=pack('N',0);
\r
2554 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2555 $hdr_TotalDataSizeHigh=0;
\r
2557 $hdr = pack("LLLLLLLLLLLL",
\r
2561 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2568 "MIME-Version: 1.0\r\n".
\r
2569 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2570 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2571 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2573 $this->SB_writedata($message);
\r
2574 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
2579 //if ($hdr_Flag == 2) {
\r
2580 // just send ACK...
\r
2581 // $this->SB_writeln("ACK $id");
\r
2584 if ($hdr_SessionID == 4) {
\r
2586 $this->debug_message("*** p2p: ignore flag 4");
\r
2589 $finished = false;
\r
2590 if ($hdr_TotalDataSizeHigh == 0) {
\r
2591 // only 32 bites size
\r
2592 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
2596 // we won't accept any file transfer
\r
2597 // so I think we won't get any message size need to use 64 bits
\r
2598 // 64 bits size here, can't count directly...
\r
2599 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
2600 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
2601 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
2602 $now_size = bcadd($dataoffset, $messagelength);
\r
2603 if (bccomp($now_size, $totalsize) >= 0)
\r
2607 // ignore not finished split packet
\r
2608 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
2611 //$new_id = ~$hdr_Identifier;
\r
2614 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2617 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2622 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2623 $footer = pack("L", 0);
\r
2624 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2625 $len = strlen($message);
\r
2626 $this->SB_writeln("MSG $id D $len");
\r
2628 $this->SB_writedata($message);
\r
2629 $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
2630 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2634 $this->log_message("*** MSG from $from_email: $sMsg");
\r
2635 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2638 $this->log_message("*** User $user is offline. Try OIM.");
\r
2639 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2640 $this->SendMessage($Message,"$user@Offline");
\r
2644 if (is_numeric($code))
\r
2646 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2647 $this->debug_message("*** SB: $this->error");
\r
2652 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2654 if (feof($this->SBFp))
\r
2656 // lost connection? error? try OIM later
\r
2657 @fclose($this->SBFp);
\r
2660 $this->SB_writeln("OUT");
\r
2661 @fclose($this->SBFp);
\r
2664 private function switchboard_control($ip, $port, $cki_code, $user, $Messages)
\r
2666 $this->SwitchBoardProcess=1;
\r
2667 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2668 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2671 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2674 return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));
\r
2676 private function switchboard_ring($ip, $port, $sid, $ticket,$user)
\r
2678 $this->SwitchBoardProcess=2;
\r
2679 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2680 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2683 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2686 return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));
\r
2689 private function sendOIM($to, $sMessage, $lockkey)
\r
2691 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2692 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2693 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2694 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
2696 <From memberName="'.$this->user.'"
\r
2697 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
2700 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
2701 msnpVer="'.$this->protocol.'"
\r
2702 buildVer="'.$this->buildver.'"/>
\r
2703 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2704 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
2705 appid="'.$this->prod_id.'"
\r
2706 lockkey="'.$lockkey.'"
\r
2707 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2708 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
2709 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
2710 <MessageNumber>1</MessageNumber>
\r
2714 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
2715 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
2716 Content-Type: text/plain; charset=UTF-8
\r
2717 Content-Transfer-Encoding: base64
\r
2718 X-OIM-Message-Type: OfflineMessage
\r
2719 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
2720 X-OIM-Sequence-Num: 1
\r
2722 '.chunk_split(base64_encode($sMessage)).'
\r
2725 </soap:Envelope>';
\r
2727 $header_array = array(
\r
2728 'SOAPAction: '.$this->oim_send_soap,
\r
2729 'Content-Type: text/xml',
\r
2730 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
2733 $this->debug_message("*** URL: $this->oim_send_url");
\r
2734 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2735 $curl = curl_init();
\r
2736 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
2737 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2738 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2739 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2740 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2741 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2742 curl_setopt($curl, CURLOPT_POST, 1);
\r
2743 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2744 $data = curl_exec($curl);
\r
2745 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2746 curl_close($curl);
\r
2747 $this->debug_message("*** Get Result:\n$data");
\r
2749 if ($http_code == 200) {
\r
2750 $this->debug_message("*** OIM sent for $to");
\r
2754 $challenge = false;
\r
2755 $auth_policy = false;
\r
2756 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
2757 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
2758 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
2759 if (count($matches) != 0) {
\r
2760 // yes, we get new LockKeyChallenge
\r
2761 $challenge = $matches[2];
\r
2762 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
2764 // auth policy error
\r
2765 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
2766 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
2767 if (count($matches) != 0) {
\r
2768 $auth_policy = $matches[2];
\r
2769 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
2771 if ($auth_policy === false && $challenge === false) {
\r
2772 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
2773 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
2774 if (count($matches) == 0) {
\r
2775 // no error, we assume the OIM is sent
\r
2776 $this->debug_message("*** OIM sent for $to");
\r
2779 $err_code = $matches[2];
\r
2780 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
2781 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
2782 if (count($matches) > 0)
\r
2783 $err_msg = $matches[1];
\r
2786 $this->debug_message("*** OIM failed for $to");
\r
2787 $this->debug_message("*** OIM Error code: $err_code");
\r
2788 $this->debug_message("*** OIM Error Message: $err_msg");
\r
2791 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
2794 // read data for specified size
\r
2795 private function ns_readdata($size) {
\r
2798 while (!feof($this->NSfp)) {
\r
2799 $buf = @fread($this->NSfp, $size - $count);
\r
2801 $count += strlen($buf);
\r
2802 if ($count >= $size) break;
\r
2804 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2809 private function ns_readln() {
\r
2810 $data = @fgets($this->NSfp, 4096);
\r
2811 if ($data !== false) {
\r
2812 $data = trim($data);
\r
2813 $this->debug_message("NS: <<< $data");
\r
2818 // write to server, append \r\n, also increase id
\r
2819 private function ns_writeln($data) {
\r
2820 @fwrite($this->NSfp, $data."\r\n");
\r
2821 $this->debug_message("NS: >>> $data");
\r
2826 // write data to server
\r
2827 private function ns_writedata($data) {
\r
2828 @fwrite($this->NSfp, $data);
\r
2829 $this->debug_message("NS: >>> $data");
\r
2833 // read data for specified size for SB
\r
2834 private function sb_readdata($size) {
\r
2837 while (!feof($this->SBFp)) {
\r
2838 $buf = @fread($this->SBFp, $size - $count);
\r
2840 $count += strlen($buf);
\r
2841 if ($count >= $size) break;
\r
2843 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2847 // read one line for SB
\r
2848 private function sb_readln() {
\r
2849 $data = @fgets($this->SBFp, 4096);
\r
2850 if ($data !== false) {
\r
2851 $data = trim($data);
\r
2852 $this->debug_message("SB: <<< $data");
\r
2857 // write to server for SB, append \r\n, also increase id
\r
2858 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
2859 private function sb_writeln($data) {
\r
2860 @fwrite($this->SBFp, $data."\r\n");
\r
2861 $this->debug_message("SB: >>> $data");
\r
2866 // write data to server
\r
2867 private function sb_writedata($data) {
\r
2868 @fwrite($this->SBFp, $data);
\r
2869 $this->debug_message("SB: >>> $data");
\r
2873 // show debug information
\r
2874 function debug_message($str) {
\r
2875 if (!$this->debug) return;
\r
2876 if($this->debug===STDOUT) echo $str."\n";
\r
2877 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
2878 $fp = fopen($fname, 'at');
\r
2880 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2884 // still show debug information, if we can't open log_file
\r
2889 function dump_binary($str) {
\r
2893 $len = strlen($str);
\r
2894 for ($i = 0; $i < $len; $i++) {
\r
2895 if (($i % 16) == 0) {
\r
2896 if ($buf !== '') {
\r
2897 $buf .= "$h_str $a_str\n";
\r
2899 $buf .= sprintf("%04X:", $i);
\r
2903 $ch = ord($str[$i]);
\r
2907 $a_str .= chr($ch);
\r
2908 $h_str .= sprintf(" %02X", $ch);
\r
2910 if ($h_str !== '')
\r
2911 $buf .= "$h_str $a_str\n";
\r
2916 function log_message($str) {
\r
2917 /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';
\r
2918 $fp = fopen($fname, 'at');
\r
2920 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2923 $this->debug_message($str);
\r
2928 * @param $FilePath 圖檔路徑
\r
2929 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
2932 private function MsnObj($FilePath,$Type=3)
\r
2934 if(!($FileSize=filesize($FilePath))) return '';
\r
2935 $Location=md5($FilePath);
\r
2936 $Friendly=md5($FilePath.$Type);
\r
2937 if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
2938 $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));
\r
2939 $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
2940 $this->MsnObjArray[$Location]=$FilePath;
\r
2941 $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
2942 $this->MsnObjMap[$Location]=$MsnObj;
\r
2943 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
2946 private function linetoArray($lines) {
\r
2947 $lines=str_replace("\r",'',$lines);
\r
2948 $lines=explode("\n",$lines);
\r
2949 foreach($lines as $line) {
\r
2950 if(!isset($line{3})) continue;
\r
2951 list($Key,$Val)=explode(':',$line);
\r
2952 $Data[trim($Key)]=trim($Val);
\r
2956 private function GetPictureFilePath($Context)
\r
2958 $MsnObj=base64_decode($Context);
\r
2959 if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))
\r
2960 $location=$Match[1];
\r
2961 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
2962 if($location&&(isset($this->MsnObjArray[$location])))
\r
2963 return $this->MsnObjArray[$location];
\r
2966 private function GetMsnObjDefine($Message)
\r
2969 if(is_array($this->Emotions))
\r
2970 foreach($this->Emotions as $Pattern => $FilePath)
\r
2972 if(strpos($Message,$Pattern)!==false)
\r
2973 $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";
\r
2975 return $DefineString;
\r
2978 * Receive Message Overload Function
\r
2981 * @param $Network 1 => msn , 32 =>yahoo
\r
2983 * @return unknown_type
\r
2985 protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}
\r
2987 * Remove Us From Member List Overload Function
\r
2990 * @param $Network 1 => msn , 32 =>yahoo
\r
2991 * @return unknown_type
\r
2993 protected function RemoveUsFromMemberList($User,$Network){}
\r
2995 * Add Us to Member List Overload Function
\r
2998 * @param $Network 1 => msn , 32 =>yahoo
\r
2999 * @return unknown_type
\r
3001 protected function AddUsToMemberList($User,$Network){}
\r
3003 public function signon() {
\r
3004 $this->log_message("*** try to connect to MSN network");
\r
3005 while(!$this->connect($this->user, $this->password))
\r
3007 $this->log_message("!!! Can't connect to server: $this->error");
\r
3008 $this->callHandler('ConnectFailed', NULL);
\r
3009 $this->NSRetryWait($this->retry_wait);
\r
3011 $this->UpdateContacts();
\r
3012 $this->LastPing=time();
\r
3013 $this->log_message("*** connected, wait for command");
\r
3014 $start_tm = time();
\r
3015 $ping_tm = time();
\r
3016 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
3017 $this->aContactList = $this->getMembershipList();
\r
3018 if ($this->update_pending) {
\r
3019 if (is_array($this->aContactList)) {
\r
3020 $pending = 'Pending';
\r
3021 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3022 foreach ($aUserList as $u_name => $aNetworks) {
\r
3023 foreach ($aNetworks as $network => $aData) {
\r
3024 if (isset($aData[$pending])) {
\r
3027 foreach (array('Allow', 'Reverse') as $list) {
\r
3028 if (isset($aData[$list]))
\r
3031 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
3032 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3038 $id = $aData[$pending];
\r
3039 // we can delete it from pending now
\r
3040 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
3041 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
3046 foreach (array('Allow', 'Reverse') as $list) {
\r
3047 if (!isset($aData[$list])) {
\r
3048 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3049 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3061 if (is_array($this->aContactList)) {
\r
3062 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3063 $str = '<d n="'.$u_domain.'">';
\r
3064 $len += strlen($str);
\r
3065 if ($len > 7400) {
\r
3066 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3069 $len = strlen($str);
\r
3072 foreach ($aUserList as $u_name => $aNetworks) {
\r
3073 foreach ($aNetworks as $network => $status) {
\r
3074 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
3075 $len += strlen($str);
\r
3076 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
3078 if ($len > 7475) {
\r
3080 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3082 $sList = '<d n="'.$u_domain.'">'.$str;
\r
3083 $len = strlen($sList);
\r
3092 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3093 // NS: >>> BLP {id} BL
\r
3094 $this->ns_writeln("BLP $this->id BL");
\r
3095 foreach ($this->aADL as $str) {
\r
3096 $len = strlen($str);
\r
3097 // NS: >>> ADL {id} {size}
\r
3098 $this->ns_writeln("ADL $this->id $len");
\r
3099 $this->ns_writedata($str);
\r
3101 // NS: >>> PRP {id} MFN name
\r
3102 if ($this->alias == '') $this->alias = $user;
\r
3103 $aliasname = rawurlencode($this->alias);
\r
3104 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
3106 //$MsnObj=$this->PhotoStckObj();
\r
3107 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
3108 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3109 if($this->PhotoStickerFile!==false)
\r
3110 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3111 // NS: >>> UUX {id} length
\r
3112 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
3113 $len = strlen($str);
\r
3114 $this->ns_writeln("UUX $this->id $len");
\r
3115 $this->ns_writedata($str);
\r
3118 public function NSreceive() {
\r
3119 $this->log_message("*** startup ***");
\r
3121 // Sign in again if not signed in or socket failed
\r
3122 if (!is_resource($this->NSfp) || feof($this->NSfp)) {
\r
3123 $this->callHandler('Reconnect', NULL);
\r
3127 $data = $this->ns_readln();
\r
3128 if($data === false) {
\r
3129 // There was no data / an error when reading from the socket so reconnect
\r
3130 $this->callHandler('Reconnect', NULL);
\r
3133 switch (substr($data,0,3))
\r
3136 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
3137 // NS: <<< SBS 0 null
\r
3142 // NS: <<< RFS ???
\r
3143 // refresh ADL, so we re-send it again
\r
3144 if (is_array($this->aADL)) {
\r
3145 foreach ($this->aADL as $str) {
\r
3146 $len = strlen($str);
\r
3147 // NS: >>> ADL {id} {size}
\r
3148 $this->ns_writeln("ADL $this->id $len");
\r
3149 $this->ns_writedata($str);
\r
3155 // NS: <<< LST {email} {alias} 11 0
\r
3156 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
3157 @list($u_name, $u_domain) = @explode('@', $email);
\r
3158 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
3159 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
3160 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
3165 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
3166 // NS: <<< ADL 0 {size}
\r
3167 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
3168 if (is_numeric($size) && $size > 0)
\r
3170 $data = $this->ns_readdata($size);
\r
3171 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3172 if (is_array($matches) && count($matches) > 0)
\r
3174 $u_domain = $matches[1];
\r
3175 $u_name = $matches[2];
\r
3176 $network = $matches[4];
\r
3177 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3178 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
3181 $re_login = false;
\r
3183 foreach (array('Allow', 'Reverse') as $list)
\r
3185 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3188 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3191 $aTickets = $this->get_passport_ticket();
\r
3192 if (!$aTickets || !is_array($aTickets)) {
\r
3193 // failed to login? ignore it
\r
3194 $this->log_message("*** can't re-login, something wrong here");
\r
3195 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3199 $this->ticket = $aTickets;
\r
3200 $this->log_message("**** get new ticket, try it again");
\r
3201 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3203 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3207 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3210 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
3212 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
3213 $len = strlen($str);
\r
3216 $this->log_message("*** someone add us to their list: $data");
\r
3217 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
3222 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
3223 // NS: <<< RML 0 {size}
\r
3224 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
3225 if (is_numeric($size) && $size > 0)
\r
3227 $data = $this->ns_readdata($size);
\r
3228 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3229 if (is_array($matches) && count($matches) > 0)
\r
3231 $u_domain = $matches[1];
\r
3232 $u_name = $matches[2];
\r
3233 $network = $matches[4];
\r
3234 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3236 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
3237 foreach ($aData as $list => $id)
\r
3238 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
3239 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
3240 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
3243 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
3244 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
3247 $this->log_message("*** someone remove us from their list: $data");
\r
3252 // randomly, we get MSG notification from server
\r
3253 // NS: <<< MSG Hotmail Hotmail {size}
\r
3254 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
3255 if (is_numeric($size) && $size > 0) {
\r
3256 $data = $this->ns_readdata($size);
\r
3257 $aLines = @explode("\n", $data);
\r
3261 foreach ($aLines as $line) {
\r
3262 $line = rtrim($line);
\r
3264 if ($line === '') {
\r
3268 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
3269 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
3270 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
3271 // we just need text/x-msmsgsinitialmdatanotification
\r
3272 // or text/x-msmsgsoimnotification
\r
3279 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
3280 $maildata = trim(substr($line, 10));
\r
3285 $this->log_message("*** ingnore MSG for: $line");
\r
3288 if ($maildata == '') {
\r
3289 $this->log_message("*** ingnore MSG not for OIM");
\r
3292 $re_login = false;
\r
3293 if (strcasecmp($maildata, 'too-large') == 0) {
\r
3294 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
3295 $maildata = $this->getOIM_maildata();
\r
3296 if ($maildata === false) {
\r
3297 $this->log_message("*** can't get mail-data via SOAP");
\r
3298 // maybe we need to re-login again
\r
3299 $aTickets = $this->get_passport_ticket();
\r
3300 if (!$aTickets || !is_array($aTickets)) {
\r
3301 // failed to login? ignore it
\r
3302 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3306 $this->ticket = $aTickets;
\r
3307 $this->log_message("**** get new ticket, try it again");
\r
3308 $maildata = $this->getOIM_maildata();
\r
3309 if ($maildata === false) {
\r
3310 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
3315 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
3319 $start = strpos($p, '<M>');
\r
3320 $end = strpos($p, '</M>');
\r
3321 if ($start === false || $end === false || $start > $end) break;
\r
3323 $sOIM = substr($p, $start, $end - $start);
\r
3325 $p = substr($p, $end);
\r
3327 if (count($aOIMs) == 0) {
\r
3328 $this->log_message("*** ingnore empty OIM");
\r
3331 foreach ($aOIMs as $maildata) {
\r
3332 // T: 11 for MSN, 13 for Yahoo
\r
3333 // S: 6 for MSN, 7 for Yahoo
\r
3334 // RT: the datetime received by server
\r
3335 // RS: already read or not
\r
3336 // SZ: size of message
\r
3339 // F: always 00000000-0000-0000-0000-000000000009
\r
3340 // N: sender alias
\r
3341 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
3342 if (count($matches) == 0) {
\r
3343 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
3346 $oim_type = $matches[1];
\r
3347 if ($oim_type = 13)
\r
3351 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
3352 if (count($matches) == 0) {
\r
3353 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
3356 $oim_sender = $matches[1];
\r
3357 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
3358 if (count($matches) == 0) {
\r
3359 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
3362 $oim_msgid = $matches[1];
\r
3363 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
3364 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
3365 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
3366 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
3367 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
3368 $sMsg = $this->getOIM_message($oim_msgid);
\r
3369 if ($sMsg === false) {
\r
3370 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
3372 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3375 $aTickets = $this->get_passport_ticket();
\r
3376 if (!$aTickets || !is_array($aTickets)) {
\r
3377 // failed to login? ignore it
\r
3378 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3382 $this->ticket = $aTickets;
\r
3383 $this->log_message("**** get new ticket, try it again");
\r
3384 $sMsg = $this->getOIM_message($oim_msgid);
\r
3385 if ($sMsg === false) {
\r
3386 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3390 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
3392 //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
3393 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
3399 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
3400 // NS: <<< UBM {email} $network $type {size}
\r
3401 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
3402 if (is_numeric($size) && $size > 0)
\r
3404 $data = $this->ns_readdata($size);
\r
3405 $aLines = @explode("\n", $data);
\r
3409 foreach ($aLines as $line) {
\r
3410 $line = rtrim($line);
\r
3412 if ($line === '') {
\r
3416 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
3422 $aSubLines = @explode("\r", $line);
\r
3423 foreach ($aSubLines as $str) {
\r
3431 $this->log_message("*** ingnore from $from_email: $line");
\r
3434 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
3435 //$this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
3436 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
3441 // randomly, we get UBX notification from server
\r
3442 // NS: <<< UBX email {network} {size}
\r
3443 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
3444 // we don't need the notification data, so just ignore it
\r
3445 if (is_numeric($size) && $size > 0)
\r
3446 $this->ns_readdata($size);
\r
3450 // randomly, we'll get challenge from server
\r
3451 // NS: <<< CHL 0 {code}
\r
3452 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
3453 $fingerprint = $this->getChallenge($chl_code);
\r
3454 // NS: >>> QRY {id} {product_id} 32
\r
3455 // NS: >>> fingerprint
\r
3456 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
3457 $this->ns_writedata($fingerprint);
\r
3458 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3459 if($this->PhotoStickerFile!==false)
\r
3460 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3463 // NS: <<< CHG {id} {status} {code}
\r
3465 // change our status to online first
\r
3469 // sometimes, NS will redirect to another NS
\r
3471 // NS: <<< XFR {id} NS {server} 0 {server}
\r
3473 // NS: <<< XFR {id} NS {server} U D
\r
3474 // for normal switchboard XFR
\r
3475 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
3476 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
3477 @list($ip, $port) = @explode(':', $server);
\r
3478 if ($server_type != 'SB') {
\r
3480 // this connection will close after XFR
\r
3481 $this->NSLogout();
\r
3484 if(count($this->MessageQueue))
\r
3486 foreach($this->MessageQueue as $User => $Message)
\r
3488 //$this->ChildProcess[$ChildPid]
\r
3489 $this->log_message("*** XFR SB $User");
\r
3490 $pid=pcntl_fork();
\r
3494 $this->ChildProcess[$pid]=$User;
\r
3499 $this->log_message("*** Fork Error $User");
\r
3505 $this->log_message("*** Child Process Start for $User");
\r
3506 unset($Message['XFRSent']);
\r
3507 unset($Message['ReqTime']);
\r
3508 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
3509 if ($bSBresult === false)
\r
3511 // error for switchboard
\r
3512 $this->log_message("!!! error for sending message to ".$User);
\r
3517 unset($this->MessageQueue[$User]);
\r
3520 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
3521 if ($bSBresult === false) {
\r
3522 // error for switchboard
\r
3523 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
3524 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
3528 // NS: <<< QNG {time}
\r
3529 @list(/* QNG */, $ping_wait) = @explode(' ', $data);
\r
3530 //if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
3531 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
3532 //Mod by Ricky Set Online
\r
3534 $this->callHandler('Pong', $ping_wait);
\r
3538 if($this->PhotoStickerFile!==false)
\r
3539 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3541 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3542 // someone is trying to talk to us
\r
3543 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
3544 $this->log_message("NS: <<< RNG $data");
\r
3545 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
3546 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
3547 if($this->IsIgnoreMail($email))
\r
3549 $this->log_message("*** Ignore RNG from $email");
\r
3552 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
3553 $this->addContact($email,1,$email, true);
\r
3554 $pid=pcntl_fork();
\r
3558 $this->ChildProcess[$pid]='RNG';
\r
3563 $this->log_message("*** Fork Error $User");
\r
3569 $this->log_message("*** Ring Child Process Start for $User");
\r
3570 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
3575 // force logout from NS
\r
3576 // NS: <<< OUT xxx
\r
3577 $this->log_message("*** LOGOUT from NS");
\r
3578 return $this->NsLogout();
\r
3581 $code = substr($data,0,3);
\r
3582 if (is_numeric($code)) {
\r
3583 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
3584 $this->debug_message("*** NS: $this->error");
\r
3586 return $this->NsLogout();
\r
3593 public function sendMessageViaSB($message, $to) {
\r
3594 $socket = $this->switchBoardSessions[$to]['socket'];
\r
3595 $lastActive = $this->switchBoardSessions[$to]['lastActive'];
\r
3596 $joined = $this->switchBoardSessions[$to]['joined'];
\r
3598 //FIXME Probably not needed (we're not running in a loop anymore)
\r
3599 /*if($this->kill_me)
\r
3601 $this->log_message("*** SB Okay, kill me now!");
\r
3602 endSBSession($socket);
\r
3606 // If our participant has not joined the session yet we can't message them!
\r
3607 //TODO Check the behaviour of the queue runner when we return false
\r
3611 $aMessage = $this->getMessage($Message);
\r
3613 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
3614 if($MsnObjDefine !== '')
\r
3616 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
3617 $len = strlen($SendString);
\r
3618 $this->SB_writeln("MSG $id N $len");
\r
3620 $this->SB_writedata($SendString);
\r
3623 $len = strlen($aMessage);
\r
3624 $this->SB_writeln("MSG $id N $len");
\r
3626 // Increment the trID
\r
3627 $this->switchBoardSessions[$to]['id']++;
\r
3629 $this->SB_writedata($aMessage);
\r
3631 // Don't close the SB session, we might as well leave it open
\r
3636 //FIXME Not sure if this is needed?
\r
3637 private function endSBSession($socket) {
\r
3638 if (feof($this->SBFp))
\r
3640 // lost connection? error? try OIM later
\r
3641 @fclose($this->SBFp);
\r
3644 $this->SB_writeln("OUT");
\r
3645 @fclose($this->SBFp);
\r
3649 private function getSBSession($to) {
\r
3653 public function sendMessage($message, $to) {
\r
3654 if($message != '') {
\r
3655 list($name,$host,$network)=explode('@',$to);
\r
3656 $network=$network==''?1:$network;
\r
3658 if($network === 1 && isset($this->switchBoardSessions[$to])) {
\r
3659 $recipient = $name . $host;
\r
3660 $this->debug_message("*** Sending Message to $recipient using existing SB session");
\r
3661 return $this->sendMessageViaSB($message, $recipient);
\r
3663 $this->debug_message("*** Not MSN network or no existing SB session");
\r
3664 //TODO implement creation of SB session etc
\r
3671 * Sends a ping command
\r
3673 * Should be called about every 50 seconds
\r
3675 public function send_ping() {
\r
3677 $this->ns_writeln("PNG");
\r
3680 public function getNSSocket() {
\r
3681 return $this->NSfp;
\r
3684 // TODO Allow for multiple SB session sockets
\r
3685 public function getSBSocket() {
\r
3686 return $this->SBfp;
\r
3689 public function getSockets() {
\r
3690 return array($this->NSfp, $this->SBfp);
\r
3694 * Calls User Handler
\r
3696 * Calls registered handler for a specific event.
\r
3698 * @param String $event Command (event) name (Rvous etc)
\r
3699 * @param String $data Raw message from server
\r
3700 * @see registerHandler
\r
3703 private function callHandler($event, $data) {
\r
3704 if (isset($this->myEventHandlers[$event])) {
\r
3705 call_user_func($this->myEventHandlers[$event], $data);
\r
3710 * Registers a user handler
\r
3713 * IMIn, Pong, ConnectFailed, Reconnect
\r
3715 * @param String $event Event name
\r
3716 * @param String $handler User function to call
\r
3717 * @see callHandler
\r
3718 * @return boolean Returns true if successful
\r
3720 public function registerHandler($event, $handler) {
\r
3721 if (is_callable($handler)) {
\r
3722 $this->myEventHandlers[$event] = $handler;
\r