]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Msn/extlib/phpmsnclass/msn.class.php
ef4f45b44158ecec174c82c0605b9a879b7f21de
[quix0rs-gnu-social.git] / plugins / Msn / extlib / phpmsnclass / msn.class.php
1 <?php\r
2 /*\r
3 \r
4 MSN class ver 2.0 by Tommy Wu, Ricky Su\r
5 License: GPL\r
6 \r
7 You can find MSN protocol from this site: http://msnpiki.msnfanatic.com/index.php/Main_Page\r
8 \r
9 This class support MSNP15 for send message. The PHP module needed:\r
10 \r
11 MSNP15: curl pcre mhash mcrypt bcmath\r
12 \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
15 */\r
16 \r
17 class MSN {\r
18     private $debug;\r
19     private $timeout;\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
28     private $windows;\r
29     private $kill_me = false;\r
30     private $id;\r
31     private $ticket;\r
32     private $user = '';\r
33     private $password = '';\r
34     private $NSfp=false;\r
35     private $SBfp;\r
36     private $passport_policy = '';\r
37     private $alias;\r
38     private $psm;\r
39     private $use_ping;\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
50     private $LastPing;\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
61     private $ABService;\r
62     private $Contacts;\r
63     private $IgnoreList;\r
64 \r
65     public $server = 'messenger.hotmail.com';\r
66     public $port = 1863;\r
67 \r
68 \r
69     public $clientid = '';\r
70 \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
77 \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
80 \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
83 \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
86 \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
89 \r
90 \r
91     public $error = '';\r
92 \r
93     public $authed = false;\r
94 \r
95     public $oim_try = 3;\r
96 \r
97     public $log_file = '';\r
98 \r
99     public $log_path = false;\r
100 \r
101     public $font_fn = 'Arial';\r
102     public $font_co = '333333';\r
103     public $font_ef = '';\r
104 \r
105 \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
111     \r
112     // Begin added for StatusNet\r
113     \r
114     private $aContactList = array();\r
115     private $switchBoardSessions = array();\r
116     \r
117     /**\r
118     * Event Handler Functions\r
119     */\r
120     private $myEventHandlers = array();\r
121     \r
122     // End added for StatusNet\r
123 \r
124     private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)\r
125     {\r
126         $ArrayString='';\r
127         foreach($Array as $Key => $Val)\r
128         {\r
129             if($Key{0}==':') continue;\r
130             $Attrib='';\r
131             if(is_array($Val[':']))\r
132             {\r
133                 foreach($Val[':'] as $AttribName => $AttribVal)\r
134                 $Attrib.=" $AttribName='$AttribVal'";\r
135             }\r
136             if($Key{0}=='!')\r
137             {\r
138                 //List Type Define\r
139                 $Key=substr($Key,1);\r
140                 foreach($Val as $ListKey => $ListVal)\r
141                 {\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
146                 }\r
147                 continue;\r
148             }\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
152         }\r
153         if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);\r
154         return $ArrayString;\r
155     }\r
156 \r
157     public function End()\r
158     {\r
159         $this->log_message("*** someone kill me ***");\r
160         $this->kill_me=true;\r
161     }\r
162     private function IsIgnoreMail($Email)\r
163     {        \r
164         if($this->IgnoreList==false) return false;\r
165         foreach($this->IgnoreList as $Pattern)\r
166         {\r
167             if(preg_match($Pattern,$Email)) return true;\r
168         }\r
169         return false;\r
170     }\r
171     public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)\r
172     {\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=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;\r
184         $this->IgnoreList=isset($Configs['IgnoreList'])?$Configs['IgnoreList']:false;\r
185         if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)\r
186         {\r
187             foreach($this->Emotions as $EmotionFilePath)\r
188             $this->MsnObj($EmotionFilePath,$Type=2);\r
189         }        \r
190         $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;\r
191         $this->timeout = $timeout;\r
192         // check support\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
196 \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
199 \r
200         /*\r
201          http://msnpiki.msnfanatic.com/index.php/Client_ID\r
202          Client ID for MSN:\r
203          normal MSN 8.1 clientid is:\r
204          01110110 01001100 11000000 00101100\r
205          = 0x764CC02C\r
206 \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
212          = 0x7000800C;\r
213          */\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
217     }\r
218 \r
219     private function get_passport_ticket($url = '')\r
220     {\r
221         $user = $this->user;\r
222         $password = htmlspecialchars($this->password);\r
223 \r
224         if ($url === '')\r
225         $passport_url = $this->passport_url;\r
226         else\r
227         $passport_url = $url;\r
228 \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
238 <Header>\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
245   </ps:AuthInfo>\r
246   <wsse:Security>\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
251   </wsse:Security>\r
252 </Header>\r
253 <Body>\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
257       <wsp:AppliesTo>\r
258         <wsa:EndpointReference>\r
259           <wsa:Address>http://Passport.NET/tb</wsa:Address>\r
260         </wsa:EndpointReference>\r
261       </wsp:AppliesTo>\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
265       <wsp:AppliesTo>\r
266         <wsa:EndpointReference>\r
267           <wsa:Address>messengerclear.live.com</wsa:Address>\r
268         </wsa:EndpointReference>\r
269       </wsp:AppliesTo>\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
274       <wsp:AppliesTo>\r
275         <wsa:EndpointReference>\r
276           <wsa:Address>messenger.msn.com</wsa:Address>\r
277         </wsa:EndpointReference>\r
278       </wsp:AppliesTo>\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
283       <wsp:AppliesTo>\r
284         <wsa:EndpointReference>\r
285           <wsa:Address>contacts.msn.com</wsa:Address>\r
286         </wsa:EndpointReference>\r
287       </wsp:AppliesTo>\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
292       <wsp:AppliesTo>\r
293         <wsa:EndpointReference>\r
294           <wsa:Address>messengersecure.live.com</wsa:Address>\r
295         </wsa:EndpointReference>\r
296       </wsp:AppliesTo>\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
301       <wsp:AppliesTo>\r
302         <wsa:EndpointReference>\r
303           <wsa:Address>spaces.live.com</wsa:Address>\r
304         </wsa:EndpointReference>\r
305       </wsp:AppliesTo>\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
310       <wsp:AppliesTo>\r
311         <wsa:EndpointReference>\r
312           <wsa:Address>storage.msn.com</wsa:Address>\r
313         </wsa:EndpointReference>\r
314       </wsp:AppliesTo>\r
315       <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
316     </wst:RequestSecurityToken>\r
317   </ps:RequestMultipleSecurityTokens>\r
318 </Body>\r
319 </Envelope>';\r
320 \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
333         curl_close($curl);\r
334         $this->debug_message("*** Get Result:\n$data");\r
335 \r
336         if ($http_code != 200) {\r
337             // sometimes, rediret to another URL\r
338             // MSNP15\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
344                 return false;\r
345             }\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
349                 return false;\r
350             }\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
354                 return false;\r
355             }\r
356             $this->debug_message("*** redirect to $redirect_url");\r
357             return $this->get_passport_ticket($redirect_url);\r
358         }\r
359 \r
360         // sometimes, rediret to another URL, also return 200\r
361         // MSNP15\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
371                     return false;\r
372                 }\r
373                 $this->debug_message("*** redirect to $redirect_url");\r
374                 return $this->get_passport_ticket($redirect_url);\r
375             }\r
376         }\r
377 \r
378         // no Redurect faultcode or URL\r
379         // we should get the ticket here\r
380 \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
395         preg_match("#".\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
403             "#",\r
404         $data, $matches);\r
405 \r
406         // no ticket found!\r
407         if (count($matches) == 0) {\r
408             $this->debug_message("*** Can't get passport ticket!");\r
409             return false;\r
410         }\r
411 \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
415         // matches[2]: ...\r
416         // matches[3]: RST1 (messengerclear.live.com) binary secret\r
417         // matches[4]: ...\r
418         // matches[5]: RST2 (messenger.msn.com) ticket\r
419         // matches[6]: ...\r
420         // matches[7]: RST3 (contacts.msn.com) ticket\r
421         // matches[8]: ...\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
428 \r
429         // so\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
437 \r
438         // yes, we get ticket\r
439         $aTickets = array(\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
447         );        \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
455         )\r
456         );\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
460         return $aTickets;\r
461     }\r
462     private function UpdateContacts()\r
463     {\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
470   )\r
471   );\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
477     'abView'=>'Full',\r
478     'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',\r
479   )\r
480   );\r
481   $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');\r
482   $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));\r
483   $this->Contacts=array();\r
484   try\r
485   {\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
491   }\r
492   catch(Exception $e)\r
493   {\r
494       $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
495   }\r
496     }\r
497     protected function addContact($email, $network, $display = '', $sendADL = false)\r
498     {\r
499         if ($network != 1) return true;\r
500         if(isset($this->Contacts[$email])) return true;\r
501 \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
506     'contacts'=>array(\r
507      'Contact'=>array(\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
515         )\r
516         )\r
517         )\r
518         ),\r
519     'options'=>array(\r
520      'EnableAllowListManagement'=>true\r
521         )\r
522         )\r
523         );\r
524         $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');\r
525         try\r
526         {\r
527             $this->debug_message("*** Add Contacts $email...");\r
528             $this->ABService->ABContactAdd($ABContactAdd);\r
529         }\r
530         catch(Exception $e)\r
531         {\r
532             $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
533         }\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
542             }\r
543         }\r
544         $this->UpdateContacts();\r
545         return true;\r
546     }\r
547 \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
551         $user = $email;\r
552         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
553         if ($network == 1)\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
559 <soap:Header>\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
568     </ABAuthHeader>\r
569 </soap:Header>\r
570 <soap:Body>\r
571     <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">\r
572         <serviceHandle>\r
573             <Id>0</Id>\r
574             <Type>Messenger</Type>\r
575             <ForeignId></ForeignId>\r
576         </serviceHandle>\r
577         <memberships>\r
578             <Membership>\r
579                 <MemberRole>'.$list.'</MemberRole>\r
580                 <Members>\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
585                     </Member>\r
586                 </Members>\r
587             </Membership>\r
588         </memberships>\r
589     </DeleteMember>\r
590 </soap:Body>\r
591 </soap:Envelope>';\r
592         else\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
598 <soap:Header>\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
607     </ABAuthHeader>\r
608 </soap:Header>\r
609 <soap:Body>\r
610     <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">\r
611         <serviceHandle>\r
612             <Id>0</Id>\r
613             <Type>Messenger</Type>\r
614             <ForeignId></ForeignId>\r
615         </serviceHandle>\r
616         <memberships>\r
617             <Membership>\r
618                 <MemberRole>'.$list.'</MemberRole>\r
619                 <Members>\r
620                     <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
621                         <Type>Email</Type>\r
622                         <MembershipId>'.$memberID.'</MembershipId>\r
623                         <State>Accepted</State>\r
624                     </Member>\r
625                 </Members>\r
626             </Membership>\r
627         </memberships>\r
628     </DeleteMember>\r
629 </soap:Body>\r
630 </soap:Envelope>';\r
631 \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
636             );\r
637 \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
651             curl_close($curl);\r
652             $this->debug_message("*** Get Result:\n$data");\r
653 \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
658                     return false;\r
659                 }\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
664                     return false;\r
665                 }\r
666                 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
667                 return true;\r
668             }\r
669             $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");\r
670             return true;\r
671     }\r
672 \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
676         $user = $email;\r
677 \r
678         if ($network == 1)\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
684 <soap:Header>\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
693     </ABAuthHeader>\r
694 </soap:Header>\r
695 <soap:Body>\r
696     <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
697         <serviceHandle>\r
698             <Id>0</Id>\r
699             <Type>Messenger</Type>\r
700             <ForeignId></ForeignId>\r
701         </serviceHandle>\r
702         <memberships>\r
703             <Membership>\r
704                 <MemberRole>'.$list.'</MemberRole>\r
705                 <Members>\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
710                     </Member>\r
711                 </Members>\r
712             </Membership>\r
713         </memberships>\r
714     </AddMember>\r
715 </soap:Body>\r
716 </soap:Envelope>';\r
717         else\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
723 <soap:Header>\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
732     </ABAuthHeader>\r
733 </soap:Header>\r
734 <soap:Body>\r
735     <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
736         <serviceHandle>\r
737             <Id>0</Id>\r
738             <Type>Messenger</Type>\r
739             <ForeignId></ForeignId>\r
740         </serviceHandle>\r
741         <memberships>\r
742             <Membership>\r
743                 <MemberRole>'.$list.'</MemberRole>\r
744                 <Members>\r
745                     <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
746                         <Type>Email</Type>\r
747                         <State>Accepted</State>\r
748                         <Email>'.$user.'</Email>\r
749                         <Annotations>\r
750                             <Annotation>\r
751                                 <Name>MSN.IM.BuddyType</Name>\r
752                                 <Value>32:YAHOO</Value>\r
753                             </Annotation>\r
754                         </Annotations>\r
755                     </Member>\r
756                 </Members>\r
757             </Membership>\r
758         </memberships>\r
759     </AddMember>\r
760 </soap:Body>\r
761 </soap:Envelope>';\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
766             );\r
767 \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
781             curl_close($curl);\r
782             $this->debug_message("*** Get Result:\n$data");\r
783 \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
788                     return false;\r
789                 }\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
794                     return false;\r
795                 }\r
796                 $this->log_message("*** add member (network: $network) $email to $list, already exist!");\r
797                 return true;\r
798             }\r
799             $this->log_message("*** add member (network: $network) $email to $list");\r
800             return true;\r
801     }\r
802 \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
810 <soap:Header>\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
819     </ABAuthHeader>\r
820 </soap:Header>\r
821 <soap:Body>\r
822     <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">\r
823         <serviceFilter>\r
824             <Types>\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
830             </Types>\r
831         </serviceFilter>\r
832     </FindMembership>\r
833 </soap:Body>\r
834 </soap:Envelope>';\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
839             );\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
853             curl_close($curl);\r
854             $this->debug_message("*** Get Result:\n$data");\r
855             if($http_code != 200) return array();\r
856             $p = $data;\r
857             $aMemberships = array();\r
858             while (1) {\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
864                 $end += 13;\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
869             }\r
870             //$this->debug_message("aMemberships = ".var_export($aMemberships, true));\r
871 \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
881                 $p = $sMembership;\r
882                 if (isset($aMembers)) unset($aMembers);\r
883                 $aMembers = array();\r
884                 while (1) {\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
890                     $end += 9;\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
895                 }\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
904                     $network = -1;\r
905                     preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);\r
906                     if (count($matches) == 0) continue;\r
907                     $id = $matches[1];\r
908                     if ($sMemberType == 'PassportMember') {\r
909                         if (strpos($sMember, '<Type>Passport</Type>') === false) continue;\r
910                         $network = 1;\r
911                         preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);\r
912                     }\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
919                         $network = 32;\r
920                         preg_match('#<Email>(.*)</Email>#', $sMember, $matches);\r
921                     }\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
929                     }\r
930                 }\r
931             }\r
932             return $aContactList;\r
933     }\r
934 \r
935     private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {\r
936         $this->id = 1;\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
941                 return false;\r
942             }\r
943         }\r
944         else {\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
948                 return false;\r
949             }\r
950         }\r
951 \r
952         stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
953         $this->authed = false;\r
954         // MSNP9\r
955         // NS: >> VER {id} MSNP9 CVR0\r
956         // MSNP15\r
957         // NS: >>> VER {id} MSNP15 CVR0\r
958         $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
959 \r
960         $start_tm = time();\r
961         while (!feof($this->NSfp))\r
962         {\r
963             $data = $this->ns_readln();\r
964             // no data?\r
965             if ($data === false) {\r
966                 if ($this->timeout > 0) {\r
967                     $now_tm = time();\r
968                     $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;\r
969                     if ($used_time > $this->timeout) {\r
970                         // logout now\r
971                         // NS: >>> OUT\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
976                         return false;\r
977                     }\r
978                 }\r
979                 continue;\r
980             }\r
981             $code = substr($data, 0, 3);\r
982             $start_tm = time();\r
983 \r
984             switch ($code) {\r
985                 case 'VER':\r
986                     // MSNP9\r
987                     // NS: <<< VER {id} MSNP9 CVR0\r
988                     // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}\r
989                     // MSNP15\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
993                     break;\r
994 \r
995                 case 'CVR':\r
996                     // MSNP9\r
997                     // NS: <<< CVR {id} {ver_list} {download_serve} ....\r
998                     // NS: >>> USR {id} TWN I {user}\r
999                     // MSNP15\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
1003                     break;\r
1004 \r
1005                 case 'USR':\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
1012 \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
1017 \r
1018                     $this->passport_policy = $policy;\r
1019                     $aTickets = $this->get_passport_ticket();\r
1020                     if (!$aTickets || !is_array($aTickets)) {\r
1021                         // logout now\r
1022                         // NS: >>> OUT\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
1027                         return false;\r
1028                     }\r
1029 \r
1030                     $ticket = $aTickets['ticket'];\r
1031                     $secret = $aTickets['secret'];\r
1032                     $this->ticket = $aTickets;\r
1033                     $login_code = $this->generateLoginBLOB($secret, $nonce);\r
1034 \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
1038                     break;\r
1039 \r
1040                 case 'XFR':\r
1041                     // main login server will redirect to anther NS after USR command\r
1042                     // MSNP9\r
1043                     // NS: <<< XFR {id} NS {server} 0 {server}\r
1044                     // MSNP15\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
1051 \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
1056                         return false;\r
1057                     }\r
1058 \r
1059                     stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
1060                     // MSNP9\r
1061                     // NS: >> VER {id} MSNP9 CVR0\r
1062                     // MSNP15\r
1063                     // NS: >>> VER {id} MSNP15 CVR0\r
1064                     $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
1065                     break;\r
1066 \r
1067                 case 'GCF':\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
1074                     break;\r
1075 \r
1076                 default:\r
1077                     // we'll quit if got any error\r
1078                     if (is_numeric($code)) {\r
1079                         // logout now\r
1080                         // NS: >>> OUT\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
1085                         return false;\r
1086                     }\r
1087                     // unknown response from server, just ignore it\r
1088                     break;\r
1089             }\r
1090         }\r
1091         // never goto here\r
1092     }\r
1093 \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
1100     }\r
1101 \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
1106 \r
1107         // get hash of challenge using key2\r
1108         $hash = mhash(MHASH_SHA1, $challenge, $key2);\r
1109 \r
1110         // get 8 bytes random data\r
1111         $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);\r
1112 \r
1113         $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);\r
1114 \r
1115         $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);\r
1116         $blob .= $iv;\r
1117         $blob .= $hash;\r
1118         $blob .= $cipher;\r
1119 \r
1120         return base64_encode($blob);\r
1121     }\r
1122 \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
1127             return false;\r
1128         }\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
1135 <soap:Header>\r
1136   <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1137     <t>'.$t.'</t>\r
1138     <p>'.$p.'</p>\r
1139   </PassportCookie>\r
1140 </soap:Header>\r
1141 <soap:Body>\r
1142   <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />\r
1143 </soap:Body>\r
1144 </soap:Envelope>';\r
1145 \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
1150             );\r
1151 \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
1167 \r
1168             if ($http_code != 200) {\r
1169                 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");\r
1170                 return false;\r
1171             }\r
1172 \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
1177                 return '';\r
1178             }\r
1179             return $matches[2];\r
1180     }\r
1181 \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
1186             return false;\r
1187         }\r
1188         $t = htmlspecialchars($matches[1]);\r
1189         $p = htmlspecialchars($matches[2]);\r
1190 \r
1191         // read OIM\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
1196 <soap:Header>\r
1197   <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1198     <t>'.$t.'</t>\r
1199     <p>'.$p.'</p>\r
1200   </PassportCookie>\r
1201 </soap:Header>\r
1202 <soap:Body>\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
1206   </GetMessage>\r
1207 </soap:Body>\r
1208 </soap:Envelope>';\r
1209 \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
1214             );\r
1215 \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
1231 \r
1232             if ($http_code != 200) {\r
1233                 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");\r
1234                 return false;\r
1235             }\r
1236 \r
1237             // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?\r
1238             // multi-lines?\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
1243                 return false;\r
1244             }\r
1245             $lines = substr($data, $start + 18, $end - $start);\r
1246             $aLines = @explode("\n", $lines);\r
1247             $header = true;\r
1248             $ignore = false;\r
1249             $sOIM = '';\r
1250             foreach ($aLines as $line) {\r
1251                 $line = rtrim($line);\r
1252                 if ($header) {\r
1253                     if ($line === '') {\r
1254                         $header = false;\r
1255                         continue;\r
1256                     }\r
1257                     continue;\r
1258                 }\r
1259                 // stop at empty lines\r
1260                 if ($line === '') break;\r
1261                 $sOIM .= $line;\r
1262             }\r
1263             $sMsg = base64_decode($sOIM);\r
1264             $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
1265 \r
1266             // delete OIM\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
1271 <soap:Header>\r
1272   <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1273     <t>'.$t.'</t>\r
1274     <p>'.$p.'</p>\r
1275   </PassportCookie>\r
1276 </soap:Header>\r
1277 <soap:Body>\r
1278   <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1279     <messageIds>\r
1280       <messageId>'.$msgid.'</messageId>\r
1281     </messageIds>\r
1282   </DeleteMessages>\r
1283 </soap:Body>\r
1284 </soap:Envelope>';\r
1285 \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
1290             );\r
1291 \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
1307 \r
1308             if ($http_code != 200)\r
1309             $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");\r
1310             else\r
1311             $this->debug_message("*** OIM ($msgid) deleted");\r
1312             return $sMsg;\r
1313     }\r
1314     private function NSLogout() {\r
1315         if (is_resource($this->NSfp) && !feof($this->NSfp)) {\r
1316             // logout now\r
1317             // NS: >>> OUT\r
1318             $this->ns_writeln("OUT");\r
1319             fclose($this->NSfp);\r
1320             $this->NSfp = false;\r
1321             $this->log_message("*** logout now!");\r
1322         }\r
1323 \r
1324     }\r
1325     private function NSRetryWait($Wait) {\r
1326         $this->log_message("*** wait for $Wait seconds");\r
1327         for($i=0;$i<$Wait;$i++) {\r
1328             sleep(1);\r
1329             if($this->kill_me) return false;\r
1330         }\r
1331         return true;\r
1332     }\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
1336         clearstatcache();\r
1337         foreach ($aFiles as $filename) {\r
1338             $fp = fopen($filename, 'rt');\r
1339             if (!$fp) continue;\r
1340             $aTo = array();\r
1341             $sMessage = '';\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
1346             }\r
1347             fclose($fp);\r
1348             if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')\r
1349             $this->log_message("!!! message format error? delete $filename");\r
1350             else\r
1351             {\r
1352                 foreach($aTo as $To)\r
1353                 {\r
1354                     @list($user, $domain, $network) = @explode('@', $To);\r
1355                     $MessageList[$network]["$user@$domain"]=$sMessage;\r
1356                 }\r
1357             }\r
1358             if($this->backup_file)\r
1359             {\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
1365             }\r
1366             else @unlink($filename);\r
1367         }\r
1368         foreach ($MessageList as $network => $Messages)\r
1369         {\r
1370             switch(trim($network))\r
1371             {\r
1372                 case '':\r
1373                 case 1:   //MSN\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
1379                     break;\r
1380                 case 'Offline':  //MSN\r
1381                     //Send OIM\r
1382                     //FIXME: 修正Send OIM\r
1383                     foreach($Messages as $To => $Message)\r
1384                     {\r
1385                         $lockkey='';\r
1386                         for ($i = 0; $i < $this->oim_try; $i++)\r
1387                         {\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
1393                                 continue;\r
1394                             }\r
1395                             if ($oim_result === false || $oim_result['auth_policy'] !== false)\r
1396                             {\r
1397                                 if ($re_login)\r
1398                                 {\r
1399                                     $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");\r
1400                                     break;\r
1401                                 }\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
1405                                 {\r
1406                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
1407                                     break;\r
1408                                 }\r
1409                                 $this->log_message("**** get new ticket, try it again");\r
1410                                 continue;\r
1411                             }\r
1412                         }\r
1413                     }\r
1414                     break;\r
1415                 default:  //Other\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
1422                     }\r
1423             }\r
1424         }\r
1425         if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))\r
1426         {\r
1427             $this->MessageQueue[$User]['XFRSent']=false;\r
1428             $this->MessageQueue[$User]['ReqTime']=false;\r
1429         }\r
1430         return true;\r
1431     }\r
1432     public function SignalFunction($signal)\r
1433     {\r
1434         switch($signal)\r
1435         {\r
1436             case SIGTRAP:\r
1437             case SIGTERM:\r
1438             case SIGHUP:\r
1439                 $this->End();\r
1440                 return;\r
1441             case SIGCHLD:\r
1442                 $ChildPid=pcntl_wait($status,WUNTRACED);\r
1443                 if($ChildPid>0)\r
1444                 {\r
1445                     $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);\r
1446                     unset($this->ChildProcess[$ChildPid]);\r
1447                 }\r
1448                 return;\r
1449         }\r
1450     }\r
1451 \r
1452     public function Run()\r
1453     {\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
1459         $sent = false;\r
1460         $aADL = array();\r
1461         $aContactList = array();\r
1462         while (true)\r
1463         {\r
1464             if($this->kill_me)\r
1465             {\r
1466                 $this->log_message("*** Okay, kill me now!");\r
1467                 return $this->NSLogout();\r
1468             }\r
1469             if (!is_resource($this->NSfp) || feof($this->NSfp))\r
1470             {\r
1471                 $this->log_message("*** try to connect to MSN network");\r
1472                 if (!$this->connect($this->user, $this->password))\r
1473                 {\r
1474                     $this->log_message("!!! Can't connect to server: $this->error");\r
1475                     if(!$this->NSRetryWait($this->retry_wait)) continue;\r
1476                 }\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
1491                                             // pending list\r
1492                                             $cnt = 0;\r
1493                                             foreach (array('Allow', 'Reverse') as $list) {\r
1494                                                 if (isset($aData[$list]))\r
1495                                                 $cnt++;\r
1496                                                 else {\r
1497                                                     if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
1498                                                         $aContactList[$u_domain][$u_name][$network][$list] = false;\r
1499                                                         $cnt++;\r
1500                                                     }\r
1501                                                 }\r
1502                                             }\r
1503                                             if ($cnt >= 2) {\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
1508                                             }\r
1509                                         }\r
1510                                         else {\r
1511                                             // sync list\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
1516                                                 }\r
1517                                             }\r
1518                                         }\r
1519                                     }\r
1520                                 }\r
1521                             }\r
1522                         }\r
1523                     }\r
1524                     $n = 0;\r
1525                     $sList = '';\r
1526                     $len = 0;\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
1533                                 $n++;\r
1534                                 $sList = '';\r
1535                                 $len = strlen($str);\r
1536                             }\r
1537                             $sList .= $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
1543                                     // so we use 7475\r
1544                                     if ($len > 7475) {\r
1545                                         $sList .= '</d>';\r
1546                                         $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
1547                                         $n++;\r
1548                                         $sList = '<d n="'.$u_domain.'">'.$str;\r
1549                                         $len = strlen($sList);\r
1550                                     }\r
1551                                     else\r
1552                                     $sList .= $str;\r
1553                                 }\r
1554                             }\r
1555                             $sList .= '</d>';\r
1556                         }\r
1557                     }\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
1566                     }\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
1571                     //設定個人大頭貼\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
1582             }\r
1583             $data = $this->ns_readln();\r
1584             if($data===false)\r
1585             {\r
1586                 //If No NS Message Process SendMessageFileQueue\r
1587                 if (time()-$this->LastPing > $this->ping_wait)\r
1588                 {\r
1589                     // NS: >>> PNG\r
1590                     $this->ns_writeln("PNG");\r
1591                     $this->LastPing = time();\r
1592                 }\r
1593                 if(count($this->ChildProcess)<$this->MAXChildProcess)\r
1594                 {\r
1595                     $Index=0;\r
1596                     foreach($this->MessageQueue as $User => $Message)\r
1597                     {\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
1601                         {\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
1606                             $Index++;\r
1607                         }\r
1608                     }\r
1609                 }\r
1610                 if($this->ProcessSendMessageFileQueue()) continue;\r
1611                 break;\r
1612             }\r
1613             switch (substr($data,0,3))\r
1614             {\r
1615                 case 'SBS':\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
1618                     break;\r
1619 \r
1620                 case 'RFS':\r
1621                     // FIXME:\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
1630                         }\r
1631                     }\r
1632                     break;\r
1633 \r
1634                 case 'LST':\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
1641                     }\r
1642                     break;\r
1643 \r
1644                 case 'ADL':\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
1649                     {\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
1653                         {\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
1659                             else\r
1660                             {\r
1661                                 $re_login = false;\r
1662                                 $cnt = 0;\r
1663                                 foreach (array('Allow', 'Reverse') as $list)\r
1664                                 {\r
1665                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
1666                                     {\r
1667                                         if ($re_login) {\r
1668                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
1669                                             continue;\r
1670                                         }\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
1676                                             continue;\r
1677                                         }\r
1678                                         $re_login = true;\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
1682                                         {\r
1683                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
1684                                             continue;\r
1685                                         }\r
1686                                     }\r
1687                                     $aContactList[$u_domain][$u_name][$network][$list] = false;\r
1688                                     $cnt++;\r
1689                                 }\r
1690                                 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
1691                             }\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
1694                         }\r
1695                         else\r
1696                         $this->log_message("*** someone add us to their list: $data");\r
1697                         $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
1698                     }\r
1699                     break;\r
1700 \r
1701                 case 'RML':\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
1706                     {\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
1710                         {\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
1715                             {\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
1721                             }\r
1722                             else\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
1725                         }\r
1726                         else\r
1727                         $this->log_message("*** someone remove us from their list: $data");\r
1728                     }\r
1729                     break;\r
1730 \r
1731                 case 'MSG':\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
1738                         $header = true;\r
1739                         $ignore = false;\r
1740                         $maildata = '';\r
1741                         foreach ($aLines as $line) {\r
1742                             $line = rtrim($line);\r
1743                             if ($header) {\r
1744                                 if ($line === '') {\r
1745                                     $header = false;\r
1746                                     continue;\r
1747                                 }\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
1753                                         $ignore = true;\r
1754                                         break;\r
1755                                     }\r
1756                                 }\r
1757                                 continue;\r
1758                             }\r
1759                             if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
1760                                 $maildata = trim(substr($line, 10));\r
1761                                 break;\r
1762                             }\r
1763                         }\r
1764                         if ($ignore) {\r
1765                             $this->log_message("*** ingnore MSG for: $line");\r
1766                             break;\r
1767                         }\r
1768                         if ($maildata == '') {\r
1769                             $this->log_message("*** ingnore MSG not for OIM");\r
1770                             break;\r
1771                         }\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
1783                                     break;\r
1784                                 }\r
1785                                 $re_login = true;\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
1791                                     break;\r
1792                                 }\r
1793                             }\r
1794                         }\r
1795                         // could be a lots of <M>...</M>, so we can't use preg_match here\r
1796                         $p = $maildata;\r
1797                         $aOIMs = array();\r
1798                         while (1) {\r
1799                             $start = strpos($p, '<M>');\r
1800                             $end = strpos($p, '</M>');\r
1801                             if ($start === false || $end === false || $start > $end) break;\r
1802                             $end += 4;\r
1803                             $sOIM = substr($p, $start, $end - $start);\r
1804                             $aOIMs[] = $sOIM;\r
1805                             $p = substr($p, $end);\r
1806                         }\r
1807                         if (count($aOIMs) == 0) {\r
1808                             $this->log_message("*** ingnore empty OIM");\r
1809                             break;\r
1810                         }\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
1817                             // E: sender\r
1818                             // I: msgid\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
1824                                 continue;\r
1825                             }\r
1826                             $oim_type = $matches[1];\r
1827                             if ($oim_type = 13)\r
1828                             $network = 32;\r
1829                             else\r
1830                             $network = 1;\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
1834                                 continue;\r
1835                             }\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
1840                                 continue;\r
1841                             }\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
1851                                 if ($re_login) {\r
1852                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
1853                                     continue;\r
1854                                 }\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
1859                                     continue;\r
1860                                 }\r
1861                                 $re_login = true;\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
1867                                     continue;\r
1868                                 }\r
1869                             }\r
1870                             $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
1871 \r
1872                             $this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
1873                         }\r
1874                     }\r
1875                     break;\r
1876 \r
1877                 case 'UBM':\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
1882                     {\r
1883                         $data = $this->ns_readdata($size);\r
1884                         $aLines = @explode("\n", $data);\r
1885                         $header = true;\r
1886                         $ignore = false;\r
1887                         $sMsg = '';\r
1888                         foreach ($aLines as $line) {\r
1889                             $line = rtrim($line);\r
1890                             if ($header) {\r
1891                                 if ($line === '') {\r
1892                                     $header = false;\r
1893                                     continue;\r
1894                                 }\r
1895                                 if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
1896                                     $ignore = true;\r
1897                                     break;\r
1898                                 }\r
1899                                 continue;\r
1900                             }\r
1901                             $aSubLines = @explode("\r", $line);\r
1902                             foreach ($aSubLines as $str) {\r
1903                                 if ($sMsg !== '')\r
1904                                 $sMsg .= "\n";\r
1905                                 $sMsg .= $str;\r
1906                             }\r
1907                         }\r
1908                         if($ignore)\r
1909                         {\r
1910                             $this->log_message("*** ingnore from $from_email: $line");\r
1911                             break;\r
1912                         }\r
1913                         $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
1914                         $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
1915                     }\r
1916                     break;\r
1917 \r
1918                 case 'UBX':\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
1925                     break;\r
1926 \r
1927                 case 'CHL':\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
1939                     break;\r
1940                 case 'CHG':\r
1941                     // NS: <<< CHG {id} {status} {code}\r
1942                     // ignore it\r
1943                     // change our status to online first\r
1944                     break;\r
1945 \r
1946                 case 'XFR':\r
1947                     // sometimes, NS will redirect to another NS\r
1948                     // MSNP9\r
1949                     // NS: <<< XFR {id} NS {server} 0 {server}\r
1950                     // MSNP15\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
1957                         // maybe exit?\r
1958                         // this connection will close after XFR\r
1959                         $this->NSLogout();\r
1960                         continue;\r
1961                     }\r
1962                     if(count($this->MessageQueue))\r
1963                     {\r
1964                         foreach($this->MessageQueue as $User => $Message)\r
1965                         {\r
1966                             //$this->ChildProcess[$ChildPid]\r
1967                             $this->log_message("*** XFR SB $User");\r
1968                             $pid=pcntl_fork();\r
1969                             if($pid)\r
1970                             {\r
1971                                 //Parrent Process\r
1972                                 $this->ChildProcess[$pid]=$User;\r
1973                                 break;\r
1974                             }\r
1975                             elseif($pid==-1)\r
1976                             {\r
1977                                 $this->log_message("*** Fork Error $User");\r
1978                                 break;\r
1979                             }\r
1980                             else\r
1981                             {\r
1982                                 //Child Process\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
1988                                 {\r
1989                                     // error for switchboard\r
1990                                     $this->log_message("!!! error for sending message to ".$User);\r
1991                                 }\r
1992                                 die;\r
1993                             }\r
1994                         }\r
1995                         unset($this->MessageQueue[$User]);\r
1996                     }\r
1997                     /*\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
2003                      }*/\r
2004                     break;\r
2005                 case 'QNG':\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
2011                     break;\r
2012 \r
2013                 case 'RNG':\r
2014                     if($this->PhotoStickerFile!==false)\r
2015                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
2016                     else\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
2024                     {\r
2025                         $this->log_message("*** Ignore RNG from $email");\r
2026                         break;\r
2027                     }\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
2031                     if($pid)\r
2032                     {\r
2033                         //Parrent Process\r
2034                         $this->ChildProcess[$pid]='RNG';\r
2035                         break;\r
2036                     }\r
2037                     elseif($pid==-1)\r
2038                     {\r
2039                         $this->log_message("*** Fork Error $User");\r
2040                         break;\r
2041                     }\r
2042                     else\r
2043                     {\r
2044                         //Child Process\r
2045                         $this->log_message("*** Ring Child Process Start for $User");\r
2046                         $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
2047                         die;\r
2048                     }\r
2049                     break;\r
2050                 case 'OUT':\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
2055                     break;\r
2056 \r
2057                 default:\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
2062 \r
2063                         return $this->NsLogout();\r
2064                     }\r
2065                     break;\r
2066             }\r
2067         }\r
2068         return $this->NsLogout();\r
2069     }\r
2070 \r
2071     /*public function SendMessage($Message, $To)\r
2072     {\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
2075         $To=array($To);\r
2076         $Receiver='';\r
2077         foreach($To as $Email)\r
2078         {\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
2082             {\r
2083                 $this->debug_message("*** SendMessage to $Receiver use SB message queue.");\r
2084                 array_push($this->SwitchBoardMessageQueue,$Message);\r
2085                 continue;\r
2086             }\r
2087             $Receiver.="$name@$host@$network,";\r
2088         }\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
2093     }*/\r
2094 \r
2095     function getChallenge($code)\r
2096     {\r
2097         // MSNP15\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
2105         }\r
2106 \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
2110 \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
2115         }\r
2116 \r
2117         // Step 3: The 64 bit key\r
2118         $magic_num = 0x0E79A9C1;\r
2119         $str7f = 0x7FFFFFFF;\r
2120         $high = 0;\r
2121         $low = 0;\r
2122         for ($i = 0; $i < count($aID); $i += 2) {\r
2123             $temp = $aID[$i];\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
2128 \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
2133 \r
2134             $low = bcadd(bcadd($low, $high), $temp);\r
2135         }\r
2136 \r
2137         $high = bcmod(bcadd($high, $aMD5[1]), $str7f);\r
2138         $low = bcmod(bcadd($low, $aMD5[3]), $str7f);\r
2139 \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
2146 \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
2153 \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
2156 \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
2160 \r
2161         $hash = '';\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
2166 \r
2167         return $hash;\r
2168     }\r
2169 \r
2170     private function getMessage($sMessage, $network = 1)\r
2171     {\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
2176         else\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
2181     }\r
2182     /**\r
2183      *\r
2184      * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息\r
2185      * @param $Param\r
2186      * @return boolean\r
2187      */\r
2188     private function DoSwitchBoard($Action,$Param)\r
2189     {\r
2190         $SessionEnd=false;\r
2191         $Joined=false;\r
2192         $id=1;\r
2193         $LastActive=time();\r
2194         stream_set_timeout($this->SBFp, $this->SBTimeout);\r
2195         switch($Action)\r
2196         {\r
2197             case 'Active':\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
2203                 $id++;\r
2204                 $this->SwitchBoardSessionUser=$user;\r
2205                 break;\r
2206             case 'Passive':\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
2212                 $id++;\r
2213                 $this->SwitchBoardSessionUser=$user;\r
2214                 break;\r
2215             default:\r
2216                 return false;\r
2217         }\r
2218         while((!feof($this->SBFp))&&(!$SessionEnd))\r
2219         {\r
2220             $data = $this->SB_readln();\r
2221             if($this->kill_me)\r
2222             {\r
2223                 $this->log_message("*** SB Okay, kill me now!");\r
2224                 break;\r
2225             }\r
2226             if($data === false)\r
2227             {\r
2228                 if(time()-$LastActive > $this->SBIdleTimeout)\r
2229                 {\r
2230                     $this->debug_message("*** SB Idle Timeout!");\r
2231                     break;\r
2232                 }\r
2233                 if(!$Joined) continue;\r
2234                 foreach($this->SwitchBoardMessageQueue as $Message)\r
2235                 {\r
2236                     if($Message=='') continue;\r
2237                     $aMessage = $this->getMessage($Message);\r
2238                     //CheckEmotion...\r
2239                     $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
2240                     if($MsnObjDefine!=='')\r
2241                     {\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
2245                         $id++;\r
2246                         $this->SB_writedata($SendString);\r
2247                         $this->id++;\r
2248                     }\r
2249                     $len = strlen($aMessage);\r
2250                     $this->SB_writeln("MSG $id N $len");\r
2251                     $id++;\r
2252                     $this->SB_writedata($aMessage);\r
2253                 }\r
2254                 $this->SwitchBoardMessageQueue=array();\r
2255                 if(!$this->IsIgnoreMail($user)) $LastActive = time();\r
2256                 continue;\r
2257             }\r
2258             $code = substr($data, 0, 3);\r
2259             switch($code)\r
2260             {\r
2261                 case 'IRO':\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
2265                     $Joined=true;\r
2266                     break;\r
2267                 case 'BYE':\r
2268                     $this->log_message("*** Quit for BYE");\r
2269                     $SessionEnd=true;\r
2270                     break;\r
2271                 case 'USR':\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
2277                     $id++;\r
2278                     break;\r
2279                 case 'CAL':\r
2280                     // SB: <<< CAL {id} RINGING {?}\r
2281                     // we don't need this, just ignore, and wait for other response\r
2282                     $this->id++;\r
2283                     break;\r
2284                 case 'JOI':\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
2289                     $Joined=true;\r
2290                     break;\r
2291                 case 'MSG':\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
2297                     $header = true;\r
2298                     $ignore = false;\r
2299                     $is_p2p = false;\r
2300                     $sMsg = '';\r
2301                     foreach ($aLines as $line)\r
2302                     {\r
2303                         $line = rtrim($line);\r
2304                         if ($header) {\r
2305                             if ($line === '') {\r
2306                                 $header = false;\r
2307                                 continue;\r
2308                             }\r
2309                             if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
2310                                 // typing notification, just ignore\r
2311                                 $ignore = true;\r
2312                                 break;\r
2313                             }\r
2314                             if (strncasecmp($line, 'Chunk:', 6) == 0) {\r
2315                                 // we don't handle any split message, just ignore\r
2316                                 $ignore = true;\r
2317                                 break;\r
2318                             }\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
2321                                 $is_p2p = true;\r
2322                                 $p = strstr($data, "\n\n");\r
2323                                 $sMsg = '';\r
2324                                 if ($p === false) {\r
2325                                     $p = strstr($data, "\r\n\r\n");\r
2326                                     if ($p !== false)\r
2327                                     $sMsg = substr($p, 4);\r
2328                                 }\r
2329                                 else\r
2330                                 $sMsg = substr($p, 2);\r
2331                                 break;\r
2332                             }\r
2333                             if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {\r
2334                                 // ignore all application/x-... message\r
2335                                 // for example:\r
2336                                 //      application/x-ms-ink        => ink message\r
2337                                 $ignore = true;\r
2338                                 break;\r
2339                             }\r
2340                             if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {\r
2341                                 // ignore all text/x-... message\r
2342                                 // for example:\r
2343                                 //      text/x-msnmsgr-datacast         => nudge, voice clip....\r
2344                                 //      text/x-mms-animemoticon         => customized animemotion word\r
2345                                 $ignore = true;\r
2346                                 break;\r
2347                             }\r
2348                             continue;\r
2349                         }\r
2350                         if ($sMsg !== '')\r
2351                         $sMsg .= "\n";\r
2352                         $sMsg .= $line;\r
2353                     }\r
2354                     if ($ignore)\r
2355                     {\r
2356                         $this->log_message("*** ingnore from $from_email: $line");\r
2357                         break;\r
2358                     }\r
2359                     if ($is_p2p)\r
2360                     {\r
2361                         // we will ignore any p2p message after sending acknowledgement\r
2362                         $ignore = true;\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
2371                             break;\r
2372                         }*/\r
2373                         $aDwords = @unpack("V12dword", $sMsg);\r
2374                         if (!is_array($aDwords)) {\r
2375                             $this->log_message("*** p2p: header unpack error!");\r
2376                             break;\r
2377                         }\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
2406                             break;\r
2407                         }\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
2411                         {\r
2412                             while(true)\r
2413                             {\r
2414                                 if($this->SB_readln()===false) break;\r
2415                             }\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
2422                             $new_id,\r
2423                             0, 0,\r
2424                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2425                             0,\r
2426                             2,\r
2427                             $hdr_Identifier,\r
2428                             $hdr_AckID,\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
2434                             $id++;\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
2440                             $new_id-=3;\r
2441                             //Send 200 OK message\r
2442                             $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);\r
2443                             $MessagePayload=\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
2453                             $MessageContent;\r
2454                             $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
2455                             $hdr_TotalDataSizeHigh=0;\r
2456                             $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
2457                             $new_id,\r
2458                             0, 0,\r
2459                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2460                             strlen($MessagePayload),\r
2461                             0,\r
2462                             rand(),\r
2463                             0,\r
2464                             0,0);\r
2465 \r
2466                             $message =\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
2471                             $id++;\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
2475                             \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
2481                             $new_id++;\r
2482                             $hdr = pack("LLLLLLLLLLLL",\r
2483                             $MsgBody['SessionID'],\r
2484                             $new_id,\r
2485                             0, 0,\r
2486                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2487                             $hdr_TotalDataSizeLow,\r
2488                             0,\r
2489                             rand(),\r
2490                             0,\r
2491                             0,0);\r
2492                             $message =\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
2497                             $id++;\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
2502 \r
2503                             //send Data Content..\r
2504                             $footer=pack('N',1);\r
2505                             $new_id++;\r
2506                             $FileSize=filesize($PictureFilePath);\r
2507                             if($hTitle=fopen($PictureFilePath,'rb'))\r
2508                             {\r
2509                                 $Offset=0;\r
2510                                 //$new_id++;\r
2511                                 while(!feof($hTitle))\r
2512                                 {\r
2513                                     $FileContent=fread($hTitle,1024);\r
2514                                     $FileContentSize=strlen($FileContent);\r
2515                                     $hdr = pack("LLLLLLLLLLLL",\r
2516                                     $MsgBody['SessionID'],\r
2517                                     $new_id,\r
2518                                     $Offset, 0,\r
2519                                     $FileSize,0,\r
2520                                     $FileContentSize,\r
2521                                     0x20,\r
2522                                     rand(),\r
2523                                     0,\r
2524                                     0,0\r
2525                                     );\r
2526                                     $message =\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
2531                                     $id++;\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
2537                                 }\r
2538                             }\r
2539                             //Send Bye\r
2540                             /*\r
2541                             $MessageContent="\r\n".pack("C", 0);\r
2542                             $MessagePayload=\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
2547                                 "CSeq: 0\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
2555                             $new_id++;\r
2556                             $hdr = pack("LLLLLLLLLLLL", \r
2557                             0,\r
2558                             $new_id,\r
2559                             0, 0,\r
2560                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2561                             0,\r
2562                             0,\r
2563                             rand(),\r
2564                             0,\r
2565                             0,0);\r
2566                             $message =\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
2571                             $id++;\r
2572                             $this->SB_writedata($message);\r
2573                             $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));\r
2574                             */\r
2575                             break;\r
2576                         }\r
2577                         //TODO:\r
2578                         //if ($hdr_Flag == 2) {\r
2579                         // just send ACK...\r
2580                         //    $this->SB_writeln("ACK $id");\r
2581                         //    break;\r
2582                         //}\r
2583                         if ($hdr_SessionID == 4) {\r
2584                             // ignore?\r
2585                             $this->debug_message("*** p2p: ignore flag 4");\r
2586                             break;\r
2587                         }\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
2592                             $finished = true;\r
2593                         }\r
2594                         else {\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
2603                             $finished = true;\r
2604                         }\r
2605                         if (!$finished) {\r
2606                             // ignore not finished split packet\r
2607                             $this->debug_message("*** p2p: ignore split packet, not finished");\r
2608                             break;\r
2609                         }\r
2610                         //$new_id = ~$hdr_Identifier;\r
2611                         /*\r
2612                          $new_id++;\r
2613                          $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
2614                          $new_id,\r
2615                          0, 0,\r
2616                          $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2617                          0,\r
2618                          2,\r
2619                          $hdr_Identifier,\r
2620                          $hdr_AckID,\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
2626                          $id++;\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
2630                          */\r
2631                         break;\r
2632                     }\r
2633                     $this->log_message("*** MSG from $from_email: $sMsg");\r
2634                     $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
2635                     break;\r
2636                 case '217':\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
2640                     $SessionEnd=true;\r
2641                     break;\r
2642                 default:\r
2643                     if (is_numeric($code))\r
2644                     {\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
2647                         $SessionEnd=true;\r
2648                     }\r
2649                     break;\r
2650             }\r
2651             if(!$this->IsIgnoreMail($user)) $LastActive = time();\r
2652         }\r
2653         if (feof($this->SBFp))\r
2654         {\r
2655             // lost connection? error? try OIM later\r
2656             @fclose($this->SBFp);\r
2657             return false;\r
2658         }\r
2659         $this->SB_writeln("OUT");\r
2660         @fclose($this->SBFp);\r
2661         return true;\r
2662     }\r
2663     private function switchboard_control($ip, $port, $cki_code, $user, $Messages)\r
2664     {\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
2668         if (!$this->SBFp)\r
2669         {\r
2670             $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
2671             return false;\r
2672         }\r
2673         return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));\r
2674     }\r
2675     private function switchboard_ring($ip, $port, $sid, $ticket,$user)\r
2676     {\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
2680         if (!$this->SBFp)\r
2681         {\r
2682             $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
2683             return false;\r
2684         }\r
2685         return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));\r
2686     }\r
2687 \r
2688     private function sendOIM($to, $sMessage, $lockkey)\r
2689     {\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
2694 <soap:Header>\r
2695   <From memberName="'.$this->user.'"\r
2696         friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="\r
2697         xml:lang="zh-TW"\r
2698         proxy="MSNMSGR"\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
2710   </Sequence>\r
2711 </soap:Header>\r
2712 <soap:Body>\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
2720 \r
2721 '.chunk_split(base64_encode($sMessage)).'\r
2722   </Content>\r
2723 </soap:Body>\r
2724 </soap:Envelope>';\r
2725 \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
2730             );\r
2731 \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
2747 \r
2748             if ($http_code == 200) {\r
2749                 $this->debug_message("*** OIM sent for $to");\r
2750                 return true;\r
2751             }\r
2752 \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
2762             }\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
2769             }\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
2776                     return true;\r
2777                 }\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
2783                 else\r
2784                 $err_msg = '';\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
2788                 return false;\r
2789             }\r
2790             return array('challenge' => $challenge, 'auth_policy' => $auth_policy);\r
2791     }\r
2792 \r
2793     // read data for specified size\r
2794     private function ns_readdata($size) {\r
2795         $data = '';\r
2796         $count = 0;\r
2797         while (!feof($this->NSfp)) {\r
2798             $buf = @fread($this->NSfp, $size - $count);\r
2799             $data .= $buf;\r
2800             $count += strlen($buf);\r
2801             if ($count >= $size) break;\r
2802         }\r
2803         $this->debug_message("NS: data ($size/$count) <<<\n$data");\r
2804         return $data;\r
2805     }\r
2806 \r
2807     // read one line\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
2813         }\r
2814         return $data;\r
2815     }\r
2816 \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
2821         $this->id++;\r
2822         return;\r
2823     }\r
2824 \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
2829         return;\r
2830     }\r
2831 \r
2832     // read data for specified size for SB\r
2833     private function sb_readdata($size) {\r
2834         $data = '';\r
2835         $count = 0;\r
2836         while (!feof($this->SBFp)) {\r
2837             $buf = @fread($this->SBFp, $size - $count);\r
2838             $data .= $buf;\r
2839             $count += strlen($buf);\r
2840             if ($count >= $size) break;\r
2841         }\r
2842         $this->debug_message("SB: data ($size/$count) <<<\n$data");\r
2843         return $data;\r
2844     }\r
2845 \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
2852         }\r
2853         return $data;\r
2854     }\r
2855 \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
2861         $this->id++;\r
2862         return;\r
2863     }\r
2864 \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
2869         return;\r
2870     }\r
2871 \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
2878         if ($fp) {\r
2879             fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");\r
2880             fclose($fp);\r
2881             return;\r
2882         }*/\r
2883         // still show debug information, if we can't open log_file\r
2884         echo $str."\n";\r
2885         return;\r
2886     }\r
2887 \r
2888     function dump_binary($str) {\r
2889         $buf = '';\r
2890         $a_str = '';\r
2891         $h_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
2897                 }\r
2898                 $buf .= sprintf("%04X:", $i);\r
2899                 $a_str = '';\r
2900                 $h_str = '';\r
2901             }\r
2902             $ch = ord($str[$i]);\r
2903             if ($ch < 32)\r
2904             $a_str .= '.';\r
2905             else\r
2906             $a_str .= chr($ch);\r
2907             $h_str .= sprintf(" %02X", $ch);\r
2908         }\r
2909         if ($h_str !== '')\r
2910         $buf .= "$h_str $a_str\n";\r
2911         return $buf;\r
2912     }\r
2913 \r
2914     // write log\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
2918         if ($fp) {\r
2919             fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");\r
2920             fclose($fp);\r
2921         }*/\r
2922         $this->debug_message($str);\r
2923         return;\r
2924     }\r
2925     /**\r
2926      *\r
2927      * @param $FilePath 圖檔路徑\r
2928      * @param $Type     檔案類型 3=>大頭貼,2表情圖案    \r
2929      * @return array\r
2930      */\r
2931     private function MsnObj($FilePath,$Type=3)\r
2932     {\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
2943         return $MsnObj;\r
2944     }\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
2952         }\r
2953         return $Data;\r
2954     }\r
2955     private function GetPictureFilePath($Context)\r
2956     {\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
2963         return false;\r
2964     }\r
2965     private function GetMsnObjDefine($Message)\r
2966     {\r
2967         $DefineString='';\r
2968         if(is_array($this->Emotions))\r
2969         foreach($this->Emotions as $Pattern => $FilePath)\r
2970         {\r
2971             if(strpos($Message,$Pattern)!==false)\r
2972             $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";\r
2973         }\r
2974         return $DefineString;\r
2975     }\r
2976     /**\r
2977      * Receive Message Overload Function\r
2978      * @param $Sender\r
2979      * @param $Message\r
2980      * @param $Network   1 => msn , 32 =>yahoo\r
2981      * @param $IsOIM\r
2982      * @return unknown_type\r
2983      */\r
2984     protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}\r
2985     /**\r
2986      * Remove Us From Member List Overload Function\r
2987      * @param $User\r
2988      * @param $Message\r
2989      * @param $Network   1 => msn , 32 =>yahoo\r
2990      * @return unknown_type\r
2991      */\r
2992     protected function RemoveUsFromMemberList($User,$Network){}\r
2993     /**\r
2994      * Add Us to Member List Overload Function\r
2995      * @param $User\r
2996      * @param $Message\r
2997      * @param $Network   1 => msn , 32 =>yahoo\r
2998      * @return unknown_type\r
2999      */\r
3000     protected function AddUsToMemberList($User,$Network){}\r
3001     \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
3005         {\r
3006             $this->log_message("!!! Can't connect to server: $this->error");\r
3007             if(!$this->NSRetryWait($this->retry_wait)) return;\r
3008         }\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
3023                                 // pending list\r
3024                                 $cnt = 0;\r
3025                                 foreach (array('Allow', 'Reverse') as $list) {\r
3026                                     if (isset($aData[$list]))\r
3027                                     $cnt++;\r
3028                                     else {\r
3029                                         if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
3030                                             $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
3031                                             $cnt++;\r
3032                                         }\r
3033                                     }\r
3034                                 }\r
3035                                 if ($cnt >= 2) {\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
3040                                 }\r
3041                             }\r
3042                             else {\r
3043                                 // sync list\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
3048                                     }\r
3049                                 }\r
3050                             }\r
3051                         }\r
3052                     }\r
3053                 }\r
3054             }\r
3055         }\r
3056         $n = 0;\r
3057         $sList = '';\r
3058         $len = 0;\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
3065                     $n++;\r
3066                     $sList = '';\r
3067                     $len = strlen($str);\r
3068                 }\r
3069                 $sList .= $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
3075                         // so we use 7475\r
3076                         if ($len > 7475) {\r
3077                             $sList .= '</d>';\r
3078                             $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
3079                             $n++;\r
3080                             $sList = '<d n="'.$u_domain.'">'.$str;\r
3081                             $len = strlen($sList);\r
3082                         }\r
3083                         else\r
3084                         $sList .= $str;\r
3085                     }\r
3086                 }\r
3087                 $sList .= '</d>';\r
3088             }\r
3089         }\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
3098         }\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
3103         //設定個人大頭貼\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
3114     }\r
3115     \r
3116     public function NSreceive() {\r
3117         $this->log_message("*** startup ***");\r
3118         \r
3119         $aADL = array();\r
3120         \r
3121         // Sign in again if not signed in or socket failed\r
3122         if (!is_resource($this->NSfp) || feof($this->NSfp)) {\r
3123             $this->signon();\r
3124         }\r
3125         \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
3129             $this->signon();\r
3130         } else {\r
3131             switch (substr($data,0,3))\r
3132             {\r
3133                 case 'SBS':\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
3136                     break;\r
3137     \r
3138                 case 'RFS':\r
3139                     // FIXME:\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
3148                         }\r
3149                     }\r
3150                     break;\r
3151     \r
3152                 case 'LST':\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
3159                     }\r
3160                     break;\r
3161     \r
3162                 case 'ADL':\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
3167                     {\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
3171                         {\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
3177                             else\r
3178                             {\r
3179                                 $re_login = false;\r
3180                                 $cnt = 0;\r
3181                                 foreach (array('Allow', 'Reverse') as $list)\r
3182                                 {\r
3183                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
3184                                     {\r
3185                                         if ($re_login) {\r
3186                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
3187                                             continue;\r
3188                                         }\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
3194                                             continue;\r
3195                                         }\r
3196                                         $re_login = true;\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
3200                                         {\r
3201                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
3202                                             continue;\r
3203                                         }\r
3204                                     }\r
3205                                     $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
3206                                     $cnt++;\r
3207                                 }\r
3208                                 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
3209                             }\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
3212                         }\r
3213                         else\r
3214                         $this->log_message("*** someone add us to their list: $data");\r
3215                         $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
3216                     }\r
3217                     break;\r
3218     \r
3219                 case 'RML':\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
3224                     {\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
3228                         {\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
3233                             {\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
3239                             }\r
3240                             else\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
3243                         }\r
3244                         else\r
3245                         $this->log_message("*** someone remove us from their list: $data");\r
3246                     }\r
3247                     break;\r
3248     \r
3249                 case 'MSG':\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
3256                         $header = true;\r
3257                         $ignore = false;\r
3258                         $maildata = '';\r
3259                         foreach ($aLines as $line) {\r
3260                             $line = rtrim($line);\r
3261                             if ($header) {\r
3262                                 if ($line === '') {\r
3263                                     $header = false;\r
3264                                     continue;\r
3265                                 }\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
3271                                         $ignore = true;\r
3272                                         break;\r
3273                                     }\r
3274                                 }\r
3275                                 continue;\r
3276                             }\r
3277                             if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
3278                                 $maildata = trim(substr($line, 10));\r
3279                                 break;\r
3280                             }\r
3281                         }\r
3282                         if ($ignore) {\r
3283                             $this->log_message("*** ingnore MSG for: $line");\r
3284                             break;\r
3285                         }\r
3286                         if ($maildata == '') {\r
3287                             $this->log_message("*** ingnore MSG not for OIM");\r
3288                             break;\r
3289                         }\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
3301                                     break;\r
3302                                 }\r
3303                                 $re_login = true;\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
3309                                     break;\r
3310                                 }\r
3311                             }\r
3312                         }\r
3313                         // could be a lots of <M>...</M>, so we can't use preg_match here\r
3314                         $p = $maildata;\r
3315                         $aOIMs = array();\r
3316                         while (1) {\r
3317                             $start = strpos($p, '<M>');\r
3318                             $end = strpos($p, '</M>');\r
3319                             if ($start === false || $end === false || $start > $end) break;\r
3320                             $end += 4;\r
3321                             $sOIM = substr($p, $start, $end - $start);\r
3322                             $aOIMs[] = $sOIM;\r
3323                             $p = substr($p, $end);\r
3324                         }\r
3325                         if (count($aOIMs) == 0) {\r
3326                             $this->log_message("*** ingnore empty OIM");\r
3327                             break;\r
3328                         }\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
3335                             // E: sender\r
3336                             // I: msgid\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
3342                                 continue;\r
3343                             }\r
3344                             $oim_type = $matches[1];\r
3345                             if ($oim_type = 13)\r
3346                             $network = 32;\r
3347                             else\r
3348                             $network = 1;\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
3352                                 continue;\r
3353                             }\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
3358                                 continue;\r
3359                             }\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
3369                                 if ($re_login) {\r
3370                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
3371                                     continue;\r
3372                                 }\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
3377                                     continue;\r
3378                                 }\r
3379                                 $re_login = true;\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
3385                                     continue;\r
3386                                 }\r
3387                             }\r
3388                             $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
3389     \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
3392                         }\r
3393                     }\r
3394                     break;\r
3395     \r
3396                 case 'UBM':\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
3401                     {\r
3402                         $data = $this->ns_readdata($size);\r
3403                         $aLines = @explode("\n", $data);\r
3404                         $header = true;\r
3405                         $ignore = false;\r
3406                         $sMsg = '';\r
3407                         foreach ($aLines as $line) {\r
3408                             $line = rtrim($line);\r
3409                             if ($header) {\r
3410                                 if ($line === '') {\r
3411                                     $header = false;\r
3412                                     continue;\r
3413                                 }\r
3414                                 if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
3415                                     $ignore = true;\r
3416                                     break;\r
3417                                 }\r
3418                                 continue;\r
3419                             }\r
3420                             $aSubLines = @explode("\r", $line);\r
3421                             foreach ($aSubLines as $str) {\r
3422                                 if ($sMsg !== '')\r
3423                                 $sMsg .= "\n";\r
3424                                 $sMsg .= $str;\r
3425                             }\r
3426                         }\r
3427                         if($ignore)\r
3428                         {\r
3429                             $this->log_message("*** ingnore from $from_email: $line");\r
3430                             break;\r
3431                         }\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
3435                     }\r
3436                     break;\r
3437     \r
3438                 case 'UBX':\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
3445                     break;\r
3446     \r
3447                 case 'CHL':\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
3459                     break;\r
3460                 case 'CHG':\r
3461                     // NS: <<< CHG {id} {status} {code}\r
3462                     // ignore it\r
3463                     // change our status to online first\r
3464                     break;\r
3465     \r
3466                 case 'XFR':\r
3467                     // sometimes, NS will redirect to another NS\r
3468                     // MSNP9\r
3469                     // NS: <<< XFR {id} NS {server} 0 {server}\r
3470                     // MSNP15\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
3477                         // maybe exit?\r
3478                         // this connection will close after XFR\r
3479                         $this->NSLogout();\r
3480                         continue;\r
3481                     }\r
3482                     if(count($this->MessageQueue))\r
3483                     {\r
3484                         foreach($this->MessageQueue as $User => $Message)\r
3485                         {\r
3486                             //$this->ChildProcess[$ChildPid]\r
3487                             $this->log_message("*** XFR SB $User");\r
3488                             $pid=pcntl_fork();\r
3489                             if($pid)\r
3490                             {\r
3491                                 //Parrent Process\r
3492                                 $this->ChildProcess[$pid]=$User;\r
3493                                 break;\r
3494                             }\r
3495                             elseif($pid==-1)\r
3496                             {\r
3497                                 $this->log_message("*** Fork Error $User");\r
3498                                 break;\r
3499                             }\r
3500                             else\r
3501                             {\r
3502                                 //Child Process\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
3508                                 {\r
3509                                     // error for switchboard\r
3510                                     $this->log_message("!!! error for sending message to ".$User);\r
3511                                 }\r
3512                                 die;\r
3513                             }\r
3514                         }\r
3515                         unset($this->MessageQueue[$User]);\r
3516                     }\r
3517                     /*\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
3523                      }*/\r
3524                     break;\r
3525                 case 'QNG':\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
3531                     \r
3532                     $this->callHandler('Pong', $ping_wait);\r
3533                     break;\r
3534     \r
3535                 case 'RNG':\r
3536                     if($this->PhotoStickerFile!==false)\r
3537                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
3538                     else\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
3546                     {\r
3547                         $this->log_message("*** Ignore RNG from $email");\r
3548                         break;\r
3549                     }\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
3553                     if($pid)\r
3554                     {\r
3555                         //Parrent Process\r
3556                         $this->ChildProcess[$pid]='RNG';\r
3557                         break;\r
3558                     }\r
3559                     elseif($pid==-1)\r
3560                     {\r
3561                         $this->log_message("*** Fork Error $User");\r
3562                         break;\r
3563                     }\r
3564                     else\r
3565                     {\r
3566                         //Child Process\r
3567                         $this->log_message("*** Ring Child Process Start for $User");\r
3568                         $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
3569                         die;\r
3570                     }\r
3571                     break;\r
3572                 case 'OUT':\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
3577     \r
3578                 default:\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
3583     \r
3584                         return $this->NsLogout();\r
3585                     }\r
3586                     break;\r
3587             }\r
3588         }\r
3589     }\r
3590     \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
3595         \r
3596         //FIXME Probably not needed (we're not running in a loop anymore)\r
3597         /*if($this->kill_me)\r
3598         {\r
3599             $this->log_message("*** SB Okay, kill me now!");\r
3600             endSBSession($socket);\r
3601         }*/\r
3602         \r
3603         if(!$Joined) {\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
3606             return false;\r
3607         }\r
3608         \r
3609         $aMessage = $this->getMessage($Message);\r
3610         //CheckEmotion...\r
3611         $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
3612         if($MsnObjDefine !== '')\r
3613         {\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
3617             $id++;\r
3618             $this->SB_writedata($SendString);\r
3619             $this->id++;\r
3620         }\r
3621         $len = strlen($aMessage);\r
3622         $this->SB_writeln("MSG $id N $len");\r
3623         \r
3624         // Increment the trID\r
3625         $this->switchBoardSessions[$to]['id']++;\r
3626         \r
3627         $this->SB_writedata($aMessage);\r
3628         \r
3629         // Don't close the SB session, we might as well leave it open\r
3630         \r
3631         return true;\r
3632     }\r
3633     \r
3634     //FIXME Not sure if this is needed?\r
3635     private function endSBSession($socket) {\r
3636         if (feof($this->SBFp))\r
3637         {\r
3638             // lost connection? error? try OIM later\r
3639             @fclose($this->SBFp);\r
3640             return false;\r
3641         }\r
3642         $this->SB_writeln("OUT");\r
3643         @fclose($this->SBFp);\r
3644         return true;\r
3645     }\r
3646     \r
3647     private function getSBSession($to) {\r
3648         \r
3649     }\r
3650     \r
3651     public function sendMessage($message, $to) {\r
3652         if($message != '') {\r
3653             list($name,$host,$network)=explode('@',$to);\r
3654             $network=$network==''?1:$network;\r
3655             \r
3656             if($network === 1 && isset($this->switchBoardSessions[$to])) {\r
3657                 $recipient = $name . $host;\r
3658                 $this->debug_message("*** Sending Message to $recipient using existing SB session");\r
3659                 return $this->sendMessageViaSB($message, $recipient);\r
3660             } else {\r
3661                 $this->debug_message("*** Not MSN network or no existing SB session");\r
3662                 //TODO implement creation of SB session etc\r
3663             }\r
3664         }\r
3665         return true;\r
3666     }\r
3667     \r
3668     /**\r
3669      * Sends a ping command\r
3670      * \r
3671      * Should be called about every 50 seconds\r
3672      */\r
3673     public function send_ping() {\r
3674         // NS: >>> PNG\r
3675         $this->ns_writeln("PNG");\r
3676     }\r
3677     \r
3678     public function getNSSocket() {\r
3679         return $this->NSfp;\r
3680     }\r
3681     \r
3682     // TODO Allow for multiple SB session sockets\r
3683     public function getSBSocket() {\r
3684         return $this->SBfp;\r
3685     }\r
3686     \r
3687     public function getSockets() {\r
3688         return array($this->NSfp, $this->SBfp);\r
3689     }\r
3690     \r
3691     /**\r
3692      * Calls User Handler\r
3693      *\r
3694      * Calls registered handler for a specific event.\r
3695      * \r
3696      * @param String $event Command (event) name (Rvous etc)\r
3697      * @param String $data Raw message from server\r
3698      * @see registerHandler\r
3699      * @return void\r
3700      */\r
3701     private function callHandler($event, $data) {\r
3702         if (isset($this->myEventHandlers[$event])) {\r
3703             call_user_func($this->myEventHandlers[$event], $data);\r
3704         } else {\r
3705             $this->noHandler($data);\r
3706         }\r
3707     }\r
3708     \r
3709     /** \r
3710      * Registers a user handler\r
3711      * \r
3712      * Handler List\r
3713      * IMIn, Pong\r
3714      *\r
3715      * @param String $event Event name\r
3716      * @param String $handler User function to call\r
3717      * @see callHandler\r
3718      * @return boolean Returns true if successful\r
3719      */\r
3720     public function registerHandler($event, $handler) {\r
3721         if (is_callable($handler)) {\r
3722             $this->myEventHandlers[$event] = $handler;\r
3723             return true;\r
3724         } else {\r
3725             return false;\r
3726         }\r
3727     }\r
3728 }\r