]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Msn/extlib/phpmsnclass/msn.class.php
Added some more event handlers and corrected aADL scope
[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 $aADL = array();\r
116     private $switchBoardSessions = array();\r
117     \r
118     /**\r
119     * Event Handler Functions\r
120     */\r
121     private $myEventHandlers = array();\r
122     \r
123     // End added for StatusNet\r
124 \r
125     private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)\r
126     {\r
127         $ArrayString='';\r
128         foreach($Array as $Key => $Val)\r
129         {\r
130             if($Key{0}==':') continue;\r
131             $Attrib='';\r
132             if(is_array($Val[':']))\r
133             {\r
134                 foreach($Val[':'] as $AttribName => $AttribVal)\r
135                 $Attrib.=" $AttribName='$AttribVal'";\r
136             }\r
137             if($Key{0}=='!')\r
138             {\r
139                 //List Type Define\r
140                 $Key=substr($Key,1);\r
141                 foreach($Val as $ListKey => $ListVal)\r
142                 {\r
143                     if($ListKey{0}==':') continue;\r
144                     if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);\r
145                     elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';\r
146                     $ArrayString.="<$Key$Attrib>$ListVal</$Key>";\r
147                 }\r
148                 continue;\r
149             }\r
150             if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);\r
151             elseif(is_bool($Val)) $Val=$Val?'true':'false';\r
152             $ArrayString.="<$Key$Attrib>$Val</$Key>";\r
153         }\r
154         if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);\r
155         return $ArrayString;\r
156     }\r
157 \r
158     public function End()\r
159     {\r
160         $this->log_message("*** someone kill me ***");\r
161         $this->kill_me=true;\r
162     }\r
163     private function IsIgnoreMail($Email)\r
164     {        \r
165         if($this->IgnoreList==false) return false;\r
166         foreach($this->IgnoreList as $Pattern)\r
167         {\r
168             if(preg_match($Pattern,$Email)) return true;\r
169         }\r
170         return false;\r
171     }\r
172     public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)\r
173     {\r
174         $this->user = $Configs['user'];\r
175         $this->password = $Configs['password'];\r
176         $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';\r
177         $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';\r
178         $my_add_function = isset($Configs['add_user_function']) ? $Configs['add_user_function'] : false;\r
179         $my_rem_function = isset($Configs['remove_user_function']) ? $Configs['remove_user_function'] : false;\r
180         $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;\r
181         $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;\r
182         $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;\r
183         $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;\r
184         $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;\r
185         $this->IgnoreList=isset($Configs['IgnoreList'])?$Configs['IgnoreList']:false;\r
186         if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)\r
187         {\r
188             foreach($this->Emotions as $EmotionFilePath)\r
189             $this->MsnObj($EmotionFilePath,$Type=2);\r
190         }        \r
191         $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;\r
192         $this->timeout = $timeout;\r
193         // check support\r
194         if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");\r
195         if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");\r
196         if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");\r
197 \r
198         if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");\r
199         if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");\r
200 \r
201         /*\r
202          http://msnpiki.msnfanatic.com/index.php/Client_ID\r
203          Client ID for MSN:\r
204          normal MSN 8.1 clientid is:\r
205          01110110 01001100 11000000 00101100\r
206          = 0x764CC02C\r
207 \r
208          we just use following:\r
209          * 0x04: Your client can send/receive Ink (GIF format)\r
210          * 0x08: Your client can send/recieve Ink (ISF format)\r
211          * 0x8000: This means you support Winks receiving (If not set the official Client will warn with 'contact has an older client and is not capable of receiving Winks')\r
212          * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)\r
213          = 0x7000800C;\r
214          */\r
215         $this->clientid = $client_id;\r
216         $this->windows =(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');\r
217         $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));\r
218     }\r
219 \r
220     private function get_passport_ticket($url = '')\r
221     {\r
222         $user = $this->user;\r
223         $password = htmlspecialchars($this->password);\r
224 \r
225         if ($url === '')\r
226         $passport_url = $this->passport_url;\r
227         else\r
228         $passport_url = $url;\r
229 \r
230         $XML = '<?xml version="1.0" encoding="UTF-8"?>\r
231 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"\r
232           xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"\r
233           xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"\r
234           xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"\r
235           xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"\r
236           xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"\r
237           xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"\r
238           xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">\r
239 <Header>\r
240   <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">\r
241     <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>\r
242     <ps:BinaryVersion>4</ps:BinaryVersion>\r
243     <ps:UIVersion>1</ps:UIVersion>\r
244     <ps:Cookies></ps:Cookies>\r
245     <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>\r
246   </ps:AuthInfo>\r
247   <wsse:Security>\r
248     <wsse:UsernameToken Id="user">\r
249       <wsse:Username>'.$user.'</wsse:Username>\r
250       <wsse:Password>'.$password.'</wsse:Password>\r
251     </wsse:UsernameToken>\r
252   </wsse:Security>\r
253 </Header>\r
254 <Body>\r
255   <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">\r
256     <wst:RequestSecurityToken Id="RST0">\r
257       <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
258       <wsp:AppliesTo>\r
259         <wsa:EndpointReference>\r
260           <wsa:Address>http://Passport.NET/tb</wsa:Address>\r
261         </wsa:EndpointReference>\r
262       </wsp:AppliesTo>\r
263     </wst:RequestSecurityToken>\r
264     <wst:RequestSecurityToken Id="RST1">\r
265       <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
266       <wsp:AppliesTo>\r
267         <wsa:EndpointReference>\r
268           <wsa:Address>messengerclear.live.com</wsa:Address>\r
269         </wsa:EndpointReference>\r
270       </wsp:AppliesTo>\r
271       <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>\r
272     </wst:RequestSecurityToken>\r
273     <wst:RequestSecurityToken Id="RST2">\r
274       <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
275       <wsp:AppliesTo>\r
276         <wsa:EndpointReference>\r
277           <wsa:Address>messenger.msn.com</wsa:Address>\r
278         </wsa:EndpointReference>\r
279       </wsp:AppliesTo>\r
280       <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>\r
281     </wst:RequestSecurityToken>\r
282     <wst:RequestSecurityToken Id="RST3">\r
283       <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
284       <wsp:AppliesTo>\r
285         <wsa:EndpointReference>\r
286           <wsa:Address>contacts.msn.com</wsa:Address>\r
287         </wsa:EndpointReference>\r
288       </wsp:AppliesTo>\r
289       <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
290     </wst:RequestSecurityToken>\r
291     <wst:RequestSecurityToken Id="RST4">\r
292       <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
293       <wsp:AppliesTo>\r
294         <wsa:EndpointReference>\r
295           <wsa:Address>messengersecure.live.com</wsa:Address>\r
296         </wsa:EndpointReference>\r
297       </wsp:AppliesTo>\r
298       <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>\r
299     </wst:RequestSecurityToken>\r
300     <wst:RequestSecurityToken Id="RST5">\r
301       <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
302       <wsp:AppliesTo>\r
303         <wsa:EndpointReference>\r
304           <wsa:Address>spaces.live.com</wsa:Address>\r
305         </wsa:EndpointReference>\r
306       </wsp:AppliesTo>\r
307       <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
308     </wst:RequestSecurityToken>\r
309     <wst:RequestSecurityToken Id="RST6">\r
310       <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
311       <wsp:AppliesTo>\r
312         <wsa:EndpointReference>\r
313           <wsa:Address>storage.msn.com</wsa:Address>\r
314         </wsa:EndpointReference>\r
315       </wsp:AppliesTo>\r
316       <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
317     </wst:RequestSecurityToken>\r
318   </ps:RequestMultipleSecurityTokens>\r
319 </Body>\r
320 </Envelope>';\r
321 \r
322         $this->debug_message("*** URL: $passport_url");\r
323         $this->debug_message("*** Sending SOAP:\n$XML");\r
324         $curl = curl_init();\r
325         curl_setopt($curl, CURLOPT_URL, $passport_url);\r
326         if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
327         curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
328         curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
329         curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
330         curl_setopt($curl, CURLOPT_POST, 1);\r
331         curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
332         $data = curl_exec($curl);\r
333         $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
334         curl_close($curl);\r
335         $this->debug_message("*** Get Result:\n$data");\r
336 \r
337         if ($http_code != 200) {\r
338             // sometimes, rediret to another URL\r
339             // MSNP15\r
340             //<faultcode>psf:Redirect</faultcode>\r
341             //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>\r
342             //<faultstring>Authentication Failure</faultstring>\r
343             if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {\r
344                 $this->debug_message("*** Can't get passport ticket! http code = $http_code");\r
345                 return false;\r
346             }\r
347             preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);\r
348             if (count($matches) == 0) {\r
349                 $this->debug_message("*** redirect, but can't get redirect URL!");\r
350                 return false;\r
351             }\r
352             $redirect_url = $matches[1];\r
353             if ($redirect_url == $passport_url) {\r
354                 $this->debug_message("*** redirect, but redirect to same URL!");\r
355                 return false;\r
356             }\r
357             $this->debug_message("*** redirect to $redirect_url");\r
358             return $this->get_passport_ticket($redirect_url);\r
359         }\r
360 \r
361         // sometimes, rediret to another URL, also return 200\r
362         // MSNP15\r
363         //<faultcode>psf:Redirect</faultcode>\r
364         //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>\r
365         //<faultstring>Authentication Failure</faultstring>\r
366         if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {\r
367             preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);\r
368             if (count($matches) != 0) {\r
369                 $redirect_url = $matches[1];\r
370                 if ($redirect_url == $passport_url) {\r
371                     $this->debug_message("*** redirect, but redirect to same URL!");\r
372                     return false;\r
373                 }\r
374                 $this->debug_message("*** redirect to $redirect_url");\r
375                 return $this->get_passport_ticket($redirect_url);\r
376             }\r
377         }\r
378 \r
379         // no Redurect faultcode or URL\r
380         // we should get the ticket here\r
381 \r
382         // we need ticket and secret code\r
383         // RST1: messengerclear.live.com\r
384         // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>\r
385         // <wst:BinarySecret>binary secret</wst:BinarySecret>\r
386         // RST2: messenger.msn.com\r
387         // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>\r
388         // RST3: contacts.msn.com\r
389         // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>\r
390         // RST4: messengersecure.live.com\r
391         // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>\r
392         // RST5: spaces.live.com\r
393         // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>\r
394         // RST6: storage.msn.com\r
395         // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>\r
396         preg_match("#".\r
397             "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
398             "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".\r
399             "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
400             "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
401             "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
402             "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
403             "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
404             "#",\r
405         $data, $matches);\r
406 \r
407         // no ticket found!\r
408         if (count($matches) == 0) {\r
409             $this->debug_message("*** Can't get passport ticket!");\r
410             return false;\r
411         }\r
412 \r
413         //$this->debug_message(var_export($matches, true));\r
414         // matches[0]: all data\r
415         // matches[1]: RST1 (messengerclear.live.com) ticket\r
416         // matches[2]: ...\r
417         // matches[3]: RST1 (messengerclear.live.com) binary secret\r
418         // matches[4]: ...\r
419         // matches[5]: RST2 (messenger.msn.com) ticket\r
420         // matches[6]: ...\r
421         // matches[7]: RST3 (contacts.msn.com) ticket\r
422         // matches[8]: ...\r
423         // matches[9]: RST4 (messengersecure.live.com) ticket\r
424         // matches[10]: ...\r
425         // matches[11]: RST5 (spaces.live.com) ticket\r
426         // matches[12]: ...\r
427         // matches[13]: RST6 (storage.live.com) ticket\r
428         // matches[14]: ...\r
429 \r
430         // so\r
431         // ticket => $matches[1]\r
432         // secret => $matches[3]\r
433         // web_ticket => $matches[5]\r
434         // contact_ticket => $matches[7]\r
435         // oim_ticket => $matches[9]\r
436         // space_ticket => $matches[11]\r
437         // storage_ticket => $matches[13]\r
438 \r
439         // yes, we get ticket\r
440         $aTickets = array(\r
441             'ticket' => html_entity_decode($matches[1]),\r
442             'secret' => html_entity_decode($matches[3]),\r
443             'web_ticket' => html_entity_decode($matches[5]),\r
444             'contact_ticket' => html_entity_decode($matches[7]),\r
445             'oim_ticket' => html_entity_decode($matches[9]),\r
446             'space_ticket' => html_entity_decode($matches[11]),\r
447             'storage_ticket' => html_entity_decode($matches[13])\r
448         );        \r
449         $this->ticket=$aTickets;\r
450         $this->debug_message(var_export($aTickets, true));\r
451         $ABAuthHeaderArray=array(\r
452   'ABAuthHeader'=>array(\r
453     ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
454     'ManagedGroupRequest'=>false,\r
455     'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),\r
456         )\r
457         );\r
458         $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));\r
459         file_put_contents('/tmp/STTicket.txt',htmlspecialchars($this->ticket['storage_ticket']));\r
460         //$this->debug_message("StorageTicket:\n",htmlspecialchars($this->ticket['storage_ticket']));\r
461         return $aTickets;\r
462     }\r
463     private function UpdateContacts()\r
464     {\r
465         $ABApplicationHeaderArray=array(\r
466  'ABApplicationHeader'=>array(\r
467   ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
468   'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',\r
469   'IsMigration'=>false,\r
470   'PartnerScenario'=>'ContactSave'\r
471   )\r
472   );\r
473   $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));\r
474   $ABFindAllArray=array(\r
475    'ABFindAll'=>array(\r
476     ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
477     'abId'=>'00000000-0000-0000-0000-000000000000',\r
478     'abView'=>'Full',\r
479     'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',\r
480   )\r
481   );\r
482   $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');\r
483   $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));\r
484   $this->Contacts=array();\r
485   try\r
486   {\r
487       $this->debug_message("*** Update Contacts...");\r
488       $Result=$this->ABService->ABFindAll($ABFindAll);\r
489       $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());\r
490       foreach($Result->ABFindAllResult->contacts->Contact as $Contact)\r
491       $this->Contacts[$Contact->contactInfo->passportName]=$Contact;\r
492   }\r
493   catch(Exception $e)\r
494   {\r
495       $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
496   }\r
497     }\r
498     protected function addContact($email, $network, $display = '', $sendADL = false)\r
499     {\r
500         if ($network != 1) return true;\r
501         if(isset($this->Contacts[$email])) return true;\r
502 \r
503         $ABContactAddArray=array(\r
504    'ABContactAdd'=>array(\r
505     ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
506     'abId'=>'00000000-0000-0000-0000-000000000000',\r
507     'contacts'=>array(\r
508      'Contact'=>array(\r
509       ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
510       'contactInfo'=>array(\r
511        'contactType'=>'LivePending',\r
512        'passportName'=>$email,\r
513        'isMessengerUser'=>true,\r
514        'MessengerMemberInfo'=>array(\r
515         'DisplayName'=>$email\r
516         )\r
517         )\r
518         )\r
519         ),\r
520     'options'=>array(\r
521      'EnableAllowListManagement'=>true\r
522         )\r
523         )\r
524         );\r
525         $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');\r
526         try\r
527         {\r
528             $this->debug_message("*** Add Contacts $email...");\r
529             $this->ABService->ABContactAdd($ABContactAdd);\r
530         }\r
531         catch(Exception $e)\r
532         {\r
533             $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
534         }\r
535         if ($sendADL && !feof($this->NSfp)) {\r
536             @list($u_name, $u_domain) = @explode('@', $email);\r
537             foreach (array('1', '2') as $l) {\r
538                 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';\r
539                 $len = strlen($str);\r
540                 // NS: >>> ADL {id} {size}\r
541                 $this->ns_writeln("ADL $this->id $len");\r
542                 $this->ns_writedata($str);\r
543             }\r
544         }\r
545         $this->UpdateContacts();\r
546         return true;\r
547     }\r
548 \r
549     function delMemberFromList($memberID, $email, $network, $list) {\r
550         if ($network != 1 && $network != 32) return true;\r
551         if ($memberID === false) return true;\r
552         $user = $email;\r
553         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
554         if ($network == 1)\r
555         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
556 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
557                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
558                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
559                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
560 <soap:Header>\r
561     <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
562         <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
563         <IsMigration>false</IsMigration>\r
564         <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
565     </ABApplicationHeader>\r
566     <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
567         <ManagedGroupRequest>false</ManagedGroupRequest>\r
568         <TicketToken>'.$ticket.'</TicketToken>\r
569     </ABAuthHeader>\r
570 </soap:Header>\r
571 <soap:Body>\r
572     <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">\r
573         <serviceHandle>\r
574             <Id>0</Id>\r
575             <Type>Messenger</Type>\r
576             <ForeignId></ForeignId>\r
577         </serviceHandle>\r
578         <memberships>\r
579             <Membership>\r
580                 <MemberRole>'.$list.'</MemberRole>\r
581                 <Members>\r
582                     <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
583                         <Type>Passport</Type>\r
584                         <MembershipId>'.$memberID.'</MembershipId>\r
585                         <State>Accepted</State>\r
586                     </Member>\r
587                 </Members>\r
588             </Membership>\r
589         </memberships>\r
590     </DeleteMember>\r
591 </soap:Body>\r
592 </soap:Envelope>';\r
593         else\r
594         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
595 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
596                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
597                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
598                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
599 <soap:Header>\r
600     <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
601         <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
602         <IsMigration>false</IsMigration>\r
603         <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
604     </ABApplicationHeader>\r
605     <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
606         <ManagedGroupRequest>false</ManagedGroupRequest>\r
607         <TicketToken>'.$ticket.'</TicketToken>\r
608     </ABAuthHeader>\r
609 </soap:Header>\r
610 <soap:Body>\r
611     <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">\r
612         <serviceHandle>\r
613             <Id>0</Id>\r
614             <Type>Messenger</Type>\r
615             <ForeignId></ForeignId>\r
616         </serviceHandle>\r
617         <memberships>\r
618             <Membership>\r
619                 <MemberRole>'.$list.'</MemberRole>\r
620                 <Members>\r
621                     <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
622                         <Type>Email</Type>\r
623                         <MembershipId>'.$memberID.'</MembershipId>\r
624                         <State>Accepted</State>\r
625                     </Member>\r
626                 </Members>\r
627             </Membership>\r
628         </memberships>\r
629     </DeleteMember>\r
630 </soap:Body>\r
631 </soap:Envelope>';\r
632 \r
633         $header_array = array(\r
634             'SOAPAction: '.$this->delmember_soap,\r
635             'Content-Type: text/xml; charset=utf-8',\r
636             'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
637             );\r
638 \r
639             $this->debug_message("*** URL: $this->delmember_url");\r
640             $this->debug_message("*** Sending SOAP:\n$XML");\r
641             $curl = curl_init();\r
642             curl_setopt($curl, CURLOPT_URL, $this->delmember_url);\r
643             curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
644             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
645             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
646             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
647             if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
648             curl_setopt($curl, CURLOPT_POST, 1);\r
649             curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
650             $data = curl_exec($curl);\r
651             $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
652             curl_close($curl);\r
653             $this->debug_message("*** Get Result:\n$data");\r
654 \r
655             if ($http_code != 200) {\r
656                 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
657                 if (count($matches) == 0) {\r
658                     $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");\r
659                     return false;\r
660                 }\r
661                 $faultcode = trim($matches[1]);\r
662                 $faultstring = trim($matches[2]);\r
663                 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {\r
664                     $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");\r
665                     return false;\r
666                 }\r
667                 $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
668                 return true;\r
669             }\r
670             $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");\r
671             return true;\r
672     }\r
673 \r
674     function addMemberToList($email, $network, $list) {\r
675         if ($network != 1 && $network != 32) return true;\r
676         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
677         $user = $email;\r
678 \r
679         if ($network == 1)\r
680         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
681 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
682                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
683                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
684                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
685 <soap:Header>\r
686     <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
687         <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
688         <IsMigration>false</IsMigration>\r
689         <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
690     </ABApplicationHeader>\r
691     <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
692         <ManagedGroupRequest>false</ManagedGroupRequest>\r
693         <TicketToken>'.$ticket.'</TicketToken>\r
694     </ABAuthHeader>\r
695 </soap:Header>\r
696 <soap:Body>\r
697     <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
698         <serviceHandle>\r
699             <Id>0</Id>\r
700             <Type>Messenger</Type>\r
701             <ForeignId></ForeignId>\r
702         </serviceHandle>\r
703         <memberships>\r
704             <Membership>\r
705                 <MemberRole>'.$list.'</MemberRole>\r
706                 <Members>\r
707                     <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
708                         <Type>Passport</Type>\r
709                         <State>Accepted</State>\r
710                         <PassportName>'.$user.'</PassportName>\r
711                     </Member>\r
712                 </Members>\r
713             </Membership>\r
714         </memberships>\r
715     </AddMember>\r
716 </soap:Body>\r
717 </soap:Envelope>';\r
718         else\r
719         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
720 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
721                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
722                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
723                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
724 <soap:Header>\r
725     <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
726         <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
727         <IsMigration>false</IsMigration>\r
728         <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
729     </ABApplicationHeader>\r
730     <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
731         <ManagedGroupRequest>false</ManagedGroupRequest>\r
732         <TicketToken>'.$ticket.'</TicketToken>\r
733     </ABAuthHeader>\r
734 </soap:Header>\r
735 <soap:Body>\r
736     <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
737         <serviceHandle>\r
738             <Id>0</Id>\r
739             <Type>Messenger</Type>\r
740             <ForeignId></ForeignId>\r
741         </serviceHandle>\r
742         <memberships>\r
743             <Membership>\r
744                 <MemberRole>'.$list.'</MemberRole>\r
745                 <Members>\r
746                     <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
747                         <Type>Email</Type>\r
748                         <State>Accepted</State>\r
749                         <Email>'.$user.'</Email>\r
750                         <Annotations>\r
751                             <Annotation>\r
752                                 <Name>MSN.IM.BuddyType</Name>\r
753                                 <Value>32:YAHOO</Value>\r
754                             </Annotation>\r
755                         </Annotations>\r
756                     </Member>\r
757                 </Members>\r
758             </Membership>\r
759         </memberships>\r
760     </AddMember>\r
761 </soap:Body>\r
762 </soap:Envelope>';\r
763         $header_array = array(\r
764             'SOAPAction: '.$this->addmember_soap,\r
765             'Content-Type: text/xml; charset=utf-8',\r
766             'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
767             );\r
768 \r
769             $this->debug_message("*** URL: $this->addmember_url");\r
770             $this->debug_message("*** Sending SOAP:\n$XML");\r
771             $curl = curl_init();\r
772             curl_setopt($curl, CURLOPT_URL, $this->addmember_url);\r
773             curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
774             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
775             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
776             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
777             if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
778             curl_setopt($curl, CURLOPT_POST, 1);\r
779             curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
780             $data = curl_exec($curl);\r
781             $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
782             curl_close($curl);\r
783             $this->debug_message("*** Get Result:\n$data");\r
784 \r
785             if ($http_code != 200) {\r
786                 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
787                 if (count($matches) == 0) {\r
788                     $this->log_message("*** can't add member (network: $network) $email to $list");\r
789                     return false;\r
790                 }\r
791                 $faultcode = trim($matches[1]);\r
792                 $faultstring = trim($matches[2]);\r
793                 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {\r
794                     $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");\r
795                     return false;\r
796                 }\r
797                 $this->log_message("*** add member (network: $network) $email to $list, already exist!");\r
798                 return true;\r
799             }\r
800             $this->log_message("*** add member (network: $network) $email to $list");\r
801             return true;\r
802     }\r
803 \r
804     function getMembershipList($returnData=false) {\r
805         $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
806         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
807 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
808                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
809                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
810                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
811 <soap:Header>\r
812     <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
813         <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
814         <IsMigration>false</IsMigration>\r
815         <PartnerScenario>Initial</PartnerScenario>\r
816     </ABApplicationHeader>\r
817     <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
818         <ManagedGroupRequest>false</ManagedGroupRequest>\r
819         <TicketToken>'.$ticket.'</TicketToken>\r
820     </ABAuthHeader>\r
821 </soap:Header>\r
822 <soap:Body>\r
823     <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">\r
824         <serviceFilter>\r
825             <Types>\r
826                 <ServiceType>Messenger</ServiceType>\r
827                 <ServiceType>Invitation</ServiceType>\r
828                 <ServiceType>SocialNetwork</ServiceType>\r
829                 <ServiceType>Space</ServiceType>\r
830                 <ServiceType>Profile</ServiceType>\r
831             </Types>\r
832         </serviceFilter>\r
833     </FindMembership>\r
834 </soap:Body>\r
835 </soap:Envelope>';\r
836         $header_array = array(\r
837             'SOAPAction: '.$this->membership_soap,\r
838             'Content-Type: text/xml; charset=utf-8',\r
839             'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
840             );\r
841             $this->debug_message("*** URL: $this->membership_url");\r
842             $this->debug_message("*** Sending SOAP:\n$XML");\r
843             $curl = curl_init();\r
844             curl_setopt($curl, CURLOPT_URL, $this->membership_url);\r
845             curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
846             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
847             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
848             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
849             if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
850             curl_setopt($curl, CURLOPT_POST, 1);\r
851             curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
852             $data = curl_exec($curl);\r
853             $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
854             curl_close($curl);\r
855             $this->debug_message("*** Get Result:\n$data");\r
856             if($http_code != 200) return array();\r
857             $p = $data;\r
858             $aMemberships = array();\r
859             while (1) {\r
860                 //$this->debug_message("search p = $p");\r
861                 $start = strpos($p, '<Membership>');\r
862                 $end = strpos($p, '</Membership>');\r
863                 if ($start === false || $end === false || $start > $end) break;\r
864                 //$this->debug_message("start = $start, end = $end");\r
865                 $end += 13;\r
866                 $sMembership = substr($p, $start, $end - $start);\r
867                 $aMemberships[] = $sMembership;\r
868                 //$this->debug_message("add sMembership = $sMembership");\r
869                 $p = substr($p, $end);\r
870             }\r
871             //$this->debug_message("aMemberships = ".var_export($aMemberships, true));\r
872 \r
873             $aContactList = array();\r
874             foreach ($aMemberships as $sMembership) {\r
875                 //$this->debug_message("sMembership = $sMembership");\r
876                 if (isset($matches)) unset($matches);\r
877                 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);\r
878                 if (count($matches) == 0) continue;\r
879                 $sMemberRole = $matches[1];\r
880                 //$this->debug_message("MemberRole = $sMemberRole");\r
881                 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;\r
882                 $p = $sMembership;\r
883                 if (isset($aMembers)) unset($aMembers);\r
884                 $aMembers = array();\r
885                 while (1) {\r
886                     //$this->debug_message("search p = $p");\r
887                     $start = strpos($p, '<Member xsi:type="');\r
888                     $end = strpos($p, '</Member>');\r
889                     if ($start === false || $end === false || $start > $end) break;\r
890                     //$this->debug_message("start = $start, end = $end");\r
891                     $end += 9;\r
892                     $sMember = substr($p, $start, $end - $start);\r
893                     $aMembers[] = $sMember;\r
894                     //$this->debug_message("add sMember = $sMember");\r
895                     $p = substr($p, $end);\r
896                 }\r
897                 //$this->debug_message("aMembers = ".var_export($aMembers, true));\r
898                 foreach ($aMembers as $sMember) {\r
899                     //$this->debug_message("sMember = $sMember");\r
900                     if (isset($matches)) unset($matches);\r
901                     preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);\r
902                     if (count($matches) == 0) continue;\r
903                     $sMemberType = $matches[1];\r
904                     //$this->debug_message("MemberType = $sMemberType");\r
905                     $network = -1;\r
906                     preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);\r
907                     if (count($matches) == 0) continue;\r
908                     $id = $matches[1];\r
909                     if ($sMemberType == 'PassportMember') {\r
910                         if (strpos($sMember, '<Type>Passport</Type>') === false) continue;\r
911                         $network = 1;\r
912                         preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);\r
913                     }\r
914                     else if ($sMemberType == 'EmailMember') {\r
915                         if (strpos($sMember, '<Type>Email</Type>') === false) continue;\r
916                         // Value is 32: or 32:YAHOO\r
917                         preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);\r
918                         if (count($matches) == 0) continue;\r
919                         if ($matches[1] != 32) continue;\r
920                         $network = 32;\r
921                         preg_match('#<Email>(.*)</Email>#', $sMember, $matches);\r
922                     }\r
923                     if ($network == -1) continue;\r
924                     if (count($matches) > 0) {\r
925                         $email = $matches[1];\r
926                         @list($u_name, $u_domain) = @explode('@', $email);\r
927                         if ($u_domain == NULL) continue;\r
928                         $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;\r
929                         $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
930                     }\r
931                 }\r
932             }\r
933             return $aContactList;\r
934     }\r
935 \r
936     private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {\r
937         $this->id = 1;\r
938         if ($redirect_server === '') {\r
939             $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);\r
940             if (!$this->NSfp) {\r
941                 $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";\r
942                 return false;\r
943             }\r
944         }\r
945         else {\r
946             $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);\r
947             if (!$this->NSfp) {\r
948                 $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";\r
949                 return false;\r
950             }\r
951         }\r
952 \r
953         stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
954         $this->authed = false;\r
955         // MSNP9\r
956         // NS: >> VER {id} MSNP9 CVR0\r
957         // MSNP15\r
958         // NS: >>> VER {id} MSNP15 CVR0\r
959         $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
960 \r
961         $start_tm = time();\r
962         while (!feof($this->NSfp))\r
963         {\r
964             $data = $this->ns_readln();\r
965             // no data?\r
966             if ($data === false) {\r
967                 if ($this->timeout > 0) {\r
968                     $now_tm = time();\r
969                     $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;\r
970                     if ($used_time > $this->timeout) {\r
971                         // logout now\r
972                         // NS: >>> OUT\r
973                         $this->ns_writeln("OUT");\r
974                         fclose($this->NSfp);\r
975                         $this->error = 'Timeout, maybe protocol changed!';\r
976                         $this->debug_message("*** $this->error");\r
977                         return false;\r
978                     }\r
979                 }\r
980                 continue;\r
981             }\r
982             $code = substr($data, 0, 3);\r
983             $start_tm = time();\r
984 \r
985             switch ($code) {\r
986                 case 'VER':\r
987                     // MSNP9\r
988                     // NS: <<< VER {id} MSNP9 CVR0\r
989                     // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}\r
990                     // MSNP15\r
991                     // NS: <<< VER {id} MSNP15 CVR0\r
992                     // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}\r
993                     $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");\r
994                     break;\r
995 \r
996                 case 'CVR':\r
997                     // MSNP9\r
998                     // NS: <<< CVR {id} {ver_list} {download_serve} ....\r
999                     // NS: >>> USR {id} TWN I {user}\r
1000                     // MSNP15\r
1001                     // NS: <<< CVR {id} {ver_list} {download_serve} ....\r
1002                     // NS: >>> USR {id} SSO I {user}\r
1003                     $this->ns_writeln("USR $this->id $this->login_method I $user");\r
1004                     break;\r
1005 \r
1006                 case 'USR':\r
1007                     // already login for passport site, finish the login process now.\r
1008                     // NS: <<< USR {id} OK {user} {verify} 0\r
1009                     if ($this->authed) return true;\r
1010                     // max. 16 digits for password\r
1011                     if (strlen($password) > 16)\r
1012                     $password = substr($password, 0, 16);\r
1013 \r
1014                     $this->user = $user;\r
1015                     $this->password = $password;\r
1016                     // NS: <<< USR {id} SSO S {policy} {nonce}\r
1017                     @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);\r
1018 \r
1019                     $this->passport_policy = $policy;\r
1020                     $aTickets = $this->get_passport_ticket();\r
1021                     if (!$aTickets || !is_array($aTickets)) {\r
1022                         // logout now\r
1023                         // NS: >>> OUT\r
1024                         $this->ns_writeln("OUT");\r
1025                         fclose($this->NSfp);\r
1026                         $this->error = 'Passport authenticated fail!';\r
1027                         $this->debug_message("*** $this->error");\r
1028                         return false;\r
1029                     }\r
1030 \r
1031                     $ticket = $aTickets['ticket'];\r
1032                     $secret = $aTickets['secret'];\r
1033                     $this->ticket = $aTickets;\r
1034                     $login_code = $this->generateLoginBLOB($secret, $nonce);\r
1035 \r
1036                     // NS: >>> USR {id} SSO S {ticket} {login_code}\r
1037                     $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");\r
1038                     $this->authed = true;\r
1039                     break;\r
1040 \r
1041                 case 'XFR':\r
1042                     // main login server will redirect to anther NS after USR command\r
1043                     // MSNP9\r
1044                     // NS: <<< XFR {id} NS {server} 0 {server}\r
1045                     // MSNP15\r
1046                     // NS: <<< XFR {id} NS {server} U D\r
1047                     @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);\r
1048                     if($Type!='NS') break;\r
1049                     @list($ip, $port) = @explode(':', $server);\r
1050                     // this connection will close after XFR\r
1051                     fclose($this->NSfp);\r
1052 \r
1053                     $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
1054                     if (!$this->NSfp) {\r
1055                         $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";\r
1056                         $this->debug_message("*** $this->error");\r
1057                         return false;\r
1058                     }\r
1059 \r
1060                     stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
1061                     // MSNP9\r
1062                     // NS: >> VER {id} MSNP9 CVR0\r
1063                     // MSNP15\r
1064                     // NS: >>> VER {id} MSNP15 CVR0\r
1065                     $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
1066                     break;\r
1067 \r
1068                 case 'GCF':\r
1069                     // return some policy data after 'USR {id} SSO I {user}' command\r
1070                     // NS: <<< GCF 0 {size}\r
1071                     @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);\r
1072                     // we don't need the data, just read it and drop\r
1073                     if (is_numeric($size) && $size > 0)\r
1074                     $this->ns_readdata($size);\r
1075                     break;\r
1076 \r
1077                 default:\r
1078                     // we'll quit if got any error\r
1079                     if (is_numeric($code)) {\r
1080                         // logout now\r
1081                         // NS: >>> OUT\r
1082                         $this->ns_writeln("OUT");\r
1083                         fclose($this->NSfp);\r
1084                         $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
1085                         $this->debug_message("*** $this->error");\r
1086                         return false;\r
1087                     }\r
1088                     // unknown response from server, just ignore it\r
1089                     break;\r
1090             }\r
1091         }\r
1092         // never goto here\r
1093     }\r
1094 \r
1095     function derive_key($key, $magic) {\r
1096         $hash1 = mhash(MHASH_SHA1, $magic, $key);\r
1097         $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);\r
1098         $hash3 = mhash(MHASH_SHA1, $hash1, $key);\r
1099         $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);\r
1100         return $hash2.substr($hash4, 0, 4);\r
1101     }\r
1102 \r
1103     function generateLoginBLOB($key, $challenge) {\r
1104         $key1 = base64_decode($key);\r
1105         $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');\r
1106         $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');\r
1107 \r
1108         // get hash of challenge using key2\r
1109         $hash = mhash(MHASH_SHA1, $challenge, $key2);\r
1110 \r
1111         // get 8 bytes random data\r
1112         $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);\r
1113 \r
1114         $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);\r
1115 \r
1116         $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);\r
1117         $blob .= $iv;\r
1118         $blob .= $hash;\r
1119         $blob .= $cipher;\r
1120 \r
1121         return base64_encode($blob);\r
1122     }\r
1123 \r
1124     function getOIM_maildata() {\r
1125         preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);\r
1126         if (count($matches) == 0) {\r
1127             $this->debug_message('*** no web ticket?');\r
1128             return false;\r
1129         }\r
1130         $t = htmlspecialchars($matches[1]);\r
1131         $p = htmlspecialchars($matches[2]);\r
1132         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
1133 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
1134                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
1135                xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
1136 <soap:Header>\r
1137   <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1138     <t>'.$t.'</t>\r
1139     <p>'.$p.'</p>\r
1140   </PassportCookie>\r
1141 </soap:Header>\r
1142 <soap:Body>\r
1143   <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />\r
1144 </soap:Body>\r
1145 </soap:Envelope>';\r
1146 \r
1147         $header_array = array(\r
1148             'SOAPAction: '.$this->oim_maildata_soap,\r
1149             'Content-Type: text/xml; charset=utf-8',\r
1150             'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
1151             );\r
1152 \r
1153             $this->debug_message("*** URL: $this->oim_maildata_url");\r
1154             $this->debug_message("*** Sending SOAP:\n$XML");\r
1155             $curl = curl_init();\r
1156             curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);\r
1157             curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
1158             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
1159             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
1160             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
1161             if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
1162             curl_setopt($curl, CURLOPT_POST, 1);\r
1163             curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
1164             $data = curl_exec($curl);\r
1165             $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
1166             curl_close($curl);\r
1167             $this->debug_message("*** Get Result:\n$data");\r
1168 \r
1169             if ($http_code != 200) {\r
1170                 $this->debug_message("*** Can't get OIM maildata! http code: $http_code");\r
1171                 return false;\r
1172             }\r
1173 \r
1174             // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>\r
1175             preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);\r
1176             if (count($matches) == 0) {\r
1177                 $this->debug_message("*** Can't get OIM maildata");\r
1178                 return '';\r
1179             }\r
1180             return $matches[2];\r
1181     }\r
1182 \r
1183     function getOIM_message($msgid) {\r
1184         preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);\r
1185         if (count($matches) == 0) {\r
1186             $this->debug_message('*** no web ticket?');\r
1187             return false;\r
1188         }\r
1189         $t = htmlspecialchars($matches[1]);\r
1190         $p = htmlspecialchars($matches[2]);\r
1191 \r
1192         // read OIM\r
1193         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
1194 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
1195                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
1196                xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
1197 <soap:Header>\r
1198   <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1199     <t>'.$t.'</t>\r
1200     <p>'.$p.'</p>\r
1201   </PassportCookie>\r
1202 </soap:Header>\r
1203 <soap:Body>\r
1204   <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1205     <messageId>'.$msgid.'</messageId>\r
1206     <alsoMarkAsRead>false</alsoMarkAsRead>\r
1207   </GetMessage>\r
1208 </soap:Body>\r
1209 </soap:Envelope>';\r
1210 \r
1211         $header_array = array(\r
1212             'SOAPAction: '.$this->oim_read_soap,\r
1213             'Content-Type: text/xml; charset=utf-8',\r
1214             'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
1215             );\r
1216 \r
1217             $this->debug_message("*** URL: $this->oim_read_url");\r
1218             $this->debug_message("*** Sending SOAP:\n$XML");\r
1219             $curl = curl_init();\r
1220             curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);\r
1221             curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
1222             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
1223             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
1224             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
1225             if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
1226             curl_setopt($curl, CURLOPT_POST, 1);\r
1227             curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
1228             $data = curl_exec($curl);\r
1229             $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
1230             curl_close($curl);\r
1231             $this->debug_message("*** Get Result:\n$data");\r
1232 \r
1233             if ($http_code != 200) {\r
1234                 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");\r
1235                 return false;\r
1236             }\r
1237 \r
1238             // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?\r
1239             // multi-lines?\r
1240             $start = strpos($data, '<GetMessageResult>');\r
1241             $end = strpos($data, '</GetMessageResult>');\r
1242             if ($start === false || $end === false || $start > $end) {\r
1243                 $this->debug_message("*** Can't get OIM: $msgid");\r
1244                 return false;\r
1245             }\r
1246             $lines = substr($data, $start + 18, $end - $start);\r
1247             $aLines = @explode("\n", $lines);\r
1248             $header = true;\r
1249             $ignore = false;\r
1250             $sOIM = '';\r
1251             foreach ($aLines as $line) {\r
1252                 $line = rtrim($line);\r
1253                 if ($header) {\r
1254                     if ($line === '') {\r
1255                         $header = false;\r
1256                         continue;\r
1257                     }\r
1258                     continue;\r
1259                 }\r
1260                 // stop at empty lines\r
1261                 if ($line === '') break;\r
1262                 $sOIM .= $line;\r
1263             }\r
1264             $sMsg = base64_decode($sOIM);\r
1265             $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
1266 \r
1267             // delete OIM\r
1268             $XML = '<?xml version="1.0" encoding="utf-8"?>\r
1269 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
1270                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
1271                xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
1272 <soap:Header>\r
1273   <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1274     <t>'.$t.'</t>\r
1275     <p>'.$p.'</p>\r
1276   </PassportCookie>\r
1277 </soap:Header>\r
1278 <soap:Body>\r
1279   <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
1280     <messageIds>\r
1281       <messageId>'.$msgid.'</messageId>\r
1282     </messageIds>\r
1283   </DeleteMessages>\r
1284 </soap:Body>\r
1285 </soap:Envelope>';\r
1286 \r
1287             $header_array = array(\r
1288             'SOAPAction: '.$this->oim_del_soap,\r
1289             'Content-Type: text/xml; charset=utf-8',\r
1290             'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
1291             );\r
1292 \r
1293             $this->debug_message("*** URL: $this->oim_del_url");\r
1294             $this->debug_message("*** Sending SOAP:\n$XML");\r
1295             $curl = curl_init();\r
1296             curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);\r
1297             curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
1298             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
1299             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
1300             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
1301             if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
1302             curl_setopt($curl, CURLOPT_POST, 1);\r
1303             curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
1304             $data = curl_exec($curl);\r
1305             $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
1306             curl_close($curl);\r
1307             $this->debug_message("*** Get Result:\n$data");\r
1308 \r
1309             if ($http_code != 200)\r
1310             $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");\r
1311             else\r
1312             $this->debug_message("*** OIM ($msgid) deleted");\r
1313             return $sMsg;\r
1314     }\r
1315     private function NSLogout() {\r
1316         if (is_resource($this->NSfp) && !feof($this->NSfp)) {\r
1317             // logout now\r
1318             // NS: >>> OUT\r
1319             $this->ns_writeln("OUT");\r
1320             fclose($this->NSfp);\r
1321             $this->NSfp = false;\r
1322             $this->log_message("*** logout now!");\r
1323         }\r
1324 \r
1325     }\r
1326     private function NSRetryWait($Wait) {\r
1327         $this->log_message("*** wait for $Wait seconds");\r
1328         for($i=0;$i<$Wait;$i++) {\r
1329             sleep(1);\r
1330             if($this->kill_me) return false;\r
1331         }\r
1332         return true;\r
1333     }\r
1334     public function ProcessSendMessageFileQueue() {\r
1335         $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');\r
1336         if (!is_array($aFiles)) return true;\r
1337         clearstatcache();\r
1338         foreach ($aFiles as $filename) {\r
1339             $fp = fopen($filename, 'rt');\r
1340             if (!$fp) continue;\r
1341             $aTo = array();\r
1342             $sMessage = '';\r
1343             $buf = trim(fgets($fp));\r
1344             if (substr($buf, 0, 3) == 'TO:') {\r
1345                 $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));\r
1346                 while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";\r
1347             }\r
1348             fclose($fp);\r
1349             if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')\r
1350             $this->log_message("!!! message format error? delete $filename");\r
1351             else\r
1352             {\r
1353                 foreach($aTo as $To)\r
1354                 {\r
1355                     @list($user, $domain, $network) = @explode('@', $To);\r
1356                     $MessageList[$network]["$user@$domain"]=$sMessage;\r
1357                 }\r
1358             }\r
1359             if($this->backup_file)\r
1360             {\r
1361                 $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';\r
1362                 if (!file_exists($backup_dir)) @mkdir($backup_dir);\r
1363                 $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);\r
1364                 if (@rename($filename, $backup_name))\r
1365                 $this->log_message("*** move file to $backup_name");\r
1366             }\r
1367             else @unlink($filename);\r
1368         }\r
1369         foreach ($MessageList as $network => $Messages)\r
1370         {\r
1371             switch(trim($network))\r
1372             {\r
1373                 case '':\r
1374                 case 1:   //MSN\r
1375                     // okay, try to ask a switchboard (SB) for sending message\r
1376                     // NS: >>> XFR {id} SB\r
1377                     // $this->ns_writeln("XFR $this->id SB");\r
1378                     foreach($Messages as $User => $Message)\r
1379                     $this->MessageQueue[$User][]=$Message;\r
1380                     break;\r
1381                 case 'Offline':  //MSN\r
1382                     //Send OIM\r
1383                     //FIXME: 修正Send OIM\r
1384                     foreach($Messages as $To => $Message)\r
1385                     {\r
1386                         $lockkey='';\r
1387                         for ($i = 0; $i < $this->oim_try; $i++)\r
1388                         {\r
1389                             if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;\r
1390                             if (is_array($oim_result) && $oim_result['challenge'] !== false) {\r
1391                                 // need challenge lockkey\r
1392                                 $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);\r
1393                                 $lockkey = $this->getChallenge($oim_result['challenge']);\r
1394                                 continue;\r
1395                             }\r
1396                             if ($oim_result === false || $oim_result['auth_policy'] !== false)\r
1397                             {\r
1398                                 if ($re_login)\r
1399                                 {\r
1400                                     $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");\r
1401                                     break;\r
1402                                 }\r
1403                                 $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");\r
1404                                 // maybe we need to re-login again\r
1405                                 if(!$this->get_passport_ticket())\r
1406                                 {\r
1407                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
1408                                     break;\r
1409                                 }\r
1410                                 $this->log_message("**** get new ticket, try it again");\r
1411                                 continue;\r
1412                             }\r
1413                         }\r
1414                     }\r
1415                     break;\r
1416                 default:  //Other\r
1417                     foreach($Messages as $To => $Message) {\r
1418                         $Message=$this->getMessage($Message, $network);\r
1419                         $len = strlen($Message);\r
1420                         $this->ns_writeln("UUM $this->id $To $network 1 $len");\r
1421                         $this->ns_writedata($Message);\r
1422                         $this->log_message("*** sent to $To (network: $network):\n$Message");\r
1423                     }\r
1424             }\r
1425         }\r
1426         if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))\r
1427         {\r
1428             $this->MessageQueue[$User]['XFRSent']=false;\r
1429             $this->MessageQueue[$User]['ReqTime']=false;\r
1430         }\r
1431         return true;\r
1432     }\r
1433     public function SignalFunction($signal)\r
1434     {\r
1435         switch($signal)\r
1436         {\r
1437             case SIGTRAP:\r
1438             case SIGTERM:\r
1439             case SIGHUP:\r
1440                 $this->End();\r
1441                 return;\r
1442             case SIGCHLD:\r
1443                 $ChildPid=pcntl_wait($status,WUNTRACED);\r
1444                 if($ChildPid>0)\r
1445                 {\r
1446                     $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);\r
1447                     unset($this->ChildProcess[$ChildPid]);\r
1448                 }\r
1449                 return;\r
1450         }\r
1451     }\r
1452 \r
1453     public function Run()\r
1454     {\r
1455         $this->log_message("*** startup ***");\r
1456         if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");\r
1457         if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");\r
1458         if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");\r
1459         $process_file = false;\r
1460         $sent = false;\r
1461         $aADL = array();\r
1462         $aContactList = array();\r
1463         while (true)\r
1464         {\r
1465             if($this->kill_me)\r
1466             {\r
1467                 $this->log_message("*** Okay, kill me now!");\r
1468                 return $this->NSLogout();\r
1469             }\r
1470             if (!is_resource($this->NSfp) || feof($this->NSfp))\r
1471             {\r
1472                 $this->log_message("*** try to connect to MSN network");\r
1473                 if (!$this->connect($this->user, $this->password))\r
1474                 {\r
1475                     $this->log_message("!!! Can't connect to server: $this->error");\r
1476                     if(!$this->NSRetryWait($this->retry_wait)) continue;\r
1477                 }\r
1478                 $this->UpdateContacts();\r
1479                 $this->LastPing=time();\r
1480                 $this->log_message("*** connected, wait for command");\r
1481                 $start_tm = time();\r
1482                 $ping_tm = time();\r
1483                 stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
1484                     $aContactList = $this->getMembershipList();\r
1485                     if ($this->update_pending) {\r
1486                         if (is_array($aContactList)) {\r
1487                             $pending = 'Pending';\r
1488                             foreach ($aContactList as $u_domain => $aUserList) {\r
1489                                 foreach ($aUserList as $u_name => $aNetworks) {\r
1490                                     foreach ($aNetworks as $network => $aData) {\r
1491                                         if (isset($aData[$pending])) {\r
1492                                             // pending list\r
1493                                             $cnt = 0;\r
1494                                             foreach (array('Allow', 'Reverse') as $list) {\r
1495                                                 if (isset($aData[$list]))\r
1496                                                 $cnt++;\r
1497                                                 else {\r
1498                                                     if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
1499                                                         $aContactList[$u_domain][$u_name][$network][$list] = false;\r
1500                                                         $cnt++;\r
1501                                                     }\r
1502                                                 }\r
1503                                             }\r
1504                                             if ($cnt >= 2) {\r
1505                                                 $id = $aData[$pending];\r
1506                                                 // we can delete it from pending now\r
1507                                                 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
1508                                                 unset($aContactList[$u_domain][$u_name][$network][$pending]);\r
1509                                             }\r
1510                                         }\r
1511                                         else {\r
1512                                             // sync list\r
1513                                             foreach (array('Allow', 'Reverse') as $list) {\r
1514                                                 if (!isset($aData[$list])) {\r
1515                                                     if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
1516                                                     $aContactList[$u_domain][$u_name][$network][$list] = false;\r
1517                                                 }\r
1518                                             }\r
1519                                         }\r
1520                                     }\r
1521                                 }\r
1522                             }\r
1523                         }\r
1524                     }\r
1525                     $n = 0;\r
1526                     $sList = '';\r
1527                     $len = 0;\r
1528                     if (is_array($aContactList)) {\r
1529                         foreach ($aContactList as $u_domain => $aUserList) {\r
1530                             $str = '<d n="'.$u_domain.'">';\r
1531                             $len += strlen($str);\r
1532                             if ($len > 7400) {\r
1533                                 $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
1534                                 $n++;\r
1535                                 $sList = '';\r
1536                                 $len = strlen($str);\r
1537                             }\r
1538                             $sList .= $str;\r
1539                             foreach ($aUserList as $u_name => $aNetworks) {\r
1540                                 foreach ($aNetworks as $network => $status) {\r
1541                                     $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
1542                                     $len += strlen($str);\r
1543                                     // max: 7500, but <ml l="1"></d></ml> is 19,\r
1544                                     // so we use 7475\r
1545                                     if ($len > 7475) {\r
1546                                         $sList .= '</d>';\r
1547                                         $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
1548                                         $n++;\r
1549                                         $sList = '<d n="'.$u_domain.'">'.$str;\r
1550                                         $len = strlen($sList);\r
1551                                     }\r
1552                                     else\r
1553                                     $sList .= $str;\r
1554                                 }\r
1555                             }\r
1556                             $sList .= '</d>';\r
1557                         }\r
1558                     }\r
1559                     $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
1560                     // NS: >>> BLP {id} BL\r
1561                     $this->ns_writeln("BLP $this->id BL");\r
1562                     foreach ($aADL as $str) {\r
1563                         $len = strlen($str);\r
1564                         // NS: >>> ADL {id} {size}\r
1565                         $this->ns_writeln("ADL $this->id $len");\r
1566                         $this->ns_writedata($str);\r
1567                     }\r
1568                     // NS: >>> PRP {id} MFN name\r
1569                     if ($this->alias == '') $this->alias = $user;\r
1570                     $aliasname = rawurlencode($this->alias);\r
1571                     $this->ns_writeln("PRP $this->id MFN $aliasname");\r
1572                     //設定個人大頭貼\r
1573                     //$MsnObj=$this->PhotoStckObj();\r
1574                     // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
1575                     $this->ns_writeln("CHG $this->id NLN $this->clientid");                    \r
1576                     if($this->PhotoStickerFile!==false)\r
1577                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
1578                     // NS: >>> UUX {id} length\r
1579                     $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
1580                     $len = strlen($str);\r
1581                     $this->ns_writeln("UUX $this->id $len");\r
1582                     $this->ns_writedata($str);               \r
1583             }\r
1584             $data = $this->ns_readln();\r
1585             if($data===false)\r
1586             {\r
1587                 //If No NS Message Process SendMessageFileQueue\r
1588                 if (time()-$this->LastPing > $this->ping_wait)\r
1589                 {\r
1590                     // NS: >>> PNG\r
1591                     $this->ns_writeln("PNG");\r
1592                     $this->LastPing = time();\r
1593                 }\r
1594                 if(count($this->ChildProcess)<$this->MAXChildProcess)\r
1595                 {\r
1596                     $Index=0;\r
1597                     foreach($this->MessageQueue as $User => $Message)\r
1598                     {\r
1599                         if(!trim($User)) continue;\r
1600                         if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;\r
1601                         if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))\r
1602                         {\r
1603                             $this->MessageQueue[$User]['XFRSent']=true;\r
1604                             $this->MessageQueue[$User]['ReqTime']=time();\r
1605                             $this->log_message("*** Request SB for $User");\r
1606                             $this->ns_writeln("XFR $this->id SB");\r
1607                             $Index++;\r
1608                         }\r
1609                     }\r
1610                 }\r
1611                 if($this->ProcessSendMessageFileQueue()) continue;\r
1612                 break;\r
1613             }\r
1614             switch (substr($data,0,3))\r
1615             {\r
1616                 case 'SBS':\r
1617                     // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
1618                     // NS: <<< SBS 0 null\r
1619                     break;\r
1620 \r
1621                 case 'RFS':\r
1622                     // FIXME:\r
1623                     // NS: <<< RFS ???\r
1624                     // refresh ADL, so we re-send it again\r
1625                     if (is_array($aADL)) {\r
1626                         foreach ($aADL as $str) {\r
1627                             $len = strlen($str);\r
1628                             // NS: >>> ADL {id} {size}\r
1629                             $this->ns_writeln("ADL $this->id $len");\r
1630                             $this->ns_writedata($str);\r
1631                         }\r
1632                     }\r
1633                     break;\r
1634 \r
1635                 case 'LST':\r
1636                     // NS: <<< LST {email} {alias} 11 0\r
1637                     @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\r
1638                     @list($u_name, $u_domain) = @explode('@', $email);\r
1639                     if (!isset($aContactList[$u_domain][$u_name][1])) {\r
1640                         $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
1641                         $this->log_message("*** add to our contact list: $u_name@$u_domain");\r
1642                     }\r
1643                     break;\r
1644 \r
1645                 case 'ADL':\r
1646                     // randomly, we get ADL command, someome add us to their contact list for MSNP15\r
1647                     // NS: <<< ADL 0 {size}\r
1648                     @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);\r
1649                     if (is_numeric($size) && $size > 0)\r
1650                     {\r
1651                         $data = $this->ns_readdata($size);\r
1652                         preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
1653                         if (is_array($matches) && count($matches) > 0)\r
1654                         {\r
1655                             $u_domain = $matches[1];\r
1656                             $u_name = $matches[2];\r
1657                             $network = $matches[4];\r
1658                             if (isset($aContactList[$u_domain][$u_name][$network]))\r
1659                             $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
1660                             else\r
1661                             {\r
1662                                 $re_login = false;\r
1663                                 $cnt = 0;\r
1664                                 foreach (array('Allow', 'Reverse') as $list)\r
1665                                 {\r
1666                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
1667                                     {\r
1668                                         if ($re_login) {\r
1669                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
1670                                             continue;\r
1671                                         }\r
1672                                         $aTickets = $this->get_passport_ticket();\r
1673                                         if (!$aTickets || !is_array($aTickets)) {\r
1674                                             // failed to login? ignore it\r
1675                                             $this->log_message("*** can't re-login, something wrong here");\r
1676                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
1677                                             continue;\r
1678                                         }\r
1679                                         $re_login = true;\r
1680                                         $this->ticket = $aTickets;\r
1681                                         $this->log_message("**** get new ticket, try it again");\r
1682                                         if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
1683                                         {\r
1684                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
1685                                             continue;\r
1686                                         }\r
1687                                     }\r
1688                                     $aContactList[$u_domain][$u_name][$network][$list] = false;\r
1689                                     $cnt++;\r
1690                                 }\r
1691                                 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
1692                             }\r
1693                             $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
1694                             $len = strlen($str);\r
1695                         }\r
1696                         else\r
1697                         $this->log_message("*** someone add us to their list: $data");\r
1698                         $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
1699                     }\r
1700                     break;\r
1701 \r
1702                 case 'RML':\r
1703                     // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
1704                     // NS: <<< RML 0 {size}\r
1705                     @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);\r
1706                     if (is_numeric($size) && $size > 0)\r
1707                     {\r
1708                         $data = $this->ns_readdata($size);\r
1709                         preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
1710                         if (is_array($matches) && count($matches) > 0)\r
1711                         {\r
1712                             $u_domain = $matches[1];\r
1713                             $u_name = $matches[2];\r
1714                             $network = $matches[4];\r
1715                             if (isset($aContactList[$u_domain][$u_name][$network]))\r
1716                             {\r
1717                                 $aData = $aContactList[$u_domain][$u_name][$network];\r
1718                                 foreach ($aData as $list => $id)\r
1719                                 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
1720                                 unset($aContactList[$u_domain][$u_name][$network]);\r
1721                                 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
1722                             }\r
1723                             else\r
1724                             $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
1725                             $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);\r
1726                         }\r
1727                         else\r
1728                         $this->log_message("*** someone remove us from their list: $data");\r
1729                     }\r
1730                     break;\r
1731 \r
1732                 case 'MSG':\r
1733                     // randomly, we get MSG notification from server\r
1734                     // NS: <<< MSG Hotmail Hotmail {size}\r
1735                     @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);\r
1736                     if (is_numeric($size) && $size > 0) {\r
1737                         $data = $this->ns_readdata($size);\r
1738                         $aLines = @explode("\n", $data);\r
1739                         $header = true;\r
1740                         $ignore = false;\r
1741                         $maildata = '';\r
1742                         foreach ($aLines as $line) {\r
1743                             $line = rtrim($line);\r
1744                             if ($header) {\r
1745                                 if ($line === '') {\r
1746                                     $header = false;\r
1747                                     continue;\r
1748                                 }\r
1749                                 if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
1750                                     if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&\r
1751                                     strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
1752                                         // we just need text/x-msmsgsinitialmdatanotification\r
1753                                         // or text/x-msmsgsoimnotification\r
1754                                         $ignore = true;\r
1755                                         break;\r
1756                                     }\r
1757                                 }\r
1758                                 continue;\r
1759                             }\r
1760                             if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
1761                                 $maildata = trim(substr($line, 10));\r
1762                                 break;\r
1763                             }\r
1764                         }\r
1765                         if ($ignore) {\r
1766                             $this->log_message("*** ingnore MSG for: $line");\r
1767                             break;\r
1768                         }\r
1769                         if ($maildata == '') {\r
1770                             $this->log_message("*** ingnore MSG not for OIM");\r
1771                             break;\r
1772                         }\r
1773                         $re_login = false;\r
1774                         if (strcasecmp($maildata, 'too-large') == 0) {\r
1775                             $this->log_message("*** large mail-data, need to get the data via SOAP");\r
1776                             $maildata = $this->getOIM_maildata();\r
1777                             if ($maildata === false) {\r
1778                                 $this->log_message("*** can't get mail-data via SOAP");\r
1779                                 // maybe we need to re-login again\r
1780                                 $aTickets = $this->get_passport_ticket();\r
1781                                 if (!$aTickets || !is_array($aTickets)) {\r
1782                                     // failed to login? ignore it\r
1783                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
1784                                     break;\r
1785                                 }\r
1786                                 $re_login = true;\r
1787                                 $this->ticket = $aTickets;\r
1788                                 $this->log_message("**** get new ticket, try it again");\r
1789                                 $maildata = $this->getOIM_maildata();\r
1790                                 if ($maildata === false) {\r
1791                                     $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
1792                                     break;\r
1793                                 }\r
1794                             }\r
1795                         }\r
1796                         // could be a lots of <M>...</M>, so we can't use preg_match here\r
1797                         $p = $maildata;\r
1798                         $aOIMs = array();\r
1799                         while (1) {\r
1800                             $start = strpos($p, '<M>');\r
1801                             $end = strpos($p, '</M>');\r
1802                             if ($start === false || $end === false || $start > $end) break;\r
1803                             $end += 4;\r
1804                             $sOIM = substr($p, $start, $end - $start);\r
1805                             $aOIMs[] = $sOIM;\r
1806                             $p = substr($p, $end);\r
1807                         }\r
1808                         if (count($aOIMs) == 0) {\r
1809                             $this->log_message("*** ingnore empty OIM");\r
1810                             break;\r
1811                         }\r
1812                         foreach ($aOIMs as $maildata) {\r
1813                             // T: 11 for MSN, 13 for Yahoo\r
1814                             // S: 6 for MSN, 7 for Yahoo\r
1815                             // RT: the datetime received by server\r
1816                             // RS: already read or not\r
1817                             // SZ: size of message\r
1818                             // E: sender\r
1819                             // I: msgid\r
1820                             // F: always 00000000-0000-0000-0000-000000000009\r
1821                             // N: sender alias\r
1822                             preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
1823                             if (count($matches) == 0) {\r
1824                                 $this->log_message("*** ingnore OIM maildata without <T>type</T>");\r
1825                                 continue;\r
1826                             }\r
1827                             $oim_type = $matches[1];\r
1828                             if ($oim_type = 13)\r
1829                             $network = 32;\r
1830                             else\r
1831                             $network = 1;\r
1832                             preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
1833                             if (count($matches) == 0) {\r
1834                                 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");\r
1835                                 continue;\r
1836                             }\r
1837                             $oim_sender = $matches[1];\r
1838                             preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
1839                             if (count($matches) == 0) {\r
1840                                 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");\r
1841                                 continue;\r
1842                             }\r
1843                             $oim_msgid = $matches[1];\r
1844                             preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
1845                             $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
1846                             preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
1847                             $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
1848                             $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
1849                             $sMsg = $this->getOIM_message($oim_msgid);\r
1850                             if ($sMsg === false) {\r
1851                                 $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
1852                                 if ($re_login) {\r
1853                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
1854                                     continue;\r
1855                                 }\r
1856                                 $aTickets = $this->get_passport_ticket();\r
1857                                 if (!$aTickets || !is_array($aTickets)) {\r
1858                                     // failed to login? ignore it\r
1859                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
1860                                     continue;\r
1861                                 }\r
1862                                 $re_login = true;\r
1863                                 $this->ticket = $aTickets;\r
1864                                 $this->log_message("**** get new ticket, try it again");\r
1865                                 $sMsg = $this->getOIM_message($oim_msgid);\r
1866                                 if ($sMsg === false) {\r
1867                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
1868                                     continue;\r
1869                                 }\r
1870                             }\r
1871                             $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
1872 \r
1873                             $this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
1874                         }\r
1875                     }\r
1876                     break;\r
1877 \r
1878                 case 'UBM':\r
1879                     // randomly, we get UBM, this is the message from other network, like Yahoo!\r
1880                     // NS: <<< UBM {email} $network $type {size}\r
1881                     @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);\r
1882                     if (is_numeric($size) && $size > 0)\r
1883                     {\r
1884                         $data = $this->ns_readdata($size);\r
1885                         $aLines = @explode("\n", $data);\r
1886                         $header = true;\r
1887                         $ignore = false;\r
1888                         $sMsg = '';\r
1889                         foreach ($aLines as $line) {\r
1890                             $line = rtrim($line);\r
1891                             if ($header) {\r
1892                                 if ($line === '') {\r
1893                                     $header = false;\r
1894                                     continue;\r
1895                                 }\r
1896                                 if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
1897                                     $ignore = true;\r
1898                                     break;\r
1899                                 }\r
1900                                 continue;\r
1901                             }\r
1902                             $aSubLines = @explode("\r", $line);\r
1903                             foreach ($aSubLines as $str) {\r
1904                                 if ($sMsg !== '')\r
1905                                 $sMsg .= "\n";\r
1906                                 $sMsg .= $str;\r
1907                             }\r
1908                         }\r
1909                         if($ignore)\r
1910                         {\r
1911                             $this->log_message("*** ingnore from $from_email: $line");\r
1912                             break;\r
1913                         }\r
1914                         $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
1915                         $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
1916                     }\r
1917                     break;\r
1918 \r
1919                 case 'UBX':\r
1920                     // randomly, we get UBX notification from server\r
1921                     // NS: <<< UBX email {network} {size}\r
1922                     @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);\r
1923                     // we don't need the notification data, so just ignore it\r
1924                     if (is_numeric($size) && $size > 0)\r
1925                     $this->ns_readdata($size);\r
1926                     break;\r
1927 \r
1928                 case 'CHL':\r
1929                     // randomly, we'll get challenge from server\r
1930                     // NS: <<< CHL 0 {code}\r
1931                     @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);\r
1932                     $fingerprint = $this->getChallenge($chl_code);\r
1933                     // NS: >>> QRY {id} {product_id} 32\r
1934                     // NS: >>> fingerprint\r
1935                     $this->ns_writeln("QRY $this->id $this->prod_id 32");\r
1936                     $this->ns_writedata($fingerprint);\r
1937                     $this->ns_writeln("CHG $this->id NLN $this->clientid");                    \r
1938                     if($this->PhotoStickerFile!==false)\r
1939                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
1940                     break;\r
1941                 case 'CHG':\r
1942                     // NS: <<< CHG {id} {status} {code}\r
1943                     // ignore it\r
1944                     // change our status to online first\r
1945                     break;\r
1946 \r
1947                 case 'XFR':\r
1948                     // sometimes, NS will redirect to another NS\r
1949                     // MSNP9\r
1950                     // NS: <<< XFR {id} NS {server} 0 {server}\r
1951                     // MSNP15\r
1952                     // NS: <<< XFR {id} NS {server} U D\r
1953                     // for normal switchboard XFR\r
1954                     // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
1955                     @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);\r
1956                     @list($ip, $port) = @explode(':', $server);\r
1957                     if ($server_type != 'SB') {\r
1958                         // maybe exit?\r
1959                         // this connection will close after XFR\r
1960                         $this->NSLogout();\r
1961                         continue;\r
1962                     }\r
1963                     if(count($this->MessageQueue))\r
1964                     {\r
1965                         foreach($this->MessageQueue as $User => $Message)\r
1966                         {\r
1967                             //$this->ChildProcess[$ChildPid]\r
1968                             $this->log_message("*** XFR SB $User");\r
1969                             $pid=pcntl_fork();\r
1970                             if($pid)\r
1971                             {\r
1972                                 //Parrent Process\r
1973                                 $this->ChildProcess[$pid]=$User;\r
1974                                 break;\r
1975                             }\r
1976                             elseif($pid==-1)\r
1977                             {\r
1978                                 $this->log_message("*** Fork Error $User");\r
1979                                 break;\r
1980                             }\r
1981                             else\r
1982                             {\r
1983                                 //Child Process\r
1984                                 $this->log_message("*** Child Process Start for $User");\r
1985                                 unset($Message['XFRSent']);\r
1986                                 unset($Message['ReqTime']);\r
1987                                 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
1988                                 if ($bSBresult === false)\r
1989                                 {\r
1990                                     // error for switchboard\r
1991                                     $this->log_message("!!! error for sending message to ".$User);\r
1992                                 }\r
1993                                 die;\r
1994                             }\r
1995                         }\r
1996                         unset($this->MessageQueue[$User]);\r
1997                     }\r
1998                     /*\r
1999                      $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
2000                      if ($bSBresult === false) {\r
2001                      // error for switchboard\r
2002                      $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
2003                      $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
2004                      }*/\r
2005                     break;\r
2006                 case 'QNG':\r
2007                     // NS: <<< QNG {time}\r
2008                     @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);\r
2009                     if ($this->ping_wait == 0) $this->ping_wait = 50;\r
2010                     //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;\r
2011                     //Mod by Ricky Set Online\r
2012                     break;\r
2013 \r
2014                 case 'RNG':\r
2015                     if($this->PhotoStickerFile!==false)\r
2016                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
2017                     else\r
2018                         $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
2019                     // someone is trying to talk to us\r
2020                     // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
2021                     $this->log_message("NS: <<< RNG $data");\r
2022                     @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
2023                     @list($sb_ip, $sb_port) = @explode(':', $server);\r
2024                     if($this->IsIgnoreMail($email)) \r
2025                     {\r
2026                         $this->log_message("*** Ignore RNG from $email");\r
2027                         break;\r
2028                     }\r
2029                     $this->log_message("*** RING from $email, $sb_ip:$sb_port");\r
2030                     $this->addContact($email,1,$email, true);\r
2031                     $pid=pcntl_fork();\r
2032                     if($pid)\r
2033                     {\r
2034                         //Parrent Process\r
2035                         $this->ChildProcess[$pid]='RNG';\r
2036                         break;\r
2037                     }\r
2038                     elseif($pid==-1)\r
2039                     {\r
2040                         $this->log_message("*** Fork Error $User");\r
2041                         break;\r
2042                     }\r
2043                     else\r
2044                     {\r
2045                         //Child Process\r
2046                         $this->log_message("*** Ring Child Process Start for $User");\r
2047                         $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
2048                         die;\r
2049                     }\r
2050                     break;\r
2051                 case 'OUT':\r
2052                     // force logout from NS\r
2053                     // NS: <<< OUT xxx\r
2054                     fclose($this->NSfp);\r
2055                     $this->log_message("*** LOGOUT from NS");\r
2056                     break;\r
2057 \r
2058                 default:\r
2059                     $code = substr($data,0,3);\r
2060                     if (is_numeric($code)) {\r
2061                         $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
2062                         $this->debug_message("*** NS: $this->error");\r
2063 \r
2064                         return $this->NsLogout();\r
2065                     }\r
2066                     break;\r
2067             }\r
2068         }\r
2069         return $this->NsLogout();\r
2070     }\r
2071 \r
2072     /*public function SendMessage($Message, $To)\r
2073     {\r
2074         $FileName = MSN_CLASS_SPOOL_DIR.'/'.strftime('%Y%m%d%H%M%S',time()).'_'.posix_getpid().'_sendMessage.msn';\r
2075         if(!is_array($To))\r
2076         $To=array($To);\r
2077         $Receiver='';\r
2078         foreach($To as $Email)\r
2079         {\r
2080             list($name,$host,$network)=explode('@',$Email);\r
2081             $network=$network==''?1:$network;\r
2082             if($network==1 && $this->SwitchBoardProcess && $this->SwitchBoardSessionUser=="$name@$host" )\r
2083             {\r
2084                 $this->debug_message("*** SendMessage to $Receiver use SB message queue.");\r
2085                 array_push($this->SwitchBoardMessageQueue,$Message);\r
2086                 continue;\r
2087             }\r
2088             $Receiver.="$name@$host@$network,";\r
2089         }\r
2090         if($Receiver=='') return;\r
2091         $Receiver=substr($Receiver,0,-1);\r
2092         $this->debug_message("*** SendMessage to $Receiver use File queue.");\r
2093         file_put_contents($FileName,"TO: $Receiver\n$Message\n");\r
2094     }*/\r
2095 \r
2096     function getChallenge($code)\r
2097     {\r
2098         // MSNP15\r
2099         // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges\r
2100         // Step 1: The MD5 Hash\r
2101         $md5Hash = md5($code.$this->prod_key);\r
2102         $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));\r
2103         for ($i = 0; $i < 4; $i++) {\r
2104             $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));\r
2105             $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;\r
2106         }\r
2107 \r
2108         // Step 2: A new string\r
2109         $chl_id = $code.$this->prod_id;\r
2110         $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));\r
2111 \r
2112         $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));\r
2113         for ($i = 0; $i < count($aID); $i++) {\r
2114             $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));\r
2115             $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);\r
2116         }\r
2117 \r
2118         // Step 3: The 64 bit key\r
2119         $magic_num = 0x0E79A9C1;\r
2120         $str7f = 0x7FFFFFFF;\r
2121         $high = 0;\r
2122         $low = 0;\r
2123         for ($i = 0; $i < count($aID); $i += 2) {\r
2124             $temp = $aID[$i];\r
2125             $temp = bcmod(bcmul($magic_num, $temp), $str7f);\r
2126             $temp = bcadd($temp, $high);\r
2127             $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);\r
2128             $temp = bcmod($temp, $str7f);\r
2129 \r
2130             $high = $aID[$i+1];\r
2131             $high = bcmod(bcadd($high, $temp), $str7f);\r
2132             $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);\r
2133             $high = bcmod($high, $str7f);\r
2134 \r
2135             $low = bcadd(bcadd($low, $high), $temp);\r
2136         }\r
2137 \r
2138         $high = bcmod(bcadd($high, $aMD5[1]), $str7f);\r
2139         $low = bcmod(bcadd($low, $aMD5[3]), $str7f);\r
2140 \r
2141         $new_high = bcmul($high & 0xFF, 0x1000000);\r
2142         $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));\r
2143         $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));\r
2144         $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));\r
2145         // we need integer here\r
2146         $high = 0+$new_high;\r
2147 \r
2148         $new_low = bcmul($low & 0xFF, 0x1000000);\r
2149         $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));\r
2150         $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));\r
2151         $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));\r
2152         // we need integer here\r
2153         $low = 0+$new_low;\r
2154 \r
2155         // we just use 32 bits integer, don't need the key, just high/low\r
2156         // $key = bcadd(bcmul($high, 0x100000000), $low);\r
2157 \r
2158         // Step 4: Using the key\r
2159         $md5Hash = md5($code.$this->prod_key);\r
2160         $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));\r
2161 \r
2162         $hash = '';\r
2163         $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);\r
2164         $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);\r
2165         $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);\r
2166         $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);\r
2167 \r
2168         return $hash;\r
2169     }\r
2170 \r
2171     private function getMessage($sMessage, $network = 1)\r
2172     {\r
2173         $msg_header = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nX-MMS-IM-Format: FN=$this->font_fn; EF=$this->font_ef; CO=$this->font_co; CS=0; PF=22\r\n\r\n";\r
2174         $msg_header_len = strlen($msg_header);\r
2175         if ($network == 1)\r
2176             $maxlen = $this->max_msn_message_len - $msg_header_len;\r
2177         else\r
2178             $maxlen = $this->max_yahoo_message_len - $msg_header_len;\r
2179         $sMessage=str_replace("\r", '', $sMessage);\r
2180         $msg=substr($sMessage,0,$maxlen);\r
2181         return $msg_header.$msg;\r
2182     }\r
2183     /**\r
2184      *\r
2185      * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息\r
2186      * @param $Param\r
2187      * @return boolean\r
2188      */\r
2189     private function DoSwitchBoard($Action,$Param)\r
2190     {\r
2191         $SessionEnd=false;\r
2192         $Joined=false;\r
2193         $id=1;\r
2194         $LastActive=time();\r
2195         stream_set_timeout($this->SBFp, $this->SBTimeout);\r
2196         switch($Action)\r
2197         {\r
2198             case 'Active':\r
2199                 $cki_code=$Param['cki'];\r
2200                 $user=$Param['user'];\r
2201                 $this->SwitchBoardMessageQueue=$Param['Msg'];\r
2202                 // SB: >>> USR {id} {user} {cki}\r
2203                 $this->SB_writeln("USR $id $this->user $cki_code");\r
2204                 $id++;\r
2205                 $this->SwitchBoardSessionUser=$user;\r
2206                 break;\r
2207             case 'Passive':\r
2208                 $ticket=$Param['ticket'];\r
2209                 $sid=$Param['sid'];\r
2210                 $user=$Param['user'];\r
2211                 // SB: >>> ANS {id} {user} {ticket} {session_id}\r
2212                 $this->SB_writeln("ANS $id $this->user $ticket $sid");\r
2213                 $id++;\r
2214                 $this->SwitchBoardSessionUser=$user;\r
2215                 break;\r
2216             default:\r
2217                 return false;\r
2218         }\r
2219         while((!feof($this->SBFp))&&(!$SessionEnd))\r
2220         {\r
2221             $data = $this->SB_readln();\r
2222             if($this->kill_me)\r
2223             {\r
2224                 $this->log_message("*** SB Okay, kill me now!");\r
2225                 break;\r
2226             }\r
2227             if($data === false)\r
2228             {\r
2229                 if(time()-$LastActive > $this->SBIdleTimeout)\r
2230                 {\r
2231                     $this->debug_message("*** SB Idle Timeout!");\r
2232                     break;\r
2233                 }\r
2234                 if(!$Joined) continue;\r
2235                 foreach($this->SwitchBoardMessageQueue as $Message)\r
2236                 {\r
2237                     if($Message=='') continue;\r
2238                     $aMessage = $this->getMessage($Message);\r
2239                     //CheckEmotion...\r
2240                     $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
2241                     if($MsnObjDefine!=='')\r
2242                     {\r
2243                         $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";\r
2244                         $len = strlen($SendString);\r
2245                         $this->SB_writeln("MSG $id N $len");\r
2246                         $id++;\r
2247                         $this->SB_writedata($SendString);\r
2248                         $this->id++;\r
2249                     }\r
2250                     $len = strlen($aMessage);\r
2251                     $this->SB_writeln("MSG $id N $len");\r
2252                     $id++;\r
2253                     $this->SB_writedata($aMessage);\r
2254                 }\r
2255                 $this->SwitchBoardMessageQueue=array();\r
2256                 if(!$this->IsIgnoreMail($user)) $LastActive = time();\r
2257                 continue;\r
2258             }\r
2259             $code = substr($data, 0, 3);\r
2260             switch($code)\r
2261             {\r
2262                 case 'IRO':\r
2263                     // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}\r
2264                     @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);\r
2265                     $this->log_message("*** $email join us");\r
2266                     $Joined=true;\r
2267                     break;\r
2268                 case 'BYE':\r
2269                     $this->log_message("*** Quit for BYE");\r
2270                     $SessionEnd=true;\r
2271                     break;\r
2272                 case 'USR':\r
2273                     // SB: <<< USR {id} OK {user} {alias}\r
2274                     // we don't need the data, just ignore it\r
2275                     // request user to join this switchboard\r
2276                     // SB: >>> CAL {id} {user}\r
2277                     $this->SB_writeln("CAL $id $user");\r
2278                     $id++;\r
2279                     break;\r
2280                 case 'CAL':\r
2281                     // SB: <<< CAL {id} RINGING {?}\r
2282                     // we don't need this, just ignore, and wait for other response\r
2283                     $this->id++;\r
2284                     break;\r
2285                 case 'JOI':\r
2286                     // SB: <<< JOI {user} {alias} {clientid?}\r
2287                     // someone join us\r
2288                     // we don't need the data, just ignore it\r
2289                     // no more user here\r
2290                     $Joined=true;\r
2291                     break;\r
2292                 case 'MSG':\r
2293                     // SB: <<< MSG {email} {alias} {len}\r
2294                     @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);\r
2295                     $len = trim($len);\r
2296                     $data = $this->SB_readdata($len);\r
2297                     $aLines = @explode("\n", $data);\r
2298                     $header = true;\r
2299                     $ignore = false;\r
2300                     $is_p2p = false;\r
2301                     $sMsg = '';\r
2302                     foreach ($aLines as $line)\r
2303                     {\r
2304                         $line = rtrim($line);\r
2305                         if ($header) {\r
2306                             if ($line === '') {\r
2307                                 $header = false;\r
2308                                 continue;\r
2309                             }\r
2310                             if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
2311                                 // typing notification, just ignore\r
2312                                 $ignore = true;\r
2313                                 break;\r
2314                             }\r
2315                             if (strncasecmp($line, 'Chunk:', 6) == 0) {\r
2316                                 // we don't handle any split message, just ignore\r
2317                                 $ignore = true;\r
2318                                 break;\r
2319                             }\r
2320                             if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {\r
2321                                 // p2p message, ignore it, but we need to send acknowledgement for it...\r
2322                                 $is_p2p = true;\r
2323                                 $p = strstr($data, "\n\n");\r
2324                                 $sMsg = '';\r
2325                                 if ($p === false) {\r
2326                                     $p = strstr($data, "\r\n\r\n");\r
2327                                     if ($p !== false)\r
2328                                     $sMsg = substr($p, 4);\r
2329                                 }\r
2330                                 else\r
2331                                 $sMsg = substr($p, 2);\r
2332                                 break;\r
2333                             }\r
2334                             if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {\r
2335                                 // ignore all application/x-... message\r
2336                                 // for example:\r
2337                                 //      application/x-ms-ink        => ink message\r
2338                                 $ignore = true;\r
2339                                 break;\r
2340                             }\r
2341                             if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {\r
2342                                 // ignore all text/x-... message\r
2343                                 // for example:\r
2344                                 //      text/x-msnmsgr-datacast         => nudge, voice clip....\r
2345                                 //      text/x-mms-animemoticon         => customized animemotion word\r
2346                                 $ignore = true;\r
2347                                 break;\r
2348                             }\r
2349                             continue;\r
2350                         }\r
2351                         if ($sMsg !== '')\r
2352                         $sMsg .= "\n";\r
2353                         $sMsg .= $line;\r
2354                     }\r
2355                     if ($ignore)\r
2356                     {\r
2357                         $this->log_message("*** ingnore from $from_email: $line");\r
2358                         break;\r
2359                     }\r
2360                     if ($is_p2p)\r
2361                     {\r
2362                         // we will ignore any p2p message after sending acknowledgement\r
2363                         $ignore = true;\r
2364                         $len = strlen($sMsg);\r
2365                         $this->log_message("*** p2p message from $from_email, size $len");\r
2366                         // header = 48 bytes\r
2367                         // content >= 0 bytes\r
2368                         // footer = 4 bytes\r
2369                         // so it need to >= 52 bytes\r
2370                         /*if ($len < 52) {\r
2371                             $this->log_message("*** p2p: size error, less than 52!");\r
2372                             break;\r
2373                         }*/\r
2374                         $aDwords = @unpack("V12dword", $sMsg);\r
2375                         if (!is_array($aDwords)) {\r
2376                             $this->log_message("*** p2p: header unpack error!");\r
2377                             break;\r
2378                         }\r
2379                         $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));\r
2380                         $hdr_SessionID = $aDwords['dword1'];\r
2381                         $hdr_Identifier = $aDwords['dword2'];\r
2382                         $hdr_DataOffsetLow = $aDwords['dword3'];\r
2383                         $hdr_DataOffsetHigh = $aDwords['dword4'];\r
2384                         $hdr_TotalDataSizeLow = $aDwords['dword5'];\r
2385                         $hdr_TotalDataSizeHigh = $aDwords['dword6'];\r
2386                         $hdr_MessageLength = $aDwords['dword7'];\r
2387                         $hdr_Flag = $aDwords['dword8'];\r
2388                         $hdr_AckID = $aDwords['dword9'];\r
2389                         $hdr_AckUID = $aDwords['dword10'];\r
2390                         $hdr_AckSizeLow = $aDwords['dword11'];\r
2391                         $hdr_AckSizeHigh = $aDwords['dword12'];\r
2392                         $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");\r
2393                         $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");\r
2394                         $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");\r
2395                         $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");\r
2396                         $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");\r
2397                         $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");\r
2398                         $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");\r
2399                         $this->debug_message("*** p2p: header Flag = $hdr_Flag");\r
2400                         $this->debug_message("*** p2p: header AckID = $hdr_AckID");\r
2401                         $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");\r
2402                         $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");\r
2403                         $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");\r
2404                         if($hdr_Flag==2) {\r
2405                             //This is an ACK from SB ignore....\r
2406                             $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");\r
2407                             break;\r
2408                         }\r
2409                         $MsgBody=$this->linetoArray(substr($sMsg,48,-4));\r
2410                         $this->debug_message("*** p2p: body".print_r($MsgBody,true));\r
2411                         if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))\r
2412                         {\r
2413                             while(true)\r
2414                             {\r
2415                                 if($this->SB_readln()===false) break;\r
2416                             }\r
2417                             $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));\r
2418                             preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);\r
2419                             $BranchGUID=$Matches[1];\r
2420                             //it's an invite to send a display picture.\r
2421                             $new_id = ~$hdr_Identifier;\r
2422                             $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
2423                             $new_id,\r
2424                             0, 0,\r
2425                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2426                             0,\r
2427                             2,\r
2428                             $hdr_Identifier,\r
2429                             $hdr_AckID,\r
2430                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);\r
2431                             $footer = pack("L", 0);\r
2432                             $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
2433                             $len = strlen($message);\r
2434                             $this->SB_writeln("MSG $id D $len");\r
2435                             $id++;\r
2436                             $this->SB_writedata($message);\r
2437                             $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");\r
2438                             $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));                            \r
2439                             $this->SB_readln();//Read ACK;                            \r
2440                             $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));\r
2441                             $new_id-=3;\r
2442                             //Send 200 OK message\r
2443                             $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);\r
2444                             $MessagePayload=\r
2445                                 "MSNSLP/1.0 200 OK\r\n".\r
2446                                 "To: <msnmsgr:".$from_email.">\r\n".\r
2447                                 "From: <msnmsgr:".$this->user.">\r\n".\r
2448                                 "Via: ".$MsgBody['Via']."\r\n".\r
2449                                 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".\r
2450                                 "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
2451                                 "Max-Forwards: 0\r\n".\r
2452                                 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".\r
2453                                 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".\r
2454                             $MessageContent;\r
2455                             $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
2456                             $hdr_TotalDataSizeHigh=0;\r
2457                             $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
2458                             $new_id,\r
2459                             0, 0,\r
2460                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2461                             strlen($MessagePayload),\r
2462                             0,\r
2463                             rand(),\r
2464                             0,\r
2465                             0,0);\r
2466 \r
2467                             $message =\r
2468                                 "MIME-Version: 1.0\r\n".\r
2469                                 "Content-Type: application/x-msnmsgrp2p\r\n".\r
2470                                 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
2471                             $this->SB_writeln("MSG $id D ".strlen($message));\r
2472                             $id++;\r
2473                             $this->SB_writedata($message);\r
2474                             $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));\r
2475                             $this->SB_readln();//Read ACK;\r
2476                             \r
2477                             $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));\r
2478                             //send Data preparation message\r
2479                             //send 4 null bytes as data\r
2480                             $hdr_TotalDataSizeLow=4;\r
2481                             $hdr_TotalDataSizeHigh=0;\r
2482                             $new_id++;\r
2483                             $hdr = pack("LLLLLLLLLLLL",\r
2484                             $MsgBody['SessionID'],\r
2485                             $new_id,\r
2486                             0, 0,\r
2487                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2488                             $hdr_TotalDataSizeLow,\r
2489                             0,\r
2490                             rand(),\r
2491                             0,\r
2492                             0,0);\r
2493                             $message =\r
2494                                 "MIME-Version: 1.0\r\n".\r
2495                                 "Content-Type: application/x-msnmsgrp2p\r\n".\r
2496                                 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";\r
2497                             $this->SB_writeln("MSG $id D ".strlen($message));\r
2498                             $id++;\r
2499                             $this->SB_writedata($message);\r
2500                             $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));\r
2501                             $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));\r
2502                             $this->SB_readln();//Read ACK;\r
2503 \r
2504                             //send Data Content..\r
2505                             $footer=pack('N',1);\r
2506                             $new_id++;\r
2507                             $FileSize=filesize($PictureFilePath);\r
2508                             if($hTitle=fopen($PictureFilePath,'rb'))\r
2509                             {\r
2510                                 $Offset=0;\r
2511                                 //$new_id++;\r
2512                                 while(!feof($hTitle))\r
2513                                 {\r
2514                                     $FileContent=fread($hTitle,1024);\r
2515                                     $FileContentSize=strlen($FileContent);\r
2516                                     $hdr = pack("LLLLLLLLLLLL",\r
2517                                     $MsgBody['SessionID'],\r
2518                                     $new_id,\r
2519                                     $Offset, 0,\r
2520                                     $FileSize,0,\r
2521                                     $FileContentSize,\r
2522                                     0x20,\r
2523                                     rand(),\r
2524                                     0,\r
2525                                     0,0\r
2526                                     );\r
2527                                     $message =\r
2528                                         "MIME-Version: 1.0\r\n".\r
2529                                         "Content-Type: application/x-msnmsgrp2p\r\n".\r
2530                                         "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";\r
2531                                     $this->SB_writeln("MSG $id D ".strlen($message));\r
2532                                     $id++;\r
2533                                     $this->SB_writedata($message);\r
2534                                     $this->debug_message("*** p2p: dump send Data Content message  $Offset / $FileSize :\n".$this->dump_binary($message));\r
2535                                     $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));\r
2536                                     //$this->SB_readln();//Read ACK;\r
2537                                     $Offset+=$FileContentSize;\r
2538                                 }\r
2539                             }\r
2540                             //Send Bye\r
2541                             /*\r
2542                             $MessageContent="\r\n".pack("C", 0);\r
2543                             $MessagePayload=\r
2544                                 "BYE MSNMSGR:MSNSLP/1.0\r\n".\r
2545                                 "To: <msnmsgr:$from_email>\r\n".\r
2546                                 "From: <msnmsgr:".$this->user.">\r\n".\r
2547                                 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".                            \r
2548                                 "CSeq: 0\r\n".\r
2549                                 "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
2550                                 "Max-Forwards: 0\r\n".\r
2551                                 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".\r
2552                                 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;\r
2553                             $footer=pack('N',0);\r
2554                             $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
2555                             $hdr_TotalDataSizeHigh=0;\r
2556                             $new_id++;\r
2557                             $hdr = pack("LLLLLLLLLLLL", \r
2558                             0,\r
2559                             $new_id,\r
2560                             0, 0,\r
2561                             $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2562                             0,\r
2563                             0,\r
2564                             rand(),\r
2565                             0,\r
2566                             0,0);\r
2567                             $message =\r
2568                                         "MIME-Version: 1.0\r\n".\r
2569                                         "Content-Type: application/x-msnmsgrp2p\r\n".\r
2570                                         "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
2571                             $this->SB_writeln("MSG $id D ".strlen($message));\r
2572                             $id++;\r
2573                             $this->SB_writedata($message);\r
2574                             $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));\r
2575                             */\r
2576                             break;\r
2577                         }\r
2578                         //TODO:\r
2579                         //if ($hdr_Flag == 2) {\r
2580                         // just send ACK...\r
2581                         //    $this->SB_writeln("ACK $id");\r
2582                         //    break;\r
2583                         //}\r
2584                         if ($hdr_SessionID == 4) {\r
2585                             // ignore?\r
2586                             $this->debug_message("*** p2p: ignore flag 4");\r
2587                             break;\r
2588                         }\r
2589                         $finished = false;\r
2590                         if ($hdr_TotalDataSizeHigh == 0) {\r
2591                             // only 32 bites size\r
2592                             if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)\r
2593                             $finished = true;\r
2594                         }\r
2595                         else {\r
2596                             // we won't accept any file transfer\r
2597                             // so I think we won't get any message size need to use 64 bits\r
2598                             // 64 bits size here, can't count directly...\r
2599                             $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);\r
2600                             $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);\r
2601                             $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);\r
2602                             $now_size = bcadd($dataoffset, $messagelength);\r
2603                             if (bccomp($now_size, $totalsize) >= 0)\r
2604                             $finished = true;\r
2605                         }\r
2606                         if (!$finished) {\r
2607                             // ignore not finished split packet\r
2608                             $this->debug_message("*** p2p: ignore split packet, not finished");\r
2609                             break;\r
2610                         }\r
2611                         //$new_id = ~$hdr_Identifier;\r
2612                         /*\r
2613                          $new_id++;\r
2614                          $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
2615                          $new_id,\r
2616                          0, 0,\r
2617                          $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
2618                          0,\r
2619                          2,\r
2620                          $hdr_Identifier,\r
2621                          $hdr_AckID,\r
2622                          $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);\r
2623                          $footer = pack("L", 0);\r
2624                          $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
2625                          $len = strlen($message);\r
2626                          $this->SB_writeln("MSG $id D $len");\r
2627                          $id++;\r
2628                          $this->SB_writedata($message);\r
2629                          $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");\r
2630                          $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));\r
2631                          */\r
2632                         break;\r
2633                     }\r
2634                     $this->log_message("*** MSG from $from_email: $sMsg");\r
2635                     $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
2636                     break;\r
2637                 case '217':\r
2638                     $this->log_message("*** User $user is offline. Try OIM.");\r
2639                     foreach($this->SwitchBoardMessageQueue as $Message)\r
2640                     $this->SendMessage($Message,"$user@Offline");\r
2641                     $SessionEnd=true;\r
2642                     break;\r
2643                 default:\r
2644                     if (is_numeric($code))\r
2645                     {\r
2646                         $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
2647                         $this->debug_message("*** SB: $this->error");\r
2648                         $SessionEnd=true;\r
2649                     }\r
2650                     break;\r
2651             }\r
2652             if(!$this->IsIgnoreMail($user)) $LastActive = time();\r
2653         }\r
2654         if (feof($this->SBFp))\r
2655         {\r
2656             // lost connection? error? try OIM later\r
2657             @fclose($this->SBFp);\r
2658             return false;\r
2659         }\r
2660         $this->SB_writeln("OUT");\r
2661         @fclose($this->SBFp);\r
2662         return true;\r
2663     }\r
2664     private function switchboard_control($ip, $port, $cki_code, $user, $Messages)\r
2665     {\r
2666         $this->SwitchBoardProcess=1;\r
2667         $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");\r
2668         $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
2669         if (!$this->SBFp)\r
2670         {\r
2671             $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
2672             return false;\r
2673         }\r
2674         return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));\r
2675     }\r
2676     private function switchboard_ring($ip, $port, $sid, $ticket,$user)\r
2677     {\r
2678         $this->SwitchBoardProcess=2;\r
2679         $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");\r
2680         $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
2681         if (!$this->SBFp)\r
2682         {\r
2683             $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
2684             return false;\r
2685         }\r
2686         return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));\r
2687     }\r
2688 \r
2689     private function sendOIM($to, $sMessage, $lockkey)\r
2690     {\r
2691         $XML = '<?xml version="1.0" encoding="utf-8"?>\r
2692 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
2693                xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
2694                xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
2695 <soap:Header>\r
2696   <From memberName="'.$this->user.'"\r
2697         friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="\r
2698         xml:lang="zh-TW"\r
2699         proxy="MSNMSGR"\r
2700         xmlns="http://messenger.msn.com/ws/2004/09/oim/"\r
2701         msnpVer="'.$this->protocol.'"\r
2702         buildVer="'.$this->buildver.'"/>\r
2703   <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
2704   <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"\r
2705           appid="'.$this->prod_id.'"\r
2706           lockkey="'.$lockkey.'"\r
2707           xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
2708   <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">\r
2709     <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>\r
2710     <MessageNumber>1</MessageNumber>\r
2711   </Sequence>\r
2712 </soap:Header>\r
2713 <soap:Body>\r
2714   <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>\r
2715   <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0\r
2716 Content-Type: text/plain; charset=UTF-8\r
2717 Content-Transfer-Encoding: base64\r
2718 X-OIM-Message-Type: OfflineMessage\r
2719 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}\r
2720 X-OIM-Sequence-Num: 1\r
2721 \r
2722 '.chunk_split(base64_encode($sMessage)).'\r
2723   </Content>\r
2724 </soap:Body>\r
2725 </soap:Envelope>';\r
2726 \r
2727         $header_array = array(\r
2728             'SOAPAction: '.$this->oim_send_soap,\r
2729             'Content-Type: text/xml',\r
2730             'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
2731             );\r
2732 \r
2733             $this->debug_message("*** URL: $this->oim_send_url");\r
2734             $this->debug_message("*** Sending SOAP:\n$XML");\r
2735             $curl = curl_init();\r
2736             curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);\r
2737             curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
2738             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
2739             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
2740             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
2741             if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
2742             curl_setopt($curl, CURLOPT_POST, 1);\r
2743             curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
2744             $data = curl_exec($curl);\r
2745             $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
2746             curl_close($curl);\r
2747             $this->debug_message("*** Get Result:\n$data");\r
2748 \r
2749             if ($http_code == 200) {\r
2750                 $this->debug_message("*** OIM sent for $to");\r
2751                 return true;\r
2752             }\r
2753 \r
2754             $challenge = false;\r
2755             $auth_policy = false;\r
2756             // the lockkey is invalid, authenticated fail, we need challenge it again\r
2757             // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>\r
2758             preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);\r
2759             if (count($matches) != 0) {\r
2760                 // yes, we get new LockKeyChallenge\r
2761                 $challenge = $matches[2];\r
2762                 $this->debug_message("*** OIM need new challenge ($challenge) for $to");\r
2763             }\r
2764             // auth policy error\r
2765             // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>\r
2766             preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);\r
2767             if (count($matches) != 0) {\r
2768                 $auth_policy = $matches[2];\r
2769                 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");\r
2770             }\r
2771             if ($auth_policy === false && $challenge === false) {\r
2772                 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>\r
2773                 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);\r
2774                 if (count($matches) == 0) {\r
2775                     // no error, we assume the OIM is sent\r
2776                     $this->debug_message("*** OIM sent for $to");\r
2777                     return true;\r
2778                 }\r
2779                 $err_code = $matches[2];\r
2780                 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>\r
2781                 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);\r
2782                 if (count($matches) > 0)\r
2783                 $err_msg = $matches[1];\r
2784                 else\r
2785                 $err_msg = '';\r
2786                 $this->debug_message("*** OIM failed for $to");\r
2787                 $this->debug_message("*** OIM Error code: $err_code");\r
2788                 $this->debug_message("*** OIM Error Message: $err_msg");\r
2789                 return false;\r
2790             }\r
2791             return array('challenge' => $challenge, 'auth_policy' => $auth_policy);\r
2792     }\r
2793 \r
2794     // read data for specified size\r
2795     private function ns_readdata($size) {\r
2796         $data = '';\r
2797         $count = 0;\r
2798         while (!feof($this->NSfp)) {\r
2799             $buf = @fread($this->NSfp, $size - $count);\r
2800             $data .= $buf;\r
2801             $count += strlen($buf);\r
2802             if ($count >= $size) break;\r
2803         }\r
2804         $this->debug_message("NS: data ($size/$count) <<<\n$data");\r
2805         return $data;\r
2806     }\r
2807 \r
2808     // read one line\r
2809     private function ns_readln() {\r
2810         $data = @fgets($this->NSfp, 4096);\r
2811         if ($data !== false) {\r
2812             $data = trim($data);\r
2813             $this->debug_message("NS: <<< $data");\r
2814         }\r
2815         return $data;\r
2816     }\r
2817 \r
2818     // write to server, append \r\n, also increase id\r
2819     private function ns_writeln($data) {\r
2820         @fwrite($this->NSfp, $data."\r\n");\r
2821         $this->debug_message("NS: >>> $data");\r
2822         $this->id++;\r
2823         return;\r
2824     }\r
2825 \r
2826     // write data to server\r
2827     private function ns_writedata($data) {\r
2828         @fwrite($this->NSfp, $data);\r
2829         $this->debug_message("NS: >>> $data");\r
2830         return;\r
2831     }\r
2832 \r
2833     // read data for specified size for SB\r
2834     private function sb_readdata($size) {\r
2835         $data = '';\r
2836         $count = 0;\r
2837         while (!feof($this->SBFp)) {\r
2838             $buf = @fread($this->SBFp, $size - $count);\r
2839             $data .= $buf;\r
2840             $count += strlen($buf);\r
2841             if ($count >= $size) break;\r
2842         }\r
2843         $this->debug_message("SB: data ($size/$count) <<<\n$data");\r
2844         return $data;\r
2845     }\r
2846 \r
2847     // read one line for SB\r
2848     private function sb_readln() {\r
2849         $data = @fgets($this->SBFp, 4096);\r
2850         if ($data !== false) {\r
2851             $data = trim($data);\r
2852             $this->debug_message("SB: <<< $data");\r
2853         }\r
2854         return $data;\r
2855     }\r
2856 \r
2857     // write to server for SB, append \r\n, also increase id\r
2858     // switchboard server only accept \r\n, it will lost connection if just \n only\r
2859     private function sb_writeln($data) {\r
2860         @fwrite($this->SBFp, $data."\r\n");\r
2861         $this->debug_message("SB: >>> $data");\r
2862         $this->id++;\r
2863         return;\r
2864     }\r
2865 \r
2866     // write data to server\r
2867     private function sb_writedata($data) {\r
2868         @fwrite($this->SBFp, $data);\r
2869         $this->debug_message("SB: >>> $data");\r
2870         return;\r
2871     }\r
2872 \r
2873     // show debug information\r
2874     function debug_message($str) {\r
2875         if (!$this->debug) return;\r
2876         if($this->debug===STDOUT) echo $str."\n";\r
2877         /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';\r
2878         $fp = fopen($fname, 'at');\r
2879         if ($fp) {\r
2880             fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");\r
2881             fclose($fp);\r
2882             return;\r
2883         }*/\r
2884         // still show debug information, if we can't open log_file\r
2885         echo $str."\n";\r
2886         return;\r
2887     }\r
2888 \r
2889     function dump_binary($str) {\r
2890         $buf = '';\r
2891         $a_str = '';\r
2892         $h_str = '';\r
2893         $len = strlen($str);\r
2894         for ($i = 0; $i < $len; $i++) {\r
2895             if (($i % 16) == 0) {\r
2896                 if ($buf !== '') {\r
2897                     $buf .= "$h_str $a_str\n";\r
2898                 }\r
2899                 $buf .= sprintf("%04X:", $i);\r
2900                 $a_str = '';\r
2901                 $h_str = '';\r
2902             }\r
2903             $ch = ord($str[$i]);\r
2904             if ($ch < 32)\r
2905             $a_str .= '.';\r
2906             else\r
2907             $a_str .= chr($ch);\r
2908             $h_str .= sprintf(" %02X", $ch);\r
2909         }\r
2910         if ($h_str !== '')\r
2911         $buf .= "$h_str $a_str\n";\r
2912         return $buf;\r
2913     }\r
2914 \r
2915     // write log\r
2916     function log_message($str) {\r
2917         /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';\r
2918         $fp = fopen($fname, 'at');\r
2919         if ($fp) {\r
2920             fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");\r
2921             fclose($fp);\r
2922         }*/\r
2923         $this->debug_message($str);\r
2924         return;\r
2925     }\r
2926     /**\r
2927      *\r
2928      * @param $FilePath 圖檔路徑\r
2929      * @param $Type     檔案類型 3=>大頭貼,2表情圖案    \r
2930      * @return array\r
2931      */\r
2932     private function MsnObj($FilePath,$Type=3)\r
2933     {\r
2934         if(!($FileSize=filesize($FilePath))) return '';\r
2935         $Location=md5($FilePath);\r
2936         $Friendly=md5($FilePath.$Type);\r
2937         if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];\r
2938         $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));\r
2939         $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));\r
2940         $this->MsnObjArray[$Location]=$FilePath;\r
2941         $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';\r
2942         $this->MsnObjMap[$Location]=$MsnObj;\r
2943         $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");\r
2944         return $MsnObj;\r
2945     }\r
2946     private function linetoArray($lines) {\r
2947         $lines=str_replace("\r",'',$lines);\r
2948         $lines=explode("\n",$lines);\r
2949         foreach($lines as $line) {\r
2950             if(!isset($line{3})) continue;\r
2951             list($Key,$Val)=explode(':',$line);\r
2952             $Data[trim($Key)]=trim($Val);\r
2953         }\r
2954         return $Data;\r
2955     }\r
2956     private function GetPictureFilePath($Context)\r
2957     {\r
2958         $MsnObj=base64_decode($Context);\r
2959         if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))\r
2960         $location=$Match[1];\r
2961         $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");\r
2962         if($location&&(isset($this->MsnObjArray[$location])))\r
2963         return $this->MsnObjArray[$location];\r
2964         return false;\r
2965     }\r
2966     private function GetMsnObjDefine($Message)\r
2967     {\r
2968         $DefineString='';\r
2969         if(is_array($this->Emotions))\r
2970         foreach($this->Emotions as $Pattern => $FilePath)\r
2971         {\r
2972             if(strpos($Message,$Pattern)!==false)\r
2973             $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";\r
2974         }\r
2975         return $DefineString;\r
2976     }\r
2977     /**\r
2978      * Receive Message Overload Function\r
2979      * @param $Sender\r
2980      * @param $Message\r
2981      * @param $Network   1 => msn , 32 =>yahoo\r
2982      * @param $IsOIM\r
2983      * @return unknown_type\r
2984      */\r
2985     protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}\r
2986     /**\r
2987      * Remove Us From Member List Overload Function\r
2988      * @param $User\r
2989      * @param $Message\r
2990      * @param $Network   1 => msn , 32 =>yahoo\r
2991      * @return unknown_type\r
2992      */\r
2993     protected function RemoveUsFromMemberList($User,$Network){}\r
2994     /**\r
2995      * Add Us to Member List Overload Function\r
2996      * @param $User\r
2997      * @param $Message\r
2998      * @param $Network   1 => msn , 32 =>yahoo\r
2999      * @return unknown_type\r
3000      */\r
3001     protected function AddUsToMemberList($User,$Network){}\r
3002     \r
3003     public function signon() {\r
3004         $this->log_message("*** try to connect to MSN network");\r
3005         while(!$this->connect($this->user, $this->password))\r
3006         {\r
3007             $this->log_message("!!! Can't connect to server: $this->error");\r
3008             $this->callHandler('ConnectFailed', NULL);\r
3009             $this->NSRetryWait($this->retry_wait);\r
3010         }\r
3011         $this->UpdateContacts();\r
3012         $this->LastPing=time();\r
3013         $this->log_message("*** connected, wait for command");\r
3014         $start_tm = time();\r
3015         $ping_tm = time();\r
3016         stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
3017         $this->aContactList = $this->getMembershipList();\r
3018         if ($this->update_pending) {\r
3019             if (is_array($this->aContactList)) {\r
3020                 $pending = 'Pending';\r
3021                 foreach ($this->aContactList as $u_domain => $aUserList) {\r
3022                     foreach ($aUserList as $u_name => $aNetworks) {\r
3023                         foreach ($aNetworks as $network => $aData) {\r
3024                             if (isset($aData[$pending])) {\r
3025                                 // pending list\r
3026                                 $cnt = 0;\r
3027                                 foreach (array('Allow', 'Reverse') as $list) {\r
3028                                     if (isset($aData[$list]))\r
3029                                     $cnt++;\r
3030                                     else {\r
3031                                         if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
3032                                             $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
3033                                             $cnt++;\r
3034                                         }\r
3035                                     }\r
3036                                 }\r
3037                                 if ($cnt >= 2) {\r
3038                                     $id = $aData[$pending];\r
3039                                     // we can delete it from pending now\r
3040                                     if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
3041                                     unset($this->aContactList[$u_domain][$u_name][$network][$pending]);\r
3042                                 }\r
3043                             }\r
3044                             else {\r
3045                                 // sync list\r
3046                                 foreach (array('Allow', 'Reverse') as $list) {\r
3047                                     if (!isset($aData[$list])) {\r
3048                                         if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
3049                                         $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
3050                                     }\r
3051                                 }\r
3052                             }\r
3053                         }\r
3054                     }\r
3055                 }\r
3056             }\r
3057         }\r
3058         $n = 0;\r
3059         $sList = '';\r
3060         $len = 0;\r
3061         if (is_array($this->aContactList)) {\r
3062             foreach ($this->aContactList as $u_domain => $aUserList) {\r
3063                 $str = '<d n="'.$u_domain.'">';\r
3064                 $len += strlen($str);\r
3065                 if ($len > 7400) {\r
3066                     $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
3067                     $n++;\r
3068                     $sList = '';\r
3069                     $len = strlen($str);\r
3070                 }\r
3071                 $sList .= $str;\r
3072                 foreach ($aUserList as $u_name => $aNetworks) {\r
3073                     foreach ($aNetworks as $network => $status) {\r
3074                         $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
3075                         $len += strlen($str);\r
3076                         // max: 7500, but <ml l="1"></d></ml> is 19,\r
3077                         // so we use 7475\r
3078                         if ($len > 7475) {\r
3079                             $sList .= '</d>';\r
3080                             $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
3081                             $n++;\r
3082                             $sList = '<d n="'.$u_domain.'">'.$str;\r
3083                             $len = strlen($sList);\r
3084                         }\r
3085                         else\r
3086                         $sList .= $str;\r
3087                     }\r
3088                 }\r
3089                 $sList .= '</d>';\r
3090             }\r
3091         }\r
3092         $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
3093         // NS: >>> BLP {id} BL\r
3094         $this->ns_writeln("BLP $this->id BL");\r
3095         foreach ($this->aADL as $str) {\r
3096             $len = strlen($str);\r
3097             // NS: >>> ADL {id} {size}\r
3098             $this->ns_writeln("ADL $this->id $len");\r
3099             $this->ns_writedata($str);\r
3100         }\r
3101         // NS: >>> PRP {id} MFN name\r
3102         if ($this->alias == '') $this->alias = $user;\r
3103         $aliasname = rawurlencode($this->alias);\r
3104         $this->ns_writeln("PRP $this->id MFN $aliasname");\r
3105         //設定個人大頭貼\r
3106         //$MsnObj=$this->PhotoStckObj();\r
3107         // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
3108         $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
3109         if($this->PhotoStickerFile!==false)\r
3110             $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
3111         // NS: >>> UUX {id} length\r
3112         $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
3113         $len = strlen($str);\r
3114         $this->ns_writeln("UUX $this->id $len");\r
3115         $this->ns_writedata($str);\r
3116     }\r
3117     \r
3118     public function NSreceive() {\r
3119         $this->log_message("*** startup ***");\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->callHandler('Reconnect', NULL);\r
3124             $this->signon();\r
3125         }\r
3126         \r
3127         $data = $this->ns_readln();\r
3128         if($data === false) {\r
3129             // There was no data / an error when reading from the socket so reconnect\r
3130             $this->callHandler('Reconnect', NULL);\r
3131             $this->signon();\r
3132         } else {\r
3133             switch (substr($data,0,3))\r
3134             {\r
3135                 case 'SBS':\r
3136                     // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
3137                     // NS: <<< SBS 0 null\r
3138                     break;\r
3139     \r
3140                 case 'RFS':\r
3141                     // FIXME:\r
3142                     // NS: <<< RFS ???\r
3143                     // refresh ADL, so we re-send it again\r
3144                     if (is_array($this->aADL)) {\r
3145                         foreach ($this->aADL as $str) {\r
3146                             $len = strlen($str);\r
3147                             // NS: >>> ADL {id} {size}\r
3148                             $this->ns_writeln("ADL $this->id $len");\r
3149                             $this->ns_writedata($str);\r
3150                         }\r
3151                     }\r
3152                     break;\r
3153     \r
3154                 case 'LST':\r
3155                     // NS: <<< LST {email} {alias} 11 0\r
3156                     @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\r
3157                     @list($u_name, $u_domain) = @explode('@', $email);\r
3158                     if (!isset($this->aContactList[$u_domain][$u_name][1])) {\r
3159                         $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
3160                         $this->log_message("*** add to our contact list: $u_name@$u_domain");\r
3161                     }\r
3162                     break;\r
3163     \r
3164                 case 'ADL':\r
3165                     // randomly, we get ADL command, someome add us to their contact list for MSNP15\r
3166                     // NS: <<< ADL 0 {size}\r
3167                     @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);\r
3168                     if (is_numeric($size) && $size > 0)\r
3169                     {\r
3170                         $data = $this->ns_readdata($size);\r
3171                         preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
3172                         if (is_array($matches) && count($matches) > 0)\r
3173                         {\r
3174                             $u_domain = $matches[1];\r
3175                             $u_name = $matches[2];\r
3176                             $network = $matches[4];\r
3177                             if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
3178                             $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
3179                             else\r
3180                             {\r
3181                                 $re_login = false;\r
3182                                 $cnt = 0;\r
3183                                 foreach (array('Allow', 'Reverse') as $list)\r
3184                                 {\r
3185                                     if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
3186                                     {\r
3187                                         if ($re_login) {\r
3188                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
3189                                             continue;\r
3190                                         }\r
3191                                         $aTickets = $this->get_passport_ticket();\r
3192                                         if (!$aTickets || !is_array($aTickets)) {\r
3193                                             // failed to login? ignore it\r
3194                                             $this->log_message("*** can't re-login, something wrong here");\r
3195                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
3196                                             continue;\r
3197                                         }\r
3198                                         $re_login = true;\r
3199                                         $this->ticket = $aTickets;\r
3200                                         $this->log_message("**** get new ticket, try it again");\r
3201                                         if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
3202                                         {\r
3203                                             $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
3204                                             continue;\r
3205                                         }\r
3206                                     }\r
3207                                     $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
3208                                     $cnt++;\r
3209                                 }\r
3210                                 $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
3211                             }\r
3212                             $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
3213                             $len = strlen($str);\r
3214                         }\r
3215                         else\r
3216                         $this->log_message("*** someone add us to their list: $data");\r
3217                         $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
3218                     }\r
3219                     break;\r
3220     \r
3221                 case 'RML':\r
3222                     // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
3223                     // NS: <<< RML 0 {size}\r
3224                     @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);\r
3225                     if (is_numeric($size) && $size > 0)\r
3226                     {\r
3227                         $data = $this->ns_readdata($size);\r
3228                         preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
3229                         if (is_array($matches) && count($matches) > 0)\r
3230                         {\r
3231                             $u_domain = $matches[1];\r
3232                             $u_name = $matches[2];\r
3233                             $network = $matches[4];\r
3234                             if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
3235                             {\r
3236                                 $aData = $this->aContactList[$u_domain][$u_name][$network];\r
3237                                 foreach ($aData as $list => $id)\r
3238                                 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
3239                                 unset($this->aContactList[$u_domain][$u_name][$network]);\r
3240                                 $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
3241                             }\r
3242                             else\r
3243                             $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
3244                             $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);\r
3245                         }\r
3246                         else\r
3247                         $this->log_message("*** someone remove us from their list: $data");\r
3248                     }\r
3249                     break;\r
3250     \r
3251                 case 'MSG':\r
3252                     // randomly, we get MSG notification from server\r
3253                     // NS: <<< MSG Hotmail Hotmail {size}\r
3254                     @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);\r
3255                     if (is_numeric($size) && $size > 0) {\r
3256                         $data = $this->ns_readdata($size);\r
3257                         $aLines = @explode("\n", $data);\r
3258                         $header = true;\r
3259                         $ignore = false;\r
3260                         $maildata = '';\r
3261                         foreach ($aLines as $line) {\r
3262                             $line = rtrim($line);\r
3263                             if ($header) {\r
3264                                 if ($line === '') {\r
3265                                     $header = false;\r
3266                                     continue;\r
3267                                 }\r
3268                                 if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
3269                                     if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&\r
3270                                     strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
3271                                         // we just need text/x-msmsgsinitialmdatanotification\r
3272                                         // or text/x-msmsgsoimnotification\r
3273                                         $ignore = true;\r
3274                                         break;\r
3275                                     }\r
3276                                 }\r
3277                                 continue;\r
3278                             }\r
3279                             if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
3280                                 $maildata = trim(substr($line, 10));\r
3281                                 break;\r
3282                             }\r
3283                         }\r
3284                         if ($ignore) {\r
3285                             $this->log_message("*** ingnore MSG for: $line");\r
3286                             break;\r
3287                         }\r
3288                         if ($maildata == '') {\r
3289                             $this->log_message("*** ingnore MSG not for OIM");\r
3290                             break;\r
3291                         }\r
3292                         $re_login = false;\r
3293                         if (strcasecmp($maildata, 'too-large') == 0) {\r
3294                             $this->log_message("*** large mail-data, need to get the data via SOAP");\r
3295                             $maildata = $this->getOIM_maildata();\r
3296                             if ($maildata === false) {\r
3297                                 $this->log_message("*** can't get mail-data via SOAP");\r
3298                                 // maybe we need to re-login again\r
3299                                 $aTickets = $this->get_passport_ticket();\r
3300                                 if (!$aTickets || !is_array($aTickets)) {\r
3301                                     // failed to login? ignore it\r
3302                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
3303                                     break;\r
3304                                 }\r
3305                                 $re_login = true;\r
3306                                 $this->ticket = $aTickets;\r
3307                                 $this->log_message("**** get new ticket, try it again");\r
3308                                 $maildata = $this->getOIM_maildata();\r
3309                                 if ($maildata === false) {\r
3310                                     $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
3311                                     break;\r
3312                                 }\r
3313                             }\r
3314                         }\r
3315                         // could be a lots of <M>...</M>, so we can't use preg_match here\r
3316                         $p = $maildata;\r
3317                         $aOIMs = array();\r
3318                         while (1) {\r
3319                             $start = strpos($p, '<M>');\r
3320                             $end = strpos($p, '</M>');\r
3321                             if ($start === false || $end === false || $start > $end) break;\r
3322                             $end += 4;\r
3323                             $sOIM = substr($p, $start, $end - $start);\r
3324                             $aOIMs[] = $sOIM;\r
3325                             $p = substr($p, $end);\r
3326                         }\r
3327                         if (count($aOIMs) == 0) {\r
3328                             $this->log_message("*** ingnore empty OIM");\r
3329                             break;\r
3330                         }\r
3331                         foreach ($aOIMs as $maildata) {\r
3332                             // T: 11 for MSN, 13 for Yahoo\r
3333                             // S: 6 for MSN, 7 for Yahoo\r
3334                             // RT: the datetime received by server\r
3335                             // RS: already read or not\r
3336                             // SZ: size of message\r
3337                             // E: sender\r
3338                             // I: msgid\r
3339                             // F: always 00000000-0000-0000-0000-000000000009\r
3340                             // N: sender alias\r
3341                             preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
3342                             if (count($matches) == 0) {\r
3343                                 $this->log_message("*** ingnore OIM maildata without <T>type</T>");\r
3344                                 continue;\r
3345                             }\r
3346                             $oim_type = $matches[1];\r
3347                             if ($oim_type = 13)\r
3348                             $network = 32;\r
3349                             else\r
3350                             $network = 1;\r
3351                             preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
3352                             if (count($matches) == 0) {\r
3353                                 $this->log_message("*** ingnore OIM maildata without <E>sender</E>");\r
3354                                 continue;\r
3355                             }\r
3356                             $oim_sender = $matches[1];\r
3357                             preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
3358                             if (count($matches) == 0) {\r
3359                                 $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");\r
3360                                 continue;\r
3361                             }\r
3362                             $oim_msgid = $matches[1];\r
3363                             preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
3364                             $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
3365                             preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
3366                             $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
3367                             $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
3368                             $sMsg = $this->getOIM_message($oim_msgid);\r
3369                             if ($sMsg === false) {\r
3370                                 $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
3371                                 if ($re_login) {\r
3372                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
3373                                     continue;\r
3374                                 }\r
3375                                 $aTickets = $this->get_passport_ticket();\r
3376                                 if (!$aTickets || !is_array($aTickets)) {\r
3377                                     // failed to login? ignore it\r
3378                                     $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
3379                                     continue;\r
3380                                 }\r
3381                                 $re_login = true;\r
3382                                 $this->ticket = $aTickets;\r
3383                                 $this->log_message("**** get new ticket, try it again");\r
3384                                 $sMsg = $this->getOIM_message($oim_msgid);\r
3385                                 if ($sMsg === false) {\r
3386                                     $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
3387                                     continue;\r
3388                                 }\r
3389                             }\r
3390                             $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
3391     \r
3392                             //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
3393                             $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));\r
3394                         }\r
3395                     }\r
3396                     break;\r
3397     \r
3398                 case 'UBM':\r
3399                     // randomly, we get UBM, this is the message from other network, like Yahoo!\r
3400                     // NS: <<< UBM {email} $network $type {size}\r
3401                     @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);\r
3402                     if (is_numeric($size) && $size > 0)\r
3403                     {\r
3404                         $data = $this->ns_readdata($size);\r
3405                         $aLines = @explode("\n", $data);\r
3406                         $header = true;\r
3407                         $ignore = false;\r
3408                         $sMsg = '';\r
3409                         foreach ($aLines as $line) {\r
3410                             $line = rtrim($line);\r
3411                             if ($header) {\r
3412                                 if ($line === '') {\r
3413                                     $header = false;\r
3414                                     continue;\r
3415                                 }\r
3416                                 if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
3417                                     $ignore = true;\r
3418                                     break;\r
3419                                 }\r
3420                                 continue;\r
3421                             }\r
3422                             $aSubLines = @explode("\r", $line);\r
3423                             foreach ($aSubLines as $str) {\r
3424                                 if ($sMsg !== '')\r
3425                                 $sMsg .= "\n";\r
3426                                 $sMsg .= $str;\r
3427                             }\r
3428                         }\r
3429                         if($ignore)\r
3430                         {\r
3431                             $this->log_message("*** ingnore from $from_email: $line");\r
3432                             break;\r
3433                         }\r
3434                         $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
3435                         //$this->ReceivedMessage($from_email,$sMsg,$network,false);\r
3436                         $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));\r
3437                     }\r
3438                     break;\r
3439     \r
3440                 case 'UBX':\r
3441                     // randomly, we get UBX notification from server\r
3442                     // NS: <<< UBX email {network} {size}\r
3443                     @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);\r
3444                     // we don't need the notification data, so just ignore it\r
3445                     if (is_numeric($size) && $size > 0)\r
3446                     $this->ns_readdata($size);\r
3447                     break;\r
3448     \r
3449                 case 'CHL':\r
3450                     // randomly, we'll get challenge from server\r
3451                     // NS: <<< CHL 0 {code}\r
3452                     @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);\r
3453                     $fingerprint = $this->getChallenge($chl_code);\r
3454                     // NS: >>> QRY {id} {product_id} 32\r
3455                     // NS: >>> fingerprint\r
3456                     $this->ns_writeln("QRY $this->id $this->prod_id 32");\r
3457                     $this->ns_writedata($fingerprint);\r
3458                     $this->ns_writeln("CHG $this->id NLN $this->clientid");                    \r
3459                     if($this->PhotoStickerFile!==false)\r
3460                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
3461                     break;\r
3462                 case 'CHG':\r
3463                     // NS: <<< CHG {id} {status} {code}\r
3464                     // ignore it\r
3465                     // change our status to online first\r
3466                     break;\r
3467     \r
3468                 case 'XFR':\r
3469                     // sometimes, NS will redirect to another NS\r
3470                     // MSNP9\r
3471                     // NS: <<< XFR {id} NS {server} 0 {server}\r
3472                     // MSNP15\r
3473                     // NS: <<< XFR {id} NS {server} U D\r
3474                     // for normal switchboard XFR\r
3475                     // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
3476                     @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);\r
3477                     @list($ip, $port) = @explode(':', $server);\r
3478                     if ($server_type != 'SB') {\r
3479                         // maybe exit?\r
3480                         // this connection will close after XFR\r
3481                         $this->NSLogout();\r
3482                         continue;\r
3483                     }\r
3484                     if(count($this->MessageQueue))\r
3485                     {\r
3486                         foreach($this->MessageQueue as $User => $Message)\r
3487                         {\r
3488                             //$this->ChildProcess[$ChildPid]\r
3489                             $this->log_message("*** XFR SB $User");\r
3490                             $pid=pcntl_fork();\r
3491                             if($pid)\r
3492                             {\r
3493                                 //Parrent Process\r
3494                                 $this->ChildProcess[$pid]=$User;\r
3495                                 break;\r
3496                             }\r
3497                             elseif($pid==-1)\r
3498                             {\r
3499                                 $this->log_message("*** Fork Error $User");\r
3500                                 break;\r
3501                             }\r
3502                             else\r
3503                             {\r
3504                                 //Child Process\r
3505                                 $this->log_message("*** Child Process Start for $User");\r
3506                                 unset($Message['XFRSent']);\r
3507                                 unset($Message['ReqTime']);\r
3508                                 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
3509                                 if ($bSBresult === false)\r
3510                                 {\r
3511                                     // error for switchboard\r
3512                                     $this->log_message("!!! error for sending message to ".$User);\r
3513                                 }\r
3514                                 die;\r
3515                             }\r
3516                         }\r
3517                         unset($this->MessageQueue[$User]);\r
3518                     }\r
3519                     /*\r
3520                      $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
3521                      if ($bSBresult === false) {\r
3522                      // error for switchboard\r
3523                      $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
3524                      $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
3525                      }*/\r
3526                     break;\r
3527                 case 'QNG':\r
3528                     // NS: <<< QNG {time}\r
3529                     @list(/* QNG */, $ping_wait) = @explode(' ', $data);\r
3530                     //if ($this->ping_wait == 0) $this->ping_wait = 50;\r
3531                     //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;\r
3532                     //Mod by Ricky Set Online\r
3533                     \r
3534                     $this->callHandler('Pong', $ping_wait);\r
3535                     break;\r
3536     \r
3537                 case 'RNG':\r
3538                     if($this->PhotoStickerFile!==false)\r
3539                         $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
3540                     else\r
3541                         $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
3542                     // someone is trying to talk to us\r
3543                     // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
3544                     $this->log_message("NS: <<< RNG $data");\r
3545                     @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
3546                     @list($sb_ip, $sb_port) = @explode(':', $server);\r
3547                     if($this->IsIgnoreMail($email))\r
3548                     {\r
3549                         $this->log_message("*** Ignore RNG from $email");\r
3550                         break;\r
3551                     }\r
3552                     $this->log_message("*** RING from $email, $sb_ip:$sb_port");\r
3553                     $this->addContact($email,1,$email, true);\r
3554                     $pid=pcntl_fork();\r
3555                     if($pid)\r
3556                     {\r
3557                         //Parrent Process\r
3558                         $this->ChildProcess[$pid]='RNG';\r
3559                         break;\r
3560                     }\r
3561                     elseif($pid==-1)\r
3562                     {\r
3563                         $this->log_message("*** Fork Error $User");\r
3564                         break;\r
3565                     }\r
3566                     else\r
3567                     {\r
3568                         //Child Process\r
3569                         $this->log_message("*** Ring Child Process Start for $User");\r
3570                         $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
3571                         die;\r
3572                     }\r
3573                     break;\r
3574                 case 'OUT':\r
3575                     // force logout from NS\r
3576                     // NS: <<< OUT xxx\r
3577                     $this->log_message("*** LOGOUT from NS");\r
3578                     return $this->NsLogout();\r
3579     \r
3580                 default:\r
3581                     $code = substr($data,0,3);\r
3582                     if (is_numeric($code)) {\r
3583                         $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
3584                         $this->debug_message("*** NS: $this->error");\r
3585     \r
3586                         return $this->NsLogout();\r
3587                     }\r
3588                     break;\r
3589             }\r
3590         }\r
3591     }\r
3592     \r
3593     public function sendMessageViaSB($message, $to) {\r
3594         $socket = $this->switchBoardSessions[$to]['socket'];\r
3595         $lastActive = $this->switchBoardSessions[$to]['lastActive'];\r
3596         $joined = $this->switchBoardSessions[$to]['joined'];\r
3597         \r
3598         //FIXME Probably not needed (we're not running in a loop anymore)\r
3599         /*if($this->kill_me)\r
3600         {\r
3601             $this->log_message("*** SB Okay, kill me now!");\r
3602             endSBSession($socket);\r
3603         }*/\r
3604         \r
3605         if(!$Joined) {\r
3606             // If our participant has not joined the session yet we can't message them!\r
3607             //TODO Check the behaviour of the queue runner when we return false\r
3608             return false;\r
3609         }\r
3610         \r
3611         $aMessage = $this->getMessage($Message);\r
3612         //CheckEmotion...\r
3613         $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
3614         if($MsnObjDefine !== '')\r
3615         {\r
3616             $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";\r
3617             $len = strlen($SendString);\r
3618             $this->SB_writeln("MSG $id N $len");\r
3619             $id++;\r
3620             $this->SB_writedata($SendString);\r
3621             $this->id++;\r
3622         }\r
3623         $len = strlen($aMessage);\r
3624         $this->SB_writeln("MSG $id N $len");\r
3625         \r
3626         // Increment the trID\r
3627         $this->switchBoardSessions[$to]['id']++;\r
3628         \r
3629         $this->SB_writedata($aMessage);\r
3630         \r
3631         // Don't close the SB session, we might as well leave it open\r
3632         \r
3633         return true;\r
3634     }\r
3635     \r
3636     //FIXME Not sure if this is needed?\r
3637     private function endSBSession($socket) {\r
3638         if (feof($this->SBFp))\r
3639         {\r
3640             // lost connection? error? try OIM later\r
3641             @fclose($this->SBFp);\r
3642             return false;\r
3643         }\r
3644         $this->SB_writeln("OUT");\r
3645         @fclose($this->SBFp);\r
3646         return true;\r
3647     }\r
3648     \r
3649     private function getSBSession($to) {\r
3650         \r
3651     }\r
3652     \r
3653     public function sendMessage($message, $to) {\r
3654         if($message != '') {\r
3655             list($name,$host,$network)=explode('@',$to);\r
3656             $network=$network==''?1:$network;\r
3657             \r
3658             if($network === 1 && isset($this->switchBoardSessions[$to])) {\r
3659                 $recipient = $name . $host;\r
3660                 $this->debug_message("*** Sending Message to $recipient using existing SB session");\r
3661                 return $this->sendMessageViaSB($message, $recipient);\r
3662             } else {\r
3663                 $this->debug_message("*** Not MSN network or no existing SB session");\r
3664                 //TODO implement creation of SB session etc\r
3665             }\r
3666         }\r
3667         return true;\r
3668     }\r
3669     \r
3670     /**\r
3671      * Sends a ping command\r
3672      * \r
3673      * Should be called about every 50 seconds\r
3674      */\r
3675     public function send_ping() {\r
3676         // NS: >>> PNG\r
3677         $this->ns_writeln("PNG");\r
3678     }\r
3679     \r
3680     public function getNSSocket() {\r
3681         return $this->NSfp;\r
3682     }\r
3683     \r
3684     // TODO Allow for multiple SB session sockets\r
3685     public function getSBSocket() {\r
3686         return $this->SBfp;\r
3687     }\r
3688     \r
3689     public function getSockets() {\r
3690         return array($this->NSfp, $this->SBfp);\r
3691     }\r
3692     \r
3693     /**\r
3694      * Calls User Handler\r
3695      *\r
3696      * Calls registered handler for a specific event.\r
3697      * \r
3698      * @param String $event Command (event) name (Rvous etc)\r
3699      * @param String $data Raw message from server\r
3700      * @see registerHandler\r
3701      * @return void\r
3702      */\r
3703     private function callHandler($event, $data) {\r
3704         if (isset($this->myEventHandlers[$event])) {\r
3705             call_user_func($this->myEventHandlers[$event], $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, ConnectFailed, Reconnect\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