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