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 $switchBoardSessions = array();
\r
118 * Event Handler Functions
\r
120 private $myEventHandlers = array();
\r
122 // End added for StatusNet
\r
124 private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)
\r
127 foreach($Array as $Key => $Val)
\r
129 if($Key{0}==':') continue;
\r
131 if(is_array($Val[':']))
\r
133 foreach($Val[':'] as $AttribName => $AttribVal)
\r
134 $Attrib.=" $AttribName='$AttribVal'";
\r
139 $Key=substr($Key,1);
\r
140 foreach($Val as $ListKey => $ListVal)
\r
142 if($ListKey{0}==':') continue;
\r
143 if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);
\r
144 elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';
\r
145 $ArrayString.="<$Key$Attrib>$ListVal</$Key>";
\r
149 if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);
\r
150 elseif(is_bool($Val)) $Val=$Val?'true':'false';
\r
151 $ArrayString.="<$Key$Attrib>$Val</$Key>";
\r
153 if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);
\r
154 return $ArrayString;
\r
157 public function End()
\r
159 $this->log_message("*** someone kill me ***");
\r
160 $this->kill_me=true;
\r
162 private function IsIgnoreMail($Email)
\r
164 if($this->IgnoreList==false) return false;
\r
165 foreach($this->IgnoreList as $Pattern)
\r
167 if(preg_match($Pattern,$Email)) return true;
\r
171 public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)
\r
173 $this->user = $Configs['user'];
\r
174 $this->password = $Configs['password'];
\r
175 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
176 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
177 $my_add_function = isset($Configs['add_user_function']) ? $Configs['add_user_function'] : false;
\r
178 $my_rem_function = isset($Configs['remove_user_function']) ? $Configs['remove_user_function'] : false;
\r
179 $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;
\r
180 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
181 $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;
\r
182 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
183 $this->PhotoStickerFile=$Configs['PhotoSticker'];
\r
184 $this->IgnoreList=isset($Configs['IgnoreList'])?$Configs['IgnoreList']:false;
\r
185 if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)
\r
187 foreach($this->Emotions as $EmotionFilePath)
\r
188 $this->MsnObj($EmotionFilePath,$Type=2);
\r
190 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
191 $this->timeout = $timeout;
\r
193 if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");
\r
194 if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");
\r
195 if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");
\r
197 if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");
\r
198 if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");
\r
201 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
203 normal MSN 8.1 clientid is:
\r
204 01110110 01001100 11000000 00101100
\r
207 we just use following:
\r
208 * 0x04: Your client can send/receive Ink (GIF format)
\r
209 * 0x08: Your client can send/recieve Ink (ISF format)
\r
210 * 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
211 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
214 $this->clientid = $client_id;
\r
215 $this->windows =(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
\r
216 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
219 private function get_passport_ticket($url = '')
\r
221 $user = $this->user;
\r
222 $password = htmlspecialchars($this->password);
\r
225 $passport_url = $this->passport_url;
\r
227 $passport_url = $url;
\r
229 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
230 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
231 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
232 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
233 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
234 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
235 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
236 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
237 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
239 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
240 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
241 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
242 <ps:UIVersion>1</ps:UIVersion>
\r
243 <ps:Cookies></ps:Cookies>
\r
244 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
247 <wsse:UsernameToken Id="user">
\r
248 <wsse:Username>'.$user.'</wsse:Username>
\r
249 <wsse:Password>'.$password.'</wsse:Password>
\r
250 </wsse:UsernameToken>
\r
254 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
255 <wst:RequestSecurityToken Id="RST0">
\r
256 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
258 <wsa:EndpointReference>
\r
259 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
260 </wsa:EndpointReference>
\r
262 </wst:RequestSecurityToken>
\r
263 <wst:RequestSecurityToken Id="RST1">
\r
264 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
266 <wsa:EndpointReference>
\r
267 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
268 </wsa:EndpointReference>
\r
270 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
271 </wst:RequestSecurityToken>
\r
272 <wst:RequestSecurityToken Id="RST2">
\r
273 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
275 <wsa:EndpointReference>
\r
276 <wsa:Address>messenger.msn.com</wsa:Address>
\r
277 </wsa:EndpointReference>
\r
279 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
280 </wst:RequestSecurityToken>
\r
281 <wst:RequestSecurityToken Id="RST3">
\r
282 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
284 <wsa:EndpointReference>
\r
285 <wsa:Address>contacts.msn.com</wsa:Address>
\r
286 </wsa:EndpointReference>
\r
288 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
289 </wst:RequestSecurityToken>
\r
290 <wst:RequestSecurityToken Id="RST4">
\r
291 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
293 <wsa:EndpointReference>
\r
294 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
295 </wsa:EndpointReference>
\r
297 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
298 </wst:RequestSecurityToken>
\r
299 <wst:RequestSecurityToken Id="RST5">
\r
300 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
302 <wsa:EndpointReference>
\r
303 <wsa:Address>spaces.live.com</wsa:Address>
\r
304 </wsa:EndpointReference>
\r
306 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
307 </wst:RequestSecurityToken>
\r
308 <wst:RequestSecurityToken Id="RST6">
\r
309 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
311 <wsa:EndpointReference>
\r
312 <wsa:Address>storage.msn.com</wsa:Address>
\r
313 </wsa:EndpointReference>
\r
315 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
316 </wst:RequestSecurityToken>
\r
317 </ps:RequestMultipleSecurityTokens>
\r
321 $this->debug_message("*** URL: $passport_url");
\r
322 $this->debug_message("*** Sending SOAP:\n$XML");
\r
323 $curl = curl_init();
\r
324 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
325 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
326 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
327 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
328 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
329 curl_setopt($curl, CURLOPT_POST, 1);
\r
330 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
331 $data = curl_exec($curl);
\r
332 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
334 $this->debug_message("*** Get Result:\n$data");
\r
336 if ($http_code != 200) {
\r
337 // sometimes, rediret to another URL
\r
339 //<faultcode>psf:Redirect</faultcode>
\r
340 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
341 //<faultstring>Authentication Failure</faultstring>
\r
342 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
343 $this->debug_message("*** Can't get passport ticket! http code = $http_code");
\r
346 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
347 if (count($matches) == 0) {
\r
348 $this->debug_message("*** redirect, but can't get redirect URL!");
\r
351 $redirect_url = $matches[1];
\r
352 if ($redirect_url == $passport_url) {
\r
353 $this->debug_message("*** redirect, but redirect to same URL!");
\r
356 $this->debug_message("*** redirect to $redirect_url");
\r
357 return $this->get_passport_ticket($redirect_url);
\r
360 // sometimes, rediret to another URL, also return 200
\r
362 //<faultcode>psf:Redirect</faultcode>
\r
363 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
364 //<faultstring>Authentication Failure</faultstring>
\r
365 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
366 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
367 if (count($matches) != 0) {
\r
368 $redirect_url = $matches[1];
\r
369 if ($redirect_url == $passport_url) {
\r
370 $this->debug_message("*** redirect, but redirect to same URL!");
\r
373 $this->debug_message("*** redirect to $redirect_url");
\r
374 return $this->get_passport_ticket($redirect_url);
\r
378 // no Redurect faultcode or URL
\r
379 // we should get the ticket here
\r
381 // we need ticket and secret code
\r
382 // RST1: messengerclear.live.com
\r
383 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
384 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
385 // RST2: messenger.msn.com
\r
386 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
387 // RST3: contacts.msn.com
\r
388 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
389 // RST4: messengersecure.live.com
\r
390 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
391 // RST5: spaces.live.com
\r
392 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
393 // RST6: storage.msn.com
\r
394 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
396 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
397 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
398 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
399 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
400 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
401 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
402 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
406 // no ticket found!
\r
407 if (count($matches) == 0) {
\r
408 $this->debug_message("*** Can't get passport ticket!");
\r
412 //$this->debug_message(var_export($matches, true));
\r
413 // matches[0]: all data
\r
414 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
416 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
418 // matches[5]: RST2 (messenger.msn.com) ticket
\r
420 // matches[7]: RST3 (contacts.msn.com) ticket
\r
422 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
423 // matches[10]: ...
\r
424 // matches[11]: RST5 (spaces.live.com) ticket
\r
425 // matches[12]: ...
\r
426 // matches[13]: RST6 (storage.live.com) ticket
\r
427 // matches[14]: ...
\r
430 // ticket => $matches[1]
\r
431 // secret => $matches[3]
\r
432 // web_ticket => $matches[5]
\r
433 // contact_ticket => $matches[7]
\r
434 // oim_ticket => $matches[9]
\r
435 // space_ticket => $matches[11]
\r
436 // storage_ticket => $matches[13]
\r
438 // yes, we get ticket
\r
440 'ticket' => html_entity_decode($matches[1]),
\r
441 'secret' => html_entity_decode($matches[3]),
\r
442 'web_ticket' => html_entity_decode($matches[5]),
\r
443 'contact_ticket' => html_entity_decode($matches[7]),
\r
444 'oim_ticket' => html_entity_decode($matches[9]),
\r
445 'space_ticket' => html_entity_decode($matches[11]),
\r
446 'storage_ticket' => html_entity_decode($matches[13])
\r
448 $this->ticket=$aTickets;
\r
449 $this->debug_message(var_export($aTickets, true));
\r
450 $ABAuthHeaderArray=array(
\r
451 'ABAuthHeader'=>array(
\r
452 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
453 'ManagedGroupRequest'=>false,
\r
454 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),
\r
457 $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));
\r
458 file_put_contents('/tmp/STTicket.txt',htmlspecialchars($this->ticket['storage_ticket']));
\r
459 //$this->debug_message("StorageTicket:\n",htmlspecialchars($this->ticket['storage_ticket']));
\r
462 private function UpdateContacts()
\r
464 $ABApplicationHeaderArray=array(
\r
465 'ABApplicationHeader'=>array(
\r
466 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
467 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
468 'IsMigration'=>false,
\r
469 'PartnerScenario'=>'ContactSave'
\r
472 $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
473 $ABFindAllArray=array(
\r
474 'ABFindAll'=>array(
\r
475 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
476 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
478 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',
\r
481 $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');
\r
482 $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));
\r
483 $this->Contacts=array();
\r
486 $this->debug_message("*** Update Contacts...");
\r
487 $Result=$this->ABService->ABFindAll($ABFindAll);
\r
488 $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());
\r
489 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
490 $this->Contacts[$Contact->contactInfo->passportName]=$Contact;
\r
492 catch(Exception $e)
\r
494 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
497 protected function addContact($email, $network, $display = '', $sendADL = false)
\r
499 if ($network != 1) return true;
\r
500 if(isset($this->Contacts[$email])) return true;
\r
502 $ABContactAddArray=array(
\r
503 'ABContactAdd'=>array(
\r
504 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
505 'abId'=>'00000000-0000-0000-0000-000000000000',
\r
508 ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
509 'contactInfo'=>array(
\r
510 'contactType'=>'LivePending',
\r
511 'passportName'=>$email,
\r
512 'isMessengerUser'=>true,
\r
513 'MessengerMemberInfo'=>array(
\r
514 'DisplayName'=>$email
\r
520 'EnableAllowListManagement'=>true
\r
524 $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');
\r
527 $this->debug_message("*** Add Contacts $email...");
\r
528 $this->ABService->ABContactAdd($ABContactAdd);
\r
530 catch(Exception $e)
\r
532 $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
534 if ($sendADL && !feof($this->NSfp)) {
\r
535 @list($u_name, $u_domain) = @explode('@', $email);
\r
536 foreach (array('1', '2') as $l) {
\r
537 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
538 $len = strlen($str);
\r
539 // NS: >>> ADL {id} {size}
\r
540 $this->ns_writeln("ADL $this->id $len");
\r
541 $this->ns_writedata($str);
\r
544 $this->UpdateContacts();
\r
548 function delMemberFromList($memberID, $email, $network, $list) {
\r
549 if ($network != 1 && $network != 32) return true;
\r
550 if ($memberID === false) return true;
\r
552 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
554 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
555 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
556 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
557 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
558 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
560 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
561 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
562 <IsMigration>false</IsMigration>
\r
563 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
564 </ABApplicationHeader>
\r
565 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
566 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
567 <TicketToken>'.$ticket.'</TicketToken>
\r
571 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
574 <Type>Messenger</Type>
\r
575 <ForeignId></ForeignId>
\r
579 <MemberRole>'.$list.'</MemberRole>
\r
581 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
582 <Type>Passport</Type>
\r
583 <MembershipId>'.$memberID.'</MembershipId>
\r
584 <State>Accepted</State>
\r
593 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
594 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
595 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
596 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
597 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
599 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
600 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
601 <IsMigration>false</IsMigration>
\r
602 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
603 </ABApplicationHeader>
\r
604 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
605 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
606 <TicketToken>'.$ticket.'</TicketToken>
\r
610 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
613 <Type>Messenger</Type>
\r
614 <ForeignId></ForeignId>
\r
618 <MemberRole>'.$list.'</MemberRole>
\r
620 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
622 <MembershipId>'.$memberID.'</MembershipId>
\r
623 <State>Accepted</State>
\r
632 $header_array = array(
\r
633 'SOAPAction: '.$this->delmember_soap,
\r
634 'Content-Type: text/xml; charset=utf-8',
\r
635 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
638 $this->debug_message("*** URL: $this->delmember_url");
\r
639 $this->debug_message("*** Sending SOAP:\n$XML");
\r
640 $curl = curl_init();
\r
641 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
642 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
643 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
644 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
645 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
646 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
647 curl_setopt($curl, CURLOPT_POST, 1);
\r
648 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
649 $data = curl_exec($curl);
\r
650 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
652 $this->debug_message("*** Get Result:\n$data");
\r
654 if ($http_code != 200) {
\r
655 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
656 if (count($matches) == 0) {
\r
657 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
\r
660 $faultcode = trim($matches[1]);
\r
661 $faultstring = trim($matches[2]);
\r
662 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
663 $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
\r
666 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
\r
669 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
\r
673 function addMemberToList($email, $network, $list) {
\r
674 if ($network != 1 && $network != 32) return true;
\r
675 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
679 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
680 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
681 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
682 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
683 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
685 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
686 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
687 <IsMigration>false</IsMigration>
\r
688 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
689 </ABApplicationHeader>
\r
690 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
691 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
692 <TicketToken>'.$ticket.'</TicketToken>
\r
696 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
699 <Type>Messenger</Type>
\r
700 <ForeignId></ForeignId>
\r
704 <MemberRole>'.$list.'</MemberRole>
\r
706 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
707 <Type>Passport</Type>
\r
708 <State>Accepted</State>
\r
709 <PassportName>'.$user.'</PassportName>
\r
718 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
719 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
720 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
721 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
722 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
724 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
725 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
726 <IsMigration>false</IsMigration>
\r
727 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
728 </ABApplicationHeader>
\r
729 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
730 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
731 <TicketToken>'.$ticket.'</TicketToken>
\r
735 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
738 <Type>Messenger</Type>
\r
739 <ForeignId></ForeignId>
\r
743 <MemberRole>'.$list.'</MemberRole>
\r
745 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
747 <State>Accepted</State>
\r
748 <Email>'.$user.'</Email>
\r
751 <Name>MSN.IM.BuddyType</Name>
\r
752 <Value>32:YAHOO</Value>
\r
762 $header_array = array(
\r
763 'SOAPAction: '.$this->addmember_soap,
\r
764 'Content-Type: text/xml; charset=utf-8',
\r
765 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
768 $this->debug_message("*** URL: $this->addmember_url");
\r
769 $this->debug_message("*** Sending SOAP:\n$XML");
\r
770 $curl = curl_init();
\r
771 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
772 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
773 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
774 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
775 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
776 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
777 curl_setopt($curl, CURLOPT_POST, 1);
\r
778 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
779 $data = curl_exec($curl);
\r
780 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
782 $this->debug_message("*** Get Result:\n$data");
\r
784 if ($http_code != 200) {
\r
785 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
786 if (count($matches) == 0) {
\r
787 $this->log_message("*** can't add member (network: $network) $email to $list");
\r
790 $faultcode = trim($matches[1]);
\r
791 $faultstring = trim($matches[2]);
\r
792 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
793 $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
\r
796 $this->log_message("*** add member (network: $network) $email to $list, already exist!");
\r
799 $this->log_message("*** add member (network: $network) $email to $list");
\r
803 function getMembershipList($returnData=false) {
\r
804 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
805 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
806 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
807 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
808 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
809 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
811 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
812 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
813 <IsMigration>false</IsMigration>
\r
814 <PartnerScenario>Initial</PartnerScenario>
\r
815 </ABApplicationHeader>
\r
816 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
817 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
818 <TicketToken>'.$ticket.'</TicketToken>
\r
822 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
825 <ServiceType>Messenger</ServiceType>
\r
826 <ServiceType>Invitation</ServiceType>
\r
827 <ServiceType>SocialNetwork</ServiceType>
\r
828 <ServiceType>Space</ServiceType>
\r
829 <ServiceType>Profile</ServiceType>
\r
835 $header_array = array(
\r
836 'SOAPAction: '.$this->membership_soap,
\r
837 'Content-Type: text/xml; charset=utf-8',
\r
838 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
840 $this->debug_message("*** URL: $this->membership_url");
\r
841 $this->debug_message("*** Sending SOAP:\n$XML");
\r
842 $curl = curl_init();
\r
843 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
844 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
845 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
846 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
847 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
848 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
849 curl_setopt($curl, CURLOPT_POST, 1);
\r
850 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
851 $data = curl_exec($curl);
\r
852 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
854 $this->debug_message("*** Get Result:\n$data");
\r
855 if($http_code != 200) return array();
\r
857 $aMemberships = array();
\r
859 //$this->debug_message("search p = $p");
\r
860 $start = strpos($p, '<Membership>');
\r
861 $end = strpos($p, '</Membership>');
\r
862 if ($start === false || $end === false || $start > $end) break;
\r
863 //$this->debug_message("start = $start, end = $end");
\r
865 $sMembership = substr($p, $start, $end - $start);
\r
866 $aMemberships[] = $sMembership;
\r
867 //$this->debug_message("add sMembership = $sMembership");
\r
868 $p = substr($p, $end);
\r
870 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
872 $aContactList = array();
\r
873 foreach ($aMemberships as $sMembership) {
\r
874 //$this->debug_message("sMembership = $sMembership");
\r
875 if (isset($matches)) unset($matches);
\r
876 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
877 if (count($matches) == 0) continue;
\r
878 $sMemberRole = $matches[1];
\r
879 //$this->debug_message("MemberRole = $sMemberRole");
\r
880 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
882 if (isset($aMembers)) unset($aMembers);
\r
883 $aMembers = array();
\r
885 //$this->debug_message("search p = $p");
\r
886 $start = strpos($p, '<Member xsi:type="');
\r
887 $end = strpos($p, '</Member>');
\r
888 if ($start === false || $end === false || $start > $end) break;
\r
889 //$this->debug_message("start = $start, end = $end");
\r
891 $sMember = substr($p, $start, $end - $start);
\r
892 $aMembers[] = $sMember;
\r
893 //$this->debug_message("add sMember = $sMember");
\r
894 $p = substr($p, $end);
\r
896 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
897 foreach ($aMembers as $sMember) {
\r
898 //$this->debug_message("sMember = $sMember");
\r
899 if (isset($matches)) unset($matches);
\r
900 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
901 if (count($matches) == 0) continue;
\r
902 $sMemberType = $matches[1];
\r
903 //$this->debug_message("MemberType = $sMemberType");
\r
905 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
906 if (count($matches) == 0) continue;
\r
908 if ($sMemberType == 'PassportMember') {
\r
909 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
911 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
913 else if ($sMemberType == 'EmailMember') {
\r
914 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
915 // Value is 32: or 32:YAHOO
\r
916 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
917 if (count($matches) == 0) continue;
\r
918 if ($matches[1] != 32) continue;
\r
920 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
922 if ($network == -1) continue;
\r
923 if (count($matches) > 0) {
\r
924 $email = $matches[1];
\r
925 @list($u_name, $u_domain) = @explode('@', $email);
\r
926 if ($u_domain == NULL) continue;
\r
927 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
928 $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
932 return $aContactList;
\r
935 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
937 if ($redirect_server === '') {
\r
938 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
\r
939 if (!$this->NSfp) {
\r
940 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
\r
945 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);
\r
946 if (!$this->NSfp) {
\r
947 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
952 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
953 $this->authed = false;
\r
955 // NS: >> VER {id} MSNP9 CVR0
\r
957 // NS: >>> VER {id} MSNP15 CVR0
\r
958 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
960 $start_tm = time();
\r
961 while (!feof($this->NSfp))
\r
963 $data = $this->ns_readln();
\r
965 if ($data === false) {
\r
966 if ($this->timeout > 0) {
\r
968 $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
\r
969 if ($used_time > $this->timeout) {
\r
972 $this->ns_writeln("OUT");
\r
973 fclose($this->NSfp);
\r
974 $this->error = 'Timeout, maybe protocol changed!';
\r
975 $this->debug_message("*** $this->error");
\r
981 $code = substr($data, 0, 3);
\r
982 $start_tm = time();
\r
987 // NS: <<< VER {id} MSNP9 CVR0
\r
988 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
990 // NS: <<< VER {id} MSNP15 CVR0
\r
991 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
992 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
997 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
998 // NS: >>> USR {id} TWN I {user}
\r
1000 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
1001 // NS: >>> USR {id} SSO I {user}
\r
1002 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
1006 // already login for passport site, finish the login process now.
\r
1007 // NS: <<< USR {id} OK {user} {verify} 0
\r
1008 if ($this->authed) return true;
\r
1009 // max. 16 digits for password
\r
1010 if (strlen($password) > 16)
\r
1011 $password = substr($password, 0, 16);
\r
1013 $this->user = $user;
\r
1014 $this->password = $password;
\r
1015 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1016 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1018 $this->passport_policy = $policy;
\r
1019 $aTickets = $this->get_passport_ticket();
\r
1020 if (!$aTickets || !is_array($aTickets)) {
\r
1023 $this->ns_writeln("OUT");
\r
1024 fclose($this->NSfp);
\r
1025 $this->error = 'Passport authenticated fail!';
\r
1026 $this->debug_message("*** $this->error");
\r
1030 $ticket = $aTickets['ticket'];
\r
1031 $secret = $aTickets['secret'];
\r
1032 $this->ticket = $aTickets;
\r
1033 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1035 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1036 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1037 $this->authed = true;
\r
1041 // main login server will redirect to anther NS after USR command
\r
1043 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1045 // NS: <<< XFR {id} NS {server} U D
\r
1046 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1047 if($Type!='NS') break;
\r
1048 @list($ip, $port) = @explode(':', $server);
\r
1049 // this connection will close after XFR
\r
1050 fclose($this->NSfp);
\r
1052 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
1053 if (!$this->NSfp) {
\r
1054 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1055 $this->debug_message("*** $this->error");
\r
1059 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1061 // NS: >> VER {id} MSNP9 CVR0
\r
1063 // NS: >>> VER {id} MSNP15 CVR0
\r
1064 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1068 // return some policy data after 'USR {id} SSO I {user}' command
\r
1069 // NS: <<< GCF 0 {size}
\r
1070 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1071 // we don't need the data, just read it and drop
\r
1072 if (is_numeric($size) && $size > 0)
\r
1073 $this->ns_readdata($size);
\r
1077 // we'll quit if got any error
\r
1078 if (is_numeric($code)) {
\r
1081 $this->ns_writeln("OUT");
\r
1082 fclose($this->NSfp);
\r
1083 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1084 $this->debug_message("*** $this->error");
\r
1087 // unknown response from server, just ignore it
\r
1091 // never goto here
\r
1094 function derive_key($key, $magic) {
\r
1095 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1096 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1097 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1098 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1099 return $hash2.substr($hash4, 0, 4);
\r
1102 function generateLoginBLOB($key, $challenge) {
\r
1103 $key1 = base64_decode($key);
\r
1104 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1105 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1107 // get hash of challenge using key2
\r
1108 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1110 // get 8 bytes random data
\r
1111 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1113 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1115 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1120 return base64_encode($blob);
\r
1123 function getOIM_maildata() {
\r
1124 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1125 if (count($matches) == 0) {
\r
1126 $this->debug_message('*** no web ticket?');
\r
1129 $t = htmlspecialchars($matches[1]);
\r
1130 $p = htmlspecialchars($matches[2]);
\r
1131 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1132 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1133 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1134 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1136 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1142 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1144 </soap:Envelope>';
\r
1146 $header_array = array(
\r
1147 'SOAPAction: '.$this->oim_maildata_soap,
\r
1148 'Content-Type: text/xml; charset=utf-8',
\r
1149 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1152 $this->debug_message("*** URL: $this->oim_maildata_url");
\r
1153 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1154 $curl = curl_init();
\r
1155 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1156 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1157 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1158 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1159 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1160 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1161 curl_setopt($curl, CURLOPT_POST, 1);
\r
1162 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1163 $data = curl_exec($curl);
\r
1164 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1165 curl_close($curl);
\r
1166 $this->debug_message("*** Get Result:\n$data");
\r
1168 if ($http_code != 200) {
\r
1169 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
\r
1173 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1174 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1175 if (count($matches) == 0) {
\r
1176 $this->debug_message("*** Can't get OIM maildata");
\r
1179 return $matches[2];
\r
1182 function getOIM_message($msgid) {
\r
1183 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1184 if (count($matches) == 0) {
\r
1185 $this->debug_message('*** no web ticket?');
\r
1188 $t = htmlspecialchars($matches[1]);
\r
1189 $p = htmlspecialchars($matches[2]);
\r
1192 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1193 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1194 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1195 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1197 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1203 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1204 <messageId>'.$msgid.'</messageId>
\r
1205 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1208 </soap:Envelope>';
\r
1210 $header_array = array(
\r
1211 'SOAPAction: '.$this->oim_read_soap,
\r
1212 'Content-Type: text/xml; charset=utf-8',
\r
1213 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1216 $this->debug_message("*** URL: $this->oim_read_url");
\r
1217 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1218 $curl = curl_init();
\r
1219 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1220 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1221 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1222 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1223 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1224 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1225 curl_setopt($curl, CURLOPT_POST, 1);
\r
1226 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1227 $data = curl_exec($curl);
\r
1228 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1229 curl_close($curl);
\r
1230 $this->debug_message("*** Get Result:\n$data");
\r
1232 if ($http_code != 200) {
\r
1233 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1237 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1239 $start = strpos($data, '<GetMessageResult>');
\r
1240 $end = strpos($data, '</GetMessageResult>');
\r
1241 if ($start === false || $end === false || $start > $end) {
\r
1242 $this->debug_message("*** Can't get OIM: $msgid");
\r
1245 $lines = substr($data, $start + 18, $end - $start);
\r
1246 $aLines = @explode("\n", $lines);
\r
1250 foreach ($aLines as $line) {
\r
1251 $line = rtrim($line);
\r
1253 if ($line === '') {
\r
1259 // stop at empty lines
\r
1260 if ($line === '') break;
\r
1263 $sMsg = base64_decode($sOIM);
\r
1264 $this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1267 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1268 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1269 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1270 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1272 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1278 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1280 <messageId>'.$msgid.'</messageId>
\r
1284 </soap:Envelope>';
\r
1286 $header_array = array(
\r
1287 'SOAPAction: '.$this->oim_del_soap,
\r
1288 'Content-Type: text/xml; charset=utf-8',
\r
1289 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1292 $this->debug_message("*** URL: $this->oim_del_url");
\r
1293 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1294 $curl = curl_init();
\r
1295 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1296 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1297 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1298 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1299 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1300 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1301 curl_setopt($curl, CURLOPT_POST, 1);
\r
1302 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1303 $data = curl_exec($curl);
\r
1304 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1305 curl_close($curl);
\r
1306 $this->debug_message("*** Get Result:\n$data");
\r
1308 if ($http_code != 200)
\r
1309 $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
\r
1311 $this->debug_message("*** OIM ($msgid) deleted");
\r
1314 private function NSLogout() {
\r
1315 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1318 $this->ns_writeln("OUT");
\r
1319 fclose($this->NSfp);
\r
1320 $this->NSfp = false;
\r
1321 $this->log_message("*** logout now!");
\r
1325 private function NSRetryWait($Wait) {
\r
1326 $this->log_message("*** wait for $Wait seconds");
\r
1327 for($i=0;$i<$Wait;$i++) {
\r
1329 if($this->kill_me) return false;
\r
1333 public function ProcessSendMessageFileQueue() {
\r
1334 $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');
\r
1335 if (!is_array($aFiles)) return true;
\r
1337 foreach ($aFiles as $filename) {
\r
1338 $fp = fopen($filename, 'rt');
\r
1339 if (!$fp) continue;
\r
1342 $buf = trim(fgets($fp));
\r
1343 if (substr($buf, 0, 3) == 'TO:') {
\r
1344 $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));
\r
1345 while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";
\r
1348 if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')
\r
1349 $this->log_message("!!! message format error? delete $filename");
\r
1352 foreach($aTo as $To)
\r
1354 @list($user, $domain, $network) = @explode('@', $To);
\r
1355 $MessageList[$network]["$user@$domain"]=$sMessage;
\r
1358 if($this->backup_file)
\r
1360 $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';
\r
1361 if (!file_exists($backup_dir)) @mkdir($backup_dir);
\r
1362 $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);
\r
1363 if (@rename($filename, $backup_name))
\r
1364 $this->log_message("*** move file to $backup_name");
\r
1366 else @unlink($filename);
\r
1368 foreach ($MessageList as $network => $Messages)
\r
1370 switch(trim($network))
\r
1374 // okay, try to ask a switchboard (SB) for sending message
\r
1375 // NS: >>> XFR {id} SB
\r
1376 // $this->ns_writeln("XFR $this->id SB");
\r
1377 foreach($Messages as $User => $Message)
\r
1378 $this->MessageQueue[$User][]=$Message;
\r
1380 case 'Offline': //MSN
\r
1382 //FIXME: 修正Send OIM
\r
1383 foreach($Messages as $To => $Message)
\r
1386 for ($i = 0; $i < $this->oim_try; $i++)
\r
1388 if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;
\r
1389 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
1390 // need challenge lockkey
\r
1391 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);
\r
1392 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
1395 if ($oim_result === false || $oim_result['auth_policy'] !== false)
\r
1399 $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
\r
1402 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
\r
1403 // maybe we need to re-login again
\r
1404 if(!$this->get_passport_ticket())
\r
1406 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1409 $this->log_message("**** get new ticket, try it again");
\r
1416 foreach($Messages as $To => $Message) {
\r
1417 $Message=$this->getMessage($Message, $network);
\r
1418 $len = strlen($Message);
\r
1419 $this->ns_writeln("UUM $this->id $To $network 1 $len");
\r
1420 $this->ns_writedata($Message);
\r
1421 $this->log_message("*** sent to $To (network: $network):\n$Message");
\r
1425 if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))
\r
1427 $this->MessageQueue[$User]['XFRSent']=false;
\r
1428 $this->MessageQueue[$User]['ReqTime']=false;
\r
1432 public function SignalFunction($signal)
\r
1442 $ChildPid=pcntl_wait($status,WUNTRACED);
\r
1445 $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);
\r
1446 unset($this->ChildProcess[$ChildPid]);
\r
1452 public function Run()
\r
1454 $this->log_message("*** startup ***");
\r
1455 if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");
\r
1456 if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");
\r
1457 if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");
\r
1458 $process_file = false;
\r
1461 $aContactList = array();
\r
1464 if($this->kill_me)
\r
1466 $this->log_message("*** Okay, kill me now!");
\r
1467 return $this->NSLogout();
\r
1469 if (!is_resource($this->NSfp) || feof($this->NSfp))
\r
1471 $this->log_message("*** try to connect to MSN network");
\r
1472 if (!$this->connect($this->user, $this->password))
\r
1474 $this->log_message("!!! Can't connect to server: $this->error");
\r
1475 if(!$this->NSRetryWait($this->retry_wait)) continue;
\r
1477 $this->UpdateContacts();
\r
1478 $this->LastPing=time();
\r
1479 $this->log_message("*** connected, wait for command");
\r
1480 $start_tm = time();
\r
1481 $ping_tm = time();
\r
1482 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
1483 $aContactList = $this->getMembershipList();
\r
1484 if ($this->update_pending) {
\r
1485 if (is_array($aContactList)) {
\r
1486 $pending = 'Pending';
\r
1487 foreach ($aContactList as $u_domain => $aUserList) {
\r
1488 foreach ($aUserList as $u_name => $aNetworks) {
\r
1489 foreach ($aNetworks as $network => $aData) {
\r
1490 if (isset($aData[$pending])) {
\r
1493 foreach (array('Allow', 'Reverse') as $list) {
\r
1494 if (isset($aData[$list]))
\r
1497 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1498 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1504 $id = $aData[$pending];
\r
1505 // we can delete it from pending now
\r
1506 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1507 unset($aContactList[$u_domain][$u_name][$network][$pending]);
\r
1512 foreach (array('Allow', 'Reverse') as $list) {
\r
1513 if (!isset($aData[$list])) {
\r
1514 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1515 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1527 if (is_array($aContactList)) {
\r
1528 foreach ($aContactList as $u_domain => $aUserList) {
\r
1529 $str = '<d n="'.$u_domain.'">';
\r
1530 $len += strlen($str);
\r
1531 if ($len > 7400) {
\r
1532 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1535 $len = strlen($str);
\r
1538 foreach ($aUserList as $u_name => $aNetworks) {
\r
1539 foreach ($aNetworks as $network => $status) {
\r
1540 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1541 $len += strlen($str);
\r
1542 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1544 if ($len > 7475) {
\r
1546 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1548 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1549 $len = strlen($sList);
\r
1558 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1559 // NS: >>> BLP {id} BL
\r
1560 $this->ns_writeln("BLP $this->id BL");
\r
1561 foreach ($aADL as $str) {
\r
1562 $len = strlen($str);
\r
1563 // NS: >>> ADL {id} {size}
\r
1564 $this->ns_writeln("ADL $this->id $len");
\r
1565 $this->ns_writedata($str);
\r
1567 // NS: >>> PRP {id} MFN name
\r
1568 if ($this->alias == '') $this->alias = $user;
\r
1569 $aliasname = rawurlencode($this->alias);
\r
1570 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1572 //$MsnObj=$this->PhotoStckObj();
\r
1573 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1574 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1575 if($this->PhotoStickerFile!==false)
\r
1576 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1577 // NS: >>> UUX {id} length
\r
1578 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1579 $len = strlen($str);
\r
1580 $this->ns_writeln("UUX $this->id $len");
\r
1581 $this->ns_writedata($str);
\r
1583 $data = $this->ns_readln();
\r
1586 //If No NS Message Process SendMessageFileQueue
\r
1587 if (time()-$this->LastPing > $this->ping_wait)
\r
1590 $this->ns_writeln("PNG");
\r
1591 $this->LastPing = time();
\r
1593 if(count($this->ChildProcess)<$this->MAXChildProcess)
\r
1596 foreach($this->MessageQueue as $User => $Message)
\r
1598 if(!trim($User)) continue;
\r
1599 if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;
\r
1600 if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))
\r
1602 $this->MessageQueue[$User]['XFRSent']=true;
\r
1603 $this->MessageQueue[$User]['ReqTime']=time();
\r
1604 $this->log_message("*** Request SB for $User");
\r
1605 $this->ns_writeln("XFR $this->id SB");
\r
1610 if($this->ProcessSendMessageFileQueue()) continue;
\r
1613 switch (substr($data,0,3))
\r
1616 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
1617 // NS: <<< SBS 0 null
\r
1622 // NS: <<< RFS ???
\r
1623 // refresh ADL, so we re-send it again
\r
1624 if (is_array($aADL)) {
\r
1625 foreach ($aADL as $str) {
\r
1626 $len = strlen($str);
\r
1627 // NS: >>> ADL {id} {size}
\r
1628 $this->ns_writeln("ADL $this->id $len");
\r
1629 $this->ns_writedata($str);
\r
1635 // NS: <<< LST {email} {alias} 11 0
\r
1636 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
1637 @list($u_name, $u_domain) = @explode('@', $email);
\r
1638 if (!isset($aContactList[$u_domain][$u_name][1])) {
\r
1639 $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
1640 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
1645 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
1646 // NS: <<< ADL 0 {size}
\r
1647 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
1648 if (is_numeric($size) && $size > 0)
\r
1650 $data = $this->ns_readdata($size);
\r
1651 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1652 if (is_array($matches) && count($matches) > 0)
\r
1654 $u_domain = $matches[1];
\r
1655 $u_name = $matches[2];
\r
1656 $network = $matches[4];
\r
1657 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1658 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
1661 $re_login = false;
\r
1663 foreach (array('Allow', 'Reverse') as $list)
\r
1665 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1668 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1671 $aTickets = $this->get_passport_ticket();
\r
1672 if (!$aTickets || !is_array($aTickets)) {
\r
1673 // failed to login? ignore it
\r
1674 $this->log_message("*** can't re-login, something wrong here");
\r
1675 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1679 $this->ticket = $aTickets;
\r
1680 $this->log_message("**** get new ticket, try it again");
\r
1681 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1683 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
1687 $aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1690 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
1692 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
1693 $len = strlen($str);
\r
1696 $this->log_message("*** someone add us to their list: $data");
\r
1697 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
1702 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
1703 // NS: <<< RML 0 {size}
\r
1704 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
1705 if (is_numeric($size) && $size > 0)
\r
1707 $data = $this->ns_readdata($size);
\r
1708 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1709 if (is_array($matches) && count($matches) > 0)
\r
1711 $u_domain = $matches[1];
\r
1712 $u_name = $matches[2];
\r
1713 $network = $matches[4];
\r
1714 if (isset($aContactList[$u_domain][$u_name][$network]))
\r
1716 $aData = $aContactList[$u_domain][$u_name][$network];
\r
1717 foreach ($aData as $list => $id)
\r
1718 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
1719 unset($aContactList[$u_domain][$u_name][$network]);
\r
1720 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
1723 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
1724 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
1727 $this->log_message("*** someone remove us from their list: $data");
\r
1732 // randomly, we get MSG notification from server
\r
1733 // NS: <<< MSG Hotmail Hotmail {size}
\r
1734 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
1735 if (is_numeric($size) && $size > 0) {
\r
1736 $data = $this->ns_readdata($size);
\r
1737 $aLines = @explode("\n", $data);
\r
1741 foreach ($aLines as $line) {
\r
1742 $line = rtrim($line);
\r
1744 if ($line === '') {
\r
1748 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
1749 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
1750 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
1751 // we just need text/x-msmsgsinitialmdatanotification
\r
1752 // or text/x-msmsgsoimnotification
\r
1759 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
1760 $maildata = trim(substr($line, 10));
\r
1765 $this->log_message("*** ingnore MSG for: $line");
\r
1768 if ($maildata == '') {
\r
1769 $this->log_message("*** ingnore MSG not for OIM");
\r
1772 $re_login = false;
\r
1773 if (strcasecmp($maildata, 'too-large') == 0) {
\r
1774 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
1775 $maildata = $this->getOIM_maildata();
\r
1776 if ($maildata === false) {
\r
1777 $this->log_message("*** can't get mail-data via SOAP");
\r
1778 // maybe we need to re-login again
\r
1779 $aTickets = $this->get_passport_ticket();
\r
1780 if (!$aTickets || !is_array($aTickets)) {
\r
1781 // failed to login? ignore it
\r
1782 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1786 $this->ticket = $aTickets;
\r
1787 $this->log_message("**** get new ticket, try it again");
\r
1788 $maildata = $this->getOIM_maildata();
\r
1789 if ($maildata === false) {
\r
1790 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
1795 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
1799 $start = strpos($p, '<M>');
\r
1800 $end = strpos($p, '</M>');
\r
1801 if ($start === false || $end === false || $start > $end) break;
\r
1803 $sOIM = substr($p, $start, $end - $start);
\r
1805 $p = substr($p, $end);
\r
1807 if (count($aOIMs) == 0) {
\r
1808 $this->log_message("*** ingnore empty OIM");
\r
1811 foreach ($aOIMs as $maildata) {
\r
1812 // T: 11 for MSN, 13 for Yahoo
\r
1813 // S: 6 for MSN, 7 for Yahoo
\r
1814 // RT: the datetime received by server
\r
1815 // RS: already read or not
\r
1816 // SZ: size of message
\r
1819 // F: always 00000000-0000-0000-0000-000000000009
\r
1820 // N: sender alias
\r
1821 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
1822 if (count($matches) == 0) {
\r
1823 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
1826 $oim_type = $matches[1];
\r
1827 if ($oim_type = 13)
\r
1831 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
1832 if (count($matches) == 0) {
\r
1833 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
1836 $oim_sender = $matches[1];
\r
1837 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
1838 if (count($matches) == 0) {
\r
1839 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
1842 $oim_msgid = $matches[1];
\r
1843 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
1844 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
1845 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
1846 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
1847 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
1848 $sMsg = $this->getOIM_message($oim_msgid);
\r
1849 if ($sMsg === false) {
\r
1850 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
1852 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1855 $aTickets = $this->get_passport_ticket();
\r
1856 if (!$aTickets || !is_array($aTickets)) {
\r
1857 // failed to login? ignore it
\r
1858 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
1862 $this->ticket = $aTickets;
\r
1863 $this->log_message("**** get new ticket, try it again");
\r
1864 $sMsg = $this->getOIM_message($oim_msgid);
\r
1865 if ($sMsg === false) {
\r
1866 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
1870 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
1872 $this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
1878 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
1879 // NS: <<< UBM {email} $network $type {size}
\r
1880 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
1881 if (is_numeric($size) && $size > 0)
\r
1883 $data = $this->ns_readdata($size);
\r
1884 $aLines = @explode("\n", $data);
\r
1888 foreach ($aLines as $line) {
\r
1889 $line = rtrim($line);
\r
1891 if ($line === '') {
\r
1895 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
1901 $aSubLines = @explode("\r", $line);
\r
1902 foreach ($aSubLines as $str) {
\r
1910 $this->log_message("*** ingnore from $from_email: $line");
\r
1913 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
1914 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
1919 // randomly, we get UBX notification from server
\r
1920 // NS: <<< UBX email {network} {size}
\r
1921 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
1922 // we don't need the notification data, so just ignore it
\r
1923 if (is_numeric($size) && $size > 0)
\r
1924 $this->ns_readdata($size);
\r
1928 // randomly, we'll get challenge from server
\r
1929 // NS: <<< CHL 0 {code}
\r
1930 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
1931 $fingerprint = $this->getChallenge($chl_code);
\r
1932 // NS: >>> QRY {id} {product_id} 32
\r
1933 // NS: >>> fingerprint
\r
1934 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
1935 $this->ns_writedata($fingerprint);
\r
1936 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1937 if($this->PhotoStickerFile!==false)
\r
1938 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1941 // NS: <<< CHG {id} {status} {code}
\r
1943 // change our status to online first
\r
1947 // sometimes, NS will redirect to another NS
\r
1949 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1951 // NS: <<< XFR {id} NS {server} U D
\r
1952 // for normal switchboard XFR
\r
1953 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
1954 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
1955 @list($ip, $port) = @explode(':', $server);
\r
1956 if ($server_type != 'SB') {
\r
1958 // this connection will close after XFR
\r
1959 $this->NSLogout();
\r
1962 if(count($this->MessageQueue))
\r
1964 foreach($this->MessageQueue as $User => $Message)
\r
1966 //$this->ChildProcess[$ChildPid]
\r
1967 $this->log_message("*** XFR SB $User");
\r
1968 $pid=pcntl_fork();
\r
1972 $this->ChildProcess[$pid]=$User;
\r
1977 $this->log_message("*** Fork Error $User");
\r
1983 $this->log_message("*** Child Process Start for $User");
\r
1984 unset($Message['XFRSent']);
\r
1985 unset($Message['ReqTime']);
\r
1986 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
1987 if ($bSBresult === false)
\r
1989 // error for switchboard
\r
1990 $this->log_message("!!! error for sending message to ".$User);
\r
1995 unset($this->MessageQueue[$User]);
\r
1998 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
1999 if ($bSBresult === false) {
\r
2000 // error for switchboard
\r
2001 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
2002 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
2006 // NS: <<< QNG {time}
\r
2007 @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);
\r
2008 if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
2009 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
2010 //Mod by Ricky Set Online
\r
2014 if($this->PhotoStickerFile!==false)
\r
2015 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2017 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2018 // someone is trying to talk to us
\r
2019 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2020 $this->log_message("NS: <<< RNG $data");
\r
2021 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2022 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2023 if($this->IsIgnoreMail($email))
\r
2025 $this->log_message("*** Ignore RNG from $email");
\r
2028 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
2029 $this->addContact($email,1,$email, true);
\r
2030 $pid=pcntl_fork();
\r
2034 $this->ChildProcess[$pid]='RNG';
\r
2039 $this->log_message("*** Fork Error $User");
\r
2045 $this->log_message("*** Ring Child Process Start for $User");
\r
2046 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
2051 // force logout from NS
\r
2052 // NS: <<< OUT xxx
\r
2053 fclose($this->NSfp);
\r
2054 $this->log_message("*** LOGOUT from NS");
\r
2058 $code = substr($data,0,3);
\r
2059 if (is_numeric($code)) {
\r
2060 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2061 $this->debug_message("*** NS: $this->error");
\r
2063 return $this->NsLogout();
\r
2068 return $this->NsLogout();
\r
2071 /*public function SendMessage($Message, $To)
\r
2073 $FileName = MSN_CLASS_SPOOL_DIR.'/'.strftime('%Y%m%d%H%M%S',time()).'_'.posix_getpid().'_sendMessage.msn';
\r
2074 if(!is_array($To))
\r
2077 foreach($To as $Email)
\r
2079 list($name,$host,$network)=explode('@',$Email);
\r
2080 $network=$network==''?1:$network;
\r
2081 if($network==1 && $this->SwitchBoardProcess && $this->SwitchBoardSessionUser=="$name@$host" )
\r
2083 $this->debug_message("*** SendMessage to $Receiver use SB message queue.");
\r
2084 array_push($this->SwitchBoardMessageQueue,$Message);
\r
2087 $Receiver.="$name@$host@$network,";
\r
2089 if($Receiver=='') return;
\r
2090 $Receiver=substr($Receiver,0,-1);
\r
2091 $this->debug_message("*** SendMessage to $Receiver use File queue.");
\r
2092 file_put_contents($FileName,"TO: $Receiver\n$Message\n");
\r
2095 function getChallenge($code)
\r
2098 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
2099 // Step 1: The MD5 Hash
\r
2100 $md5Hash = md5($code.$this->prod_key);
\r
2101 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2102 for ($i = 0; $i < 4; $i++) {
\r
2103 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
2104 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
2107 // Step 2: A new string
\r
2108 $chl_id = $code.$this->prod_id;
\r
2109 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
2111 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
2112 for ($i = 0; $i < count($aID); $i++) {
\r
2113 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
2114 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
2117 // Step 3: The 64 bit key
\r
2118 $magic_num = 0x0E79A9C1;
\r
2119 $str7f = 0x7FFFFFFF;
\r
2122 for ($i = 0; $i < count($aID); $i += 2) {
\r
2124 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
2125 $temp = bcadd($temp, $high);
\r
2126 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
2127 $temp = bcmod($temp, $str7f);
\r
2129 $high = $aID[$i+1];
\r
2130 $high = bcmod(bcadd($high, $temp), $str7f);
\r
2131 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
2132 $high = bcmod($high, $str7f);
\r
2134 $low = bcadd(bcadd($low, $high), $temp);
\r
2137 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
2138 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
2140 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
2141 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
2142 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
2143 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
2144 // we need integer here
\r
2145 $high = 0+$new_high;
\r
2147 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
2148 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
2149 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
2150 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
2151 // we need integer here
\r
2152 $low = 0+$new_low;
\r
2154 // we just use 32 bits integer, don't need the key, just high/low
\r
2155 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
2157 // Step 4: Using the key
\r
2158 $md5Hash = md5($code.$this->prod_key);
\r
2159 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2162 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
2163 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
2164 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
2165 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
2170 private function getMessage($sMessage, $network = 1)
\r
2172 $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
2173 $msg_header_len = strlen($msg_header);
\r
2174 if ($network == 1)
\r
2175 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
2177 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
2178 $sMessage=str_replace("\r", '', $sMessage);
\r
2179 $msg=substr($sMessage,0,$maxlen);
\r
2180 return $msg_header.$msg;
\r
2184 * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息
\r
2188 private function DoSwitchBoard($Action,$Param)
\r
2190 $SessionEnd=false;
\r
2193 $LastActive=time();
\r
2194 stream_set_timeout($this->SBFp, $this->SBTimeout);
\r
2198 $cki_code=$Param['cki'];
\r
2199 $user=$Param['user'];
\r
2200 $this->SwitchBoardMessageQueue=$Param['Msg'];
\r
2201 // SB: >>> USR {id} {user} {cki}
\r
2202 $this->SB_writeln("USR $id $this->user $cki_code");
\r
2204 $this->SwitchBoardSessionUser=$user;
\r
2207 $ticket=$Param['ticket'];
\r
2208 $sid=$Param['sid'];
\r
2209 $user=$Param['user'];
\r
2210 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2211 $this->SB_writeln("ANS $id $this->user $ticket $sid");
\r
2213 $this->SwitchBoardSessionUser=$user;
\r
2218 while((!feof($this->SBFp))&&(!$SessionEnd))
\r
2220 $data = $this->SB_readln();
\r
2221 if($this->kill_me)
\r
2223 $this->log_message("*** SB Okay, kill me now!");
\r
2226 if($data === false)
\r
2228 if(time()-$LastActive > $this->SBIdleTimeout)
\r
2230 $this->debug_message("*** SB Idle Timeout!");
\r
2233 if(!$Joined) continue;
\r
2234 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2236 if($Message=='') continue;
\r
2237 $aMessage = $this->getMessage($Message);
\r
2239 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2240 if($MsnObjDefine!=='')
\r
2242 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2243 $len = strlen($SendString);
\r
2244 $this->SB_writeln("MSG $id N $len");
\r
2246 $this->SB_writedata($SendString);
\r
2249 $len = strlen($aMessage);
\r
2250 $this->SB_writeln("MSG $id N $len");
\r
2252 $this->SB_writedata($aMessage);
\r
2254 $this->SwitchBoardMessageQueue=array();
\r
2255 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2258 $code = substr($data, 0, 3);
\r
2262 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
2263 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
2264 $this->log_message("*** $email join us");
\r
2268 $this->log_message("*** Quit for BYE");
\r
2272 // SB: <<< USR {id} OK {user} {alias}
\r
2273 // we don't need the data, just ignore it
\r
2274 // request user to join this switchboard
\r
2275 // SB: >>> CAL {id} {user}
\r
2276 $this->SB_writeln("CAL $id $user");
\r
2280 // SB: <<< CAL {id} RINGING {?}
\r
2281 // we don't need this, just ignore, and wait for other response
\r
2285 // SB: <<< JOI {user} {alias} {clientid?}
\r
2286 // someone join us
\r
2287 // we don't need the data, just ignore it
\r
2288 // no more user here
\r
2292 // SB: <<< MSG {email} {alias} {len}
\r
2293 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
2294 $len = trim($len);
\r
2295 $data = $this->SB_readdata($len);
\r
2296 $aLines = @explode("\n", $data);
\r
2301 foreach ($aLines as $line)
\r
2303 $line = rtrim($line);
\r
2305 if ($line === '') {
\r
2309 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2310 // typing notification, just ignore
\r
2314 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
2315 // we don't handle any split message, just ignore
\r
2319 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
2320 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
2322 $p = strstr($data, "\n\n");
\r
2324 if ($p === false) {
\r
2325 $p = strstr($data, "\r\n\r\n");
\r
2327 $sMsg = substr($p, 4);
\r
2330 $sMsg = substr($p, 2);
\r
2333 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
2334 // ignore all application/x-... message
\r
2336 // application/x-ms-ink => ink message
\r
2340 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
2341 // ignore all text/x-... message
\r
2343 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
2344 // text/x-mms-animemoticon => customized animemotion word
\r
2356 $this->log_message("*** ingnore from $from_email: $line");
\r
2361 // we will ignore any p2p message after sending acknowledgement
\r
2363 $len = strlen($sMsg);
\r
2364 $this->log_message("*** p2p message from $from_email, size $len");
\r
2365 // header = 48 bytes
\r
2366 // content >= 0 bytes
\r
2367 // footer = 4 bytes
\r
2368 // so it need to >= 52 bytes
\r
2369 /*if ($len < 52) {
\r
2370 $this->log_message("*** p2p: size error, less than 52!");
\r
2373 $aDwords = @unpack("V12dword", $sMsg);
\r
2374 if (!is_array($aDwords)) {
\r
2375 $this->log_message("*** p2p: header unpack error!");
\r
2378 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
2379 $hdr_SessionID = $aDwords['dword1'];
\r
2380 $hdr_Identifier = $aDwords['dword2'];
\r
2381 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
2382 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
2383 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
2384 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
2385 $hdr_MessageLength = $aDwords['dword7'];
\r
2386 $hdr_Flag = $aDwords['dword8'];
\r
2387 $hdr_AckID = $aDwords['dword9'];
\r
2388 $hdr_AckUID = $aDwords['dword10'];
\r
2389 $hdr_AckSizeLow = $aDwords['dword11'];
\r
2390 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
2391 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
2392 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
2393 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
2394 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
2395 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
2396 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
2397 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
2398 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
2399 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
2400 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
2401 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
2402 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
2403 if($hdr_Flag==2) {
\r
2404 //This is an ACK from SB ignore....
\r
2405 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
2408 $MsgBody=$this->linetoArray(substr($sMsg,48,-4));
\r
2409 $this->debug_message("*** p2p: body".print_r($MsgBody,true));
\r
2410 if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))
\r
2414 if($this->SB_readln()===false) break;
\r
2416 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));
\r
2417 preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);
\r
2418 $BranchGUID=$Matches[1];
\r
2419 //it's an invite to send a display picture.
\r
2420 $new_id = ~$hdr_Identifier;
\r
2421 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2424 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2429 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2430 $footer = pack("L", 0);
\r
2431 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2432 $len = strlen($message);
\r
2433 $this->SB_writeln("MSG $id D $len");
\r
2435 $this->SB_writedata($message);
\r
2436 $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
2437 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
2438 $this->SB_readln();//Read ACK;
\r
2439 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
2441 //Send 200 OK message
\r
2442 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
2444 "MSNSLP/1.0 200 OK\r\n".
\r
2445 "To: <msnmsgr:".$from_email.">\r\n".
\r
2446 "From: <msnmsgr:".$this->user.">\r\n".
\r
2447 "Via: ".$MsgBody['Via']."\r\n".
\r
2448 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
2449 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2450 "Max-Forwards: 0\r\n".
\r
2451 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
2452 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
2454 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2455 $hdr_TotalDataSizeHigh=0;
\r
2456 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2459 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2460 strlen($MessagePayload),
\r
2467 "MIME-Version: 1.0\r\n".
\r
2468 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2469 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2470 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2472 $this->SB_writedata($message);
\r
2473 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
2474 $this->SB_readln();//Read ACK;
\r
2476 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
2477 //send Data preparation message
\r
2478 //send 4 null bytes as data
\r
2479 $hdr_TotalDataSizeLow=4;
\r
2480 $hdr_TotalDataSizeHigh=0;
\r
2482 $hdr = pack("LLLLLLLLLLLL",
\r
2483 $MsgBody['SessionID'],
\r
2486 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2487 $hdr_TotalDataSizeLow,
\r
2493 "MIME-Version: 1.0\r\n".
\r
2494 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2495 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";
\r
2496 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2498 $this->SB_writedata($message);
\r
2499 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
2500 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
2501 $this->SB_readln();//Read ACK;
\r
2503 //send Data Content..
\r
2504 $footer=pack('N',1);
\r
2506 $FileSize=filesize($PictureFilePath);
\r
2507 if($hTitle=fopen($PictureFilePath,'rb'))
\r
2511 while(!feof($hTitle))
\r
2513 $FileContent=fread($hTitle,1024);
\r
2514 $FileContentSize=strlen($FileContent);
\r
2515 $hdr = pack("LLLLLLLLLLLL",
\r
2516 $MsgBody['SessionID'],
\r
2527 "MIME-Version: 1.0\r\n".
\r
2528 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2529 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
2530 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2532 $this->SB_writedata($message);
\r
2533 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
2534 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
2535 //$this->SB_readln();//Read ACK;
\r
2536 $Offset+=$FileContentSize;
\r
2541 $MessageContent="\r\n".pack("C", 0);
\r
2543 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
2544 "To: <msnmsgr:$from_email>\r\n".
\r
2545 "From: <msnmsgr:".$this->user.">\r\n".
\r
2546 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
2548 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2549 "Max-Forwards: 0\r\n".
\r
2550 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
2551 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
2552 $footer=pack('N',0);
\r
2553 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2554 $hdr_TotalDataSizeHigh=0;
\r
2556 $hdr = pack("LLLLLLLLLLLL",
\r
2560 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2567 "MIME-Version: 1.0\r\n".
\r
2568 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2569 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2570 $this->SB_writeln("MSG $id D ".strlen($message));
\r
2572 $this->SB_writedata($message);
\r
2573 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
2578 //if ($hdr_Flag == 2) {
\r
2579 // just send ACK...
\r
2580 // $this->SB_writeln("ACK $id");
\r
2583 if ($hdr_SessionID == 4) {
\r
2585 $this->debug_message("*** p2p: ignore flag 4");
\r
2588 $finished = false;
\r
2589 if ($hdr_TotalDataSizeHigh == 0) {
\r
2590 // only 32 bites size
\r
2591 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
2595 // we won't accept any file transfer
\r
2596 // so I think we won't get any message size need to use 64 bits
\r
2597 // 64 bits size here, can't count directly...
\r
2598 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
2599 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
2600 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
2601 $now_size = bcadd($dataoffset, $messagelength);
\r
2602 if (bccomp($now_size, $totalsize) >= 0)
\r
2606 // ignore not finished split packet
\r
2607 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
2610 //$new_id = ~$hdr_Identifier;
\r
2613 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2616 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2621 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2622 $footer = pack("L", 0);
\r
2623 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2624 $len = strlen($message);
\r
2625 $this->SB_writeln("MSG $id D $len");
\r
2627 $this->SB_writedata($message);
\r
2628 $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
2629 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2633 $this->log_message("*** MSG from $from_email: $sMsg");
\r
2634 $this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
2637 $this->log_message("*** User $user is offline. Try OIM.");
\r
2638 foreach($this->SwitchBoardMessageQueue as $Message)
\r
2639 $this->SendMessage($Message,"$user@Offline");
\r
2643 if (is_numeric($code))
\r
2645 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2646 $this->debug_message("*** SB: $this->error");
\r
2651 if(!$this->IsIgnoreMail($user)) $LastActive = time();
\r
2653 if (feof($this->SBFp))
\r
2655 // lost connection? error? try OIM later
\r
2656 @fclose($this->SBFp);
\r
2659 $this->SB_writeln("OUT");
\r
2660 @fclose($this->SBFp);
\r
2663 private function switchboard_control($ip, $port, $cki_code, $user, $Messages)
\r
2665 $this->SwitchBoardProcess=1;
\r
2666 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2667 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2670 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2673 return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));
\r
2675 private function switchboard_ring($ip, $port, $sid, $ticket,$user)
\r
2677 $this->SwitchBoardProcess=2;
\r
2678 $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
\r
2679 $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);
\r
2682 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2685 return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));
\r
2688 private function sendOIM($to, $sMessage, $lockkey)
\r
2690 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2691 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2692 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2693 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
2695 <From memberName="'.$this->user.'"
\r
2696 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
2699 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
2700 msnpVer="'.$this->protocol.'"
\r
2701 buildVer="'.$this->buildver.'"/>
\r
2702 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2703 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
2704 appid="'.$this->prod_id.'"
\r
2705 lockkey="'.$lockkey.'"
\r
2706 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2707 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
2708 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
2709 <MessageNumber>1</MessageNumber>
\r
2713 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
2714 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
2715 Content-Type: text/plain; charset=UTF-8
\r
2716 Content-Transfer-Encoding: base64
\r
2717 X-OIM-Message-Type: OfflineMessage
\r
2718 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
2719 X-OIM-Sequence-Num: 1
\r
2721 '.chunk_split(base64_encode($sMessage)).'
\r
2724 </soap:Envelope>';
\r
2726 $header_array = array(
\r
2727 'SOAPAction: '.$this->oim_send_soap,
\r
2728 'Content-Type: text/xml',
\r
2729 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
2732 $this->debug_message("*** URL: $this->oim_send_url");
\r
2733 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2734 $curl = curl_init();
\r
2735 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
2736 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2737 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2738 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2739 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2740 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2741 curl_setopt($curl, CURLOPT_POST, 1);
\r
2742 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2743 $data = curl_exec($curl);
\r
2744 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2745 curl_close($curl);
\r
2746 $this->debug_message("*** Get Result:\n$data");
\r
2748 if ($http_code == 200) {
\r
2749 $this->debug_message("*** OIM sent for $to");
\r
2753 $challenge = false;
\r
2754 $auth_policy = false;
\r
2755 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
2756 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
2757 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
2758 if (count($matches) != 0) {
\r
2759 // yes, we get new LockKeyChallenge
\r
2760 $challenge = $matches[2];
\r
2761 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
2763 // auth policy error
\r
2764 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
2765 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
2766 if (count($matches) != 0) {
\r
2767 $auth_policy = $matches[2];
\r
2768 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
2770 if ($auth_policy === false && $challenge === false) {
\r
2771 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
2772 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
2773 if (count($matches) == 0) {
\r
2774 // no error, we assume the OIM is sent
\r
2775 $this->debug_message("*** OIM sent for $to");
\r
2778 $err_code = $matches[2];
\r
2779 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
2780 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
2781 if (count($matches) > 0)
\r
2782 $err_msg = $matches[1];
\r
2785 $this->debug_message("*** OIM failed for $to");
\r
2786 $this->debug_message("*** OIM Error code: $err_code");
\r
2787 $this->debug_message("*** OIM Error Message: $err_msg");
\r
2790 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
2793 // read data for specified size
\r
2794 private function ns_readdata($size) {
\r
2797 while (!feof($this->NSfp)) {
\r
2798 $buf = @fread($this->NSfp, $size - $count);
\r
2800 $count += strlen($buf);
\r
2801 if ($count >= $size) break;
\r
2803 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2808 private function ns_readln() {
\r
2809 $data = @fgets($this->NSfp, 4096);
\r
2810 if ($data !== false) {
\r
2811 $data = trim($data);
\r
2812 $this->debug_message("NS: <<< $data");
\r
2817 // write to server, append \r\n, also increase id
\r
2818 private function ns_writeln($data) {
\r
2819 @fwrite($this->NSfp, $data."\r\n");
\r
2820 $this->debug_message("NS: >>> $data");
\r
2825 // write data to server
\r
2826 private function ns_writedata($data) {
\r
2827 @fwrite($this->NSfp, $data);
\r
2828 $this->debug_message("NS: >>> $data");
\r
2832 // read data for specified size for SB
\r
2833 private function sb_readdata($size) {
\r
2836 while (!feof($this->SBFp)) {
\r
2837 $buf = @fread($this->SBFp, $size - $count);
\r
2839 $count += strlen($buf);
\r
2840 if ($count >= $size) break;
\r
2842 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2846 // read one line for SB
\r
2847 private function sb_readln() {
\r
2848 $data = @fgets($this->SBFp, 4096);
\r
2849 if ($data !== false) {
\r
2850 $data = trim($data);
\r
2851 $this->debug_message("SB: <<< $data");
\r
2856 // write to server for SB, append \r\n, also increase id
\r
2857 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
2858 private function sb_writeln($data) {
\r
2859 @fwrite($this->SBFp, $data."\r\n");
\r
2860 $this->debug_message("SB: >>> $data");
\r
2865 // write data to server
\r
2866 private function sb_writedata($data) {
\r
2867 @fwrite($this->SBFp, $data);
\r
2868 $this->debug_message("SB: >>> $data");
\r
2872 // show debug information
\r
2873 function debug_message($str) {
\r
2874 if (!$this->debug) return;
\r
2875 if($this->debug===STDOUT) echo $str."\n";
\r
2876 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
2877 $fp = fopen($fname, 'at');
\r
2879 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2883 // still show debug information, if we can't open log_file
\r
2888 function dump_binary($str) {
\r
2892 $len = strlen($str);
\r
2893 for ($i = 0; $i < $len; $i++) {
\r
2894 if (($i % 16) == 0) {
\r
2895 if ($buf !== '') {
\r
2896 $buf .= "$h_str $a_str\n";
\r
2898 $buf .= sprintf("%04X:", $i);
\r
2902 $ch = ord($str[$i]);
\r
2906 $a_str .= chr($ch);
\r
2907 $h_str .= sprintf(" %02X", $ch);
\r
2909 if ($h_str !== '')
\r
2910 $buf .= "$h_str $a_str\n";
\r
2915 function log_message($str) {
\r
2916 /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';
\r
2917 $fp = fopen($fname, 'at');
\r
2919 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
2922 $this->debug_message($str);
\r
2927 * @param $FilePath 圖檔路徑
\r
2928 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
2931 private function MsnObj($FilePath,$Type=3)
\r
2933 if(!($FileSize=filesize($FilePath))) return '';
\r
2934 $Location=md5($FilePath);
\r
2935 $Friendly=md5($FilePath.$Type);
\r
2936 if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
2937 $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));
\r
2938 $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
2939 $this->MsnObjArray[$Location]=$FilePath;
\r
2940 $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
2941 $this->MsnObjMap[$Location]=$MsnObj;
\r
2942 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
2945 private function linetoArray($lines) {
\r
2946 $lines=str_replace("\r",'',$lines);
\r
2947 $lines=explode("\n",$lines);
\r
2948 foreach($lines as $line) {
\r
2949 if(!isset($line{3})) continue;
\r
2950 list($Key,$Val)=explode(':',$line);
\r
2951 $Data[trim($Key)]=trim($Val);
\r
2955 private function GetPictureFilePath($Context)
\r
2957 $MsnObj=base64_decode($Context);
\r
2958 if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))
\r
2959 $location=$Match[1];
\r
2960 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
2961 if($location&&(isset($this->MsnObjArray[$location])))
\r
2962 return $this->MsnObjArray[$location];
\r
2965 private function GetMsnObjDefine($Message)
\r
2968 if(is_array($this->Emotions))
\r
2969 foreach($this->Emotions as $Pattern => $FilePath)
\r
2971 if(strpos($Message,$Pattern)!==false)
\r
2972 $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";
\r
2974 return $DefineString;
\r
2977 * Receive Message Overload Function
\r
2980 * @param $Network 1 => msn , 32 =>yahoo
\r
2982 * @return unknown_type
\r
2984 protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}
\r
2986 * Remove Us From Member List Overload Function
\r
2989 * @param $Network 1 => msn , 32 =>yahoo
\r
2990 * @return unknown_type
\r
2992 protected function RemoveUsFromMemberList($User,$Network){}
\r
2994 * Add Us to Member List Overload Function
\r
2997 * @param $Network 1 => msn , 32 =>yahoo
\r
2998 * @return unknown_type
\r
3000 protected function AddUsToMemberList($User,$Network){}
\r
3002 public function signon() {
\r
3003 $this->log_message("*** try to connect to MSN network");
\r
3004 while(!$this->connect($this->user, $this->password))
\r
3006 $this->log_message("!!! Can't connect to server: $this->error");
\r
3007 if(!$this->NSRetryWait($this->retry_wait)) return;
\r
3009 $this->UpdateContacts();
\r
3010 $this->LastPing=time();
\r
3011 $this->log_message("*** connected, wait for command");
\r
3012 $start_tm = time();
\r
3013 $ping_tm = time();
\r
3014 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);
\r
3015 $this->aContactList = $this->getMembershipList();
\r
3016 if ($this->update_pending) {
\r
3017 if (is_array($this->aContactList)) {
\r
3018 $pending = 'Pending';
\r
3019 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3020 foreach ($aUserList as $u_name => $aNetworks) {
\r
3021 foreach ($aNetworks as $network => $aData) {
\r
3022 if (isset($aData[$pending])) {
\r
3025 foreach (array('Allow', 'Reverse') as $list) {
\r
3026 if (isset($aData[$list]))
\r
3029 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
3030 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3036 $id = $aData[$pending];
\r
3037 // we can delete it from pending now
\r
3038 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
3039 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
3044 foreach (array('Allow', 'Reverse') as $list) {
\r
3045 if (!isset($aData[$list])) {
\r
3046 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3047 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3059 if (is_array($this->aContactList)) {
\r
3060 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
3061 $str = '<d n="'.$u_domain.'">';
\r
3062 $len += strlen($str);
\r
3063 if ($len > 7400) {
\r
3064 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3067 $len = strlen($str);
\r
3070 foreach ($aUserList as $u_name => $aNetworks) {
\r
3071 foreach ($aNetworks as $network => $status) {
\r
3072 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
3073 $len += strlen($str);
\r
3074 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
3076 if ($len > 7475) {
\r
3078 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3080 $sList = '<d n="'.$u_domain.'">'.$str;
\r
3081 $len = strlen($sList);
\r
3090 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
3091 // NS: >>> BLP {id} BL
\r
3092 $this->ns_writeln("BLP $this->id BL");
\r
3093 foreach ($aADL as $str) {
\r
3094 $len = strlen($str);
\r
3095 // NS: >>> ADL {id} {size}
\r
3096 $this->ns_writeln("ADL $this->id $len");
\r
3097 $this->ns_writedata($str);
\r
3099 // NS: >>> PRP {id} MFN name
\r
3100 if ($this->alias == '') $this->alias = $user;
\r
3101 $aliasname = rawurlencode($this->alias);
\r
3102 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
3104 //$MsnObj=$this->PhotoStckObj();
\r
3105 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
3106 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3107 if($this->PhotoStickerFile!==false)
\r
3108 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3109 // NS: >>> UUX {id} length
\r
3110 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
3111 $len = strlen($str);
\r
3112 $this->ns_writeln("UUX $this->id $len");
\r
3113 $this->ns_writedata($str);
\r
3116 public function NSreceive() {
\r
3117 $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
3126 $data = $this->ns_readln();
\r
3127 if($data === false) {
\r
3128 // There was no data / an error when reading from the socket so reconnect
\r
3131 switch (substr($data,0,3))
\r
3134 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
3135 // NS: <<< SBS 0 null
\r
3140 // NS: <<< RFS ???
\r
3141 // refresh ADL, so we re-send it again
\r
3142 if (is_array($aADL)) {
\r
3143 foreach ($aADL as $str) {
\r
3144 $len = strlen($str);
\r
3145 // NS: >>> ADL {id} {size}
\r
3146 $this->ns_writeln("ADL $this->id $len");
\r
3147 $this->ns_writedata($str);
\r
3153 // NS: <<< LST {email} {alias} 11 0
\r
3154 @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
\r
3155 @list($u_name, $u_domain) = @explode('@', $email);
\r
3156 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
3157 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
3158 $this->log_message("*** add to our contact list: $u_name@$u_domain");
\r
3163 // randomly, we get ADL command, someome add us to their contact list for MSNP15
\r
3164 // NS: <<< ADL 0 {size}
\r
3165 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
3166 if (is_numeric($size) && $size > 0)
\r
3168 $data = $this->ns_readdata($size);
\r
3169 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3170 if (is_array($matches) && count($matches) > 0)
\r
3172 $u_domain = $matches[1];
\r
3173 $u_name = $matches[2];
\r
3174 $network = $matches[4];
\r
3175 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3176 $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
\r
3179 $re_login = false;
\r
3181 foreach (array('Allow', 'Reverse') as $list)
\r
3183 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3186 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3189 $aTickets = $this->get_passport_ticket();
\r
3190 if (!$aTickets || !is_array($aTickets)) {
\r
3191 // failed to login? ignore it
\r
3192 $this->log_message("*** can't re-login, something wrong here");
\r
3193 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3197 $this->ticket = $aTickets;
\r
3198 $this->log_message("**** get new ticket, try it again");
\r
3199 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
3201 $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
\r
3205 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
3208 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
\r
3210 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
3211 $len = strlen($str);
\r
3214 $this->log_message("*** someone add us to their list: $data");
\r
3215 $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);
\r
3220 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
3221 // NS: <<< RML 0 {size}
\r
3222 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
3223 if (is_numeric($size) && $size > 0)
\r
3225 $data = $this->ns_readdata($size);
\r
3226 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
3227 if (is_array($matches) && count($matches) > 0)
\r
3229 $u_domain = $matches[1];
\r
3230 $u_name = $matches[2];
\r
3231 $network = $matches[4];
\r
3232 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
3234 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
3235 foreach ($aData as $list => $id)
\r
3236 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
3237 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
3238 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
\r
3241 $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
\r
3242 $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);
\r
3245 $this->log_message("*** someone remove us from their list: $data");
\r
3250 // randomly, we get MSG notification from server
\r
3251 // NS: <<< MSG Hotmail Hotmail {size}
\r
3252 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
3253 if (is_numeric($size) && $size > 0) {
\r
3254 $data = $this->ns_readdata($size);
\r
3255 $aLines = @explode("\n", $data);
\r
3259 foreach ($aLines as $line) {
\r
3260 $line = rtrim($line);
\r
3262 if ($line === '') {
\r
3266 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
3267 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
\r
3268 strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
3269 // we just need text/x-msmsgsinitialmdatanotification
\r
3270 // or text/x-msmsgsoimnotification
\r
3277 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
3278 $maildata = trim(substr($line, 10));
\r
3283 $this->log_message("*** ingnore MSG for: $line");
\r
3286 if ($maildata == '') {
\r
3287 $this->log_message("*** ingnore MSG not for OIM");
\r
3290 $re_login = false;
\r
3291 if (strcasecmp($maildata, 'too-large') == 0) {
\r
3292 $this->log_message("*** large mail-data, need to get the data via SOAP");
\r
3293 $maildata = $this->getOIM_maildata();
\r
3294 if ($maildata === false) {
\r
3295 $this->log_message("*** can't get mail-data via SOAP");
\r
3296 // maybe we need to re-login again
\r
3297 $aTickets = $this->get_passport_ticket();
\r
3298 if (!$aTickets || !is_array($aTickets)) {
\r
3299 // failed to login? ignore it
\r
3300 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3304 $this->ticket = $aTickets;
\r
3305 $this->log_message("**** get new ticket, try it again");
\r
3306 $maildata = $this->getOIM_maildata();
\r
3307 if ($maildata === false) {
\r
3308 $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
\r
3313 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
3317 $start = strpos($p, '<M>');
\r
3318 $end = strpos($p, '</M>');
\r
3319 if ($start === false || $end === false || $start > $end) break;
\r
3321 $sOIM = substr($p, $start, $end - $start);
\r
3323 $p = substr($p, $end);
\r
3325 if (count($aOIMs) == 0) {
\r
3326 $this->log_message("*** ingnore empty OIM");
\r
3329 foreach ($aOIMs as $maildata) {
\r
3330 // T: 11 for MSN, 13 for Yahoo
\r
3331 // S: 6 for MSN, 7 for Yahoo
\r
3332 // RT: the datetime received by server
\r
3333 // RS: already read or not
\r
3334 // SZ: size of message
\r
3337 // F: always 00000000-0000-0000-0000-000000000009
\r
3338 // N: sender alias
\r
3339 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
3340 if (count($matches) == 0) {
\r
3341 $this->log_message("*** ingnore OIM maildata without <T>type</T>");
\r
3344 $oim_type = $matches[1];
\r
3345 if ($oim_type = 13)
\r
3349 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
3350 if (count($matches) == 0) {
\r
3351 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
\r
3354 $oim_sender = $matches[1];
\r
3355 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
3356 if (count($matches) == 0) {
\r
3357 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
\r
3360 $oim_msgid = $matches[1];
\r
3361 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
3362 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
3363 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
3364 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
3365 $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
3366 $sMsg = $this->getOIM_message($oim_msgid);
\r
3367 if ($sMsg === false) {
\r
3368 $this->log_message("*** can't get OIM, msgid = $oim_msgid");
\r
3370 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3373 $aTickets = $this->get_passport_ticket();
\r
3374 if (!$aTickets || !is_array($aTickets)) {
\r
3375 // failed to login? ignore it
\r
3376 $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
\r
3380 $this->ticket = $aTickets;
\r
3381 $this->log_message("**** get new ticket, try it again");
\r
3382 $sMsg = $this->getOIM_message($oim_msgid);
\r
3383 if ($sMsg === false) {
\r
3384 $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
\r
3388 $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
3390 //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);
\r
3391 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
3397 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
3398 // NS: <<< UBM {email} $network $type {size}
\r
3399 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
3400 if (is_numeric($size) && $size > 0)
\r
3402 $data = $this->ns_readdata($size);
\r
3403 $aLines = @explode("\n", $data);
\r
3407 foreach ($aLines as $line) {
\r
3408 $line = rtrim($line);
\r
3410 if ($line === '') {
\r
3414 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
3420 $aSubLines = @explode("\r", $line);
\r
3421 foreach ($aSubLines as $str) {
\r
3429 $this->log_message("*** ingnore from $from_email: $line");
\r
3432 $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
\r
3433 //$this->ReceivedMessage($from_email,$sMsg,$network,false);
\r
3434 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
3439 // randomly, we get UBX notification from server
\r
3440 // NS: <<< UBX email {network} {size}
\r
3441 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
3442 // we don't need the notification data, so just ignore it
\r
3443 if (is_numeric($size) && $size > 0)
\r
3444 $this->ns_readdata($size);
\r
3448 // randomly, we'll get challenge from server
\r
3449 // NS: <<< CHL 0 {code}
\r
3450 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
3451 $fingerprint = $this->getChallenge($chl_code);
\r
3452 // NS: >>> QRY {id} {product_id} 32
\r
3453 // NS: >>> fingerprint
\r
3454 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
3455 $this->ns_writedata($fingerprint);
\r
3456 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3457 if($this->PhotoStickerFile!==false)
\r
3458 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3461 // NS: <<< CHG {id} {status} {code}
\r
3463 // change our status to online first
\r
3467 // sometimes, NS will redirect to another NS
\r
3469 // NS: <<< XFR {id} NS {server} 0 {server}
\r
3471 // NS: <<< XFR {id} NS {server} U D
\r
3472 // for normal switchboard XFR
\r
3473 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
3474 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
3475 @list($ip, $port) = @explode(':', $server);
\r
3476 if ($server_type != 'SB') {
\r
3478 // this connection will close after XFR
\r
3479 $this->NSLogout();
\r
3482 if(count($this->MessageQueue))
\r
3484 foreach($this->MessageQueue as $User => $Message)
\r
3486 //$this->ChildProcess[$ChildPid]
\r
3487 $this->log_message("*** XFR SB $User");
\r
3488 $pid=pcntl_fork();
\r
3492 $this->ChildProcess[$pid]=$User;
\r
3497 $this->log_message("*** Fork Error $User");
\r
3503 $this->log_message("*** Child Process Start for $User");
\r
3504 unset($Message['XFRSent']);
\r
3505 unset($Message['ReqTime']);
\r
3506 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
3507 if ($bSBresult === false)
\r
3509 // error for switchboard
\r
3510 $this->log_message("!!! error for sending message to ".$User);
\r
3515 unset($this->MessageQueue[$User]);
\r
3518 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
3519 if ($bSBresult === false) {
\r
3520 // error for switchboard
\r
3521 $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
3522 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
3526 // NS: <<< QNG {time}
\r
3527 @list(/* QNG */, $ping_wait) = @explode(' ', $data);
\r
3528 //if ($this->ping_wait == 0) $this->ping_wait = 50;
\r
3529 //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
\r
3530 //Mod by Ricky Set Online
\r
3532 $this->callHandler('Pong', $ping_wait);
\r
3536 if($this->PhotoStickerFile!==false)
\r
3537 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
3539 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
3540 // someone is trying to talk to us
\r
3541 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
3542 $this->log_message("NS: <<< RNG $data");
\r
3543 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
3544 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
3545 if($this->IsIgnoreMail($email))
\r
3547 $this->log_message("*** Ignore RNG from $email");
\r
3550 $this->log_message("*** RING from $email, $sb_ip:$sb_port");
\r
3551 $this->addContact($email,1,$email, true);
\r
3552 $pid=pcntl_fork();
\r
3556 $this->ChildProcess[$pid]='RNG';
\r
3561 $this->log_message("*** Fork Error $User");
\r
3567 $this->log_message("*** Ring Child Process Start for $User");
\r
3568 $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);
\r
3573 // force logout from NS
\r
3574 // NS: <<< OUT xxx
\r
3575 $this->log_message("*** LOGOUT from NS");
\r
3576 return $this->NsLogout();
\r
3579 $code = substr($data,0,3);
\r
3580 if (is_numeric($code)) {
\r
3581 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
3582 $this->debug_message("*** NS: $this->error");
\r
3584 return $this->NsLogout();
\r
3591 public function sendMessageViaSB($message, $to) {
\r
3592 $socket = $this->switchBoardSessions[$to]['socket'];
\r
3593 $lastActive = $this->switchBoardSessions[$to]['lastActive'];
\r
3594 $joined = $this->switchBoardSessions[$to]['joined'];
\r
3596 //FIXME Probably not needed (we're not running in a loop anymore)
\r
3597 /*if($this->kill_me)
\r
3599 $this->log_message("*** SB Okay, kill me now!");
\r
3600 endSBSession($socket);
\r
3604 // If our participant has not joined the session yet we can't message them!
\r
3605 //TODO Check the behaviour of the queue runner when we return false
\r
3609 $aMessage = $this->getMessage($Message);
\r
3611 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
3612 if($MsnObjDefine !== '')
\r
3614 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
3615 $len = strlen($SendString);
\r
3616 $this->SB_writeln("MSG $id N $len");
\r
3618 $this->SB_writedata($SendString);
\r
3621 $len = strlen($aMessage);
\r
3622 $this->SB_writeln("MSG $id N $len");
\r
3624 // Increment the trID
\r
3625 $this->switchBoardSessions[$to]['id']++;
\r
3627 $this->SB_writedata($aMessage);
\r
3629 if (feof($this->SBFp))
\r
3631 // lost connection? error? try OIM later
\r
3632 @fclose($this->SBFp);
\r
3633 //TODO introduce callback to add offline message to queue?
\r
3636 $this->SB_writeln("OUT");
\r
3637 @fclose($this->SBFp);
\r
3641 //FIXME Not sure if this is needed?
\r
3642 private function endSBSession($socket) {
\r
3643 if (feof($this->SBFp))
\r
3645 // lost connection? error? try OIM later
\r
3646 @fclose($this->SBFp);
\r
3649 $this->SB_writeln("OUT");
\r
3650 @fclose($this->SBFp);
\r
3654 public function sendMessage($message, $to) {
\r
3655 if($message != '') {
\r
3656 list($name,$host,$network)=explode('@',$to);
\r
3657 $network=$network==''?1:$network;
\r
3659 if($network === 1 && isset($this->switchBoardSessions[$to])) {
\r
3660 $recipient = $name . $host;
\r
3661 $this->debug_message("*** Sending Message to $recipient using existing SB session");
\r
3662 return $this->sendMessageViaSB($message, $recipient);
\r
3664 $this->debug_message("*** Not MSN network or no existing SB session");
\r
3665 //TODO implement creation of SB session etc
\r
3672 * Sends a ping command
\r
3674 * Should be called about every 50 seconds
\r
3676 public function send_ping() {
\r
3678 $this->ns_writeln("PNG");
\r
3681 public function getNSSocket() {
\r
3682 return $this->NSfp;
\r
3685 // TODO Allow for multiple SB session sockets
\r
3686 public function getSBSocket() {
\r
3687 return $this->SBfp;
\r
3690 public function getSockets() {
\r
3691 return array($this->NSfp, $this->SBfp);
\r
3695 * Calls User Handler
\r
3697 * Calls registered handler for a specific event.
\r
3699 * @param String $event Command (event) name (Rvous etc)
\r
3700 * @param String $data Raw message from server
\r
3701 * @see registerHandler
\r
3704 private function callHandler($event, $data) {
\r
3705 if (isset($this->myEventHandlers[$event])) {
\r
3706 call_user_func($this->myEventHandlers[$event], $data);
\r
3708 $this->noHandler($data);
\r
3713 * Registers a user handler
\r
3718 * @param String $event Event name
\r
3719 * @param String $handler User function to call
\r
3720 * @see callHandler
\r
3721 * @return boolean Returns true if successful
\r
3723 public function registerHandler($event, $handler) {
\r
3724 if (is_callable($handler)) {
\r
3725 $this->myEventHandlers[$event] = $handler;
\r