--- /dev/null
+<?php\r
+/*\r
+\r
+MSN class ver 2.0 by Tommy Wu, Ricky Su\r
+License: GPL\r
+\r
+You can find MSN protocol from this site: http://msnpiki.msnfanatic.com/index.php/Main_Page\r
+\r
+This class support MSNP15 for send message. The PHP module needed:\r
+\r
+MSNP15: curl pcre mhash mcrypt bcmath\r
+\r
+Usually, this class will try to use MSNP15 if your system can support it, if your system can't support it,\r
+it will switch to use MSNP9. But if you use MSNP9, it won't support OIM (Offline Messages).\r
+*/\r
+\r
+class MSN {\r
+ private $debug;\r
+ private $timeout;\r
+ private $protocol = 'MSNP15';\r
+ private $passport_url = 'https://login.live.com/RST.srf';\r
+ private $buildver = '8.1.0178';\r
+ private $prod_key = 'PK}_A_0N_K%O?A9S';\r
+ private $prod_id = 'PROD0114ES4Z%Q5W';\r
+ private $login_method = 'SSO';\r
+ private $oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';\r
+ private $oim_send_soap = 'http://messenger.live.com/ws/2006/09/oim/Store2';\r
+ private $windows;\r
+ private $kill_me = false;\r
+ private $id;\r
+ private $ticket;\r
+ private $user = '';\r
+ private $password = '';\r
+ private $NSfp=false;\r
+ private $SBfp;\r
+ private $passport_policy = '';\r
+ private $alias;\r
+ private $psm;\r
+ private $use_ping;\r
+ private $retry_wait;\r
+ private $backup_file;\r
+ private $update_pending;\r
+ private $PhotoStickerFile=false;\r
+ private $Emotions=false;\r
+ private $MessageQueue=array();\r
+ private $ChildProcess=array();\r
+ private $MAXChildProcess=3;\r
+ private $ReqSBXFRTimeout=60;\r
+ private $SBTimeout=2;\r
+ private $LastPing;\r
+ private $ping_wait=50;\r
+ private $SBIdleTimeout=10;\r
+ private $SBStreamTimeout=10;\r
+ private $NSStreamTimeout=2;\r
+ private $MsnObjArray=array();\r
+ private $MsnObjMap=array();\r
+ private $SwitchBoardProcess=false; // false=>Main Process,1 => sb_control_process,2 => sb_ring_process\r
+ private $SwitchBoardSessionUser=false;\r
+ private $SwitchBoardMessageQueue=array();\r
+ private $ABAuthHeader;\r
+ private $ABService;\r
+ private $Contacts;\r
+\r
+ public $server = 'messenger.hotmail.com';\r
+ public $port = 1863;\r
+\r
+\r
+ public $clientid = '';\r
+\r
+ public $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';\r
+ public $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';\r
+ public $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';\r
+ public $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';\r
+ public $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';\r
+ public $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';\r
+\r
+ public $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';\r
+ public $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';\r
+\r
+ public $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';\r
+ public $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';\r
+\r
+ public $addcontact_url = 'https://contacts.msn.com/abservice/abservice.asmx';\r
+ public $addcontact_soap = 'http://www.msn.com/webservices/AddressBook/ABContactAdd';\r
+\r
+ public $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';\r
+ public $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';\r
+\r
+\r
+ public $error = '';\r
+\r
+ public $authed = false;\r
+\r
+ public $oim_try = 3;\r
+\r
+ public $log_file = '';\r
+\r
+ public $log_path = false;\r
+\r
+ public $font_fn = 'Arial';\r
+ public $font_co = '333333';\r
+ public $font_ef = '';\r
+\r
+\r
+ // the message length (include header) is limited (maybe since WLM 8.5 released)\r
+ // for WLM: 1664 bytes\r
+ // for YIM: 518 bytes\r
+ public $max_msn_message_len = 1664;\r
+ public $max_yahoo_message_len = 518;\r
+ \r
+ // Begin added for StatusNet\r
+ \r
+ private $aContactList = array();\r
+ private $switchBoardSessions = array();\r
+ \r
+ /** \r
+ * Event Handler Functions\r
+ */\r
+ private $myEventHandlers = array();\r
+ \r
+ // End added for StatusNet\r
+\r
+ private function Array2SoapVar($Array,$ReturnSoapVarObj=true,$TypeName=null,$TypeNameSpace=null)\r
+ {\r
+ $ArrayString='';\r
+ foreach($Array as $Key => $Val)\r
+ {\r
+ if($Key{0}==':') continue;\r
+ $Attrib='';\r
+ if(is_array($Val[':']))\r
+ {\r
+ foreach($Val[':'] as $AttribName => $AttribVal)\r
+ $Attrib.=" $AttribName='$AttribVal'";\r
+ }\r
+ if($Key{0}=='!')\r
+ {\r
+ //List Type Define\r
+ $Key=substr($Key,1);\r
+ foreach($Val as $ListKey => $ListVal)\r
+ {\r
+ if($ListKey{0}==':') continue;\r
+ if(is_array($ListVal)) $ListVal=$this->Array2SoapVar($ListVal,false);\r
+ elseif(is_bool($ListVal)) $ListVal=$ListVal?'true':'false';\r
+ $ArrayString.="<$Key$Attrib>$ListVal</$Key>";\r
+ }\r
+ continue;\r
+ }\r
+ if(is_array($Val)) $Val=$this->Array2SoapVar($Val,false);\r
+ elseif(is_bool($Val)) $Val=$Val?'true':'false';\r
+ $ArrayString.="<$Key$Attrib>$Val</$Key>";\r
+ }\r
+ if($ReturnSoapVarObj) return new SoapVar($ArrayString,XSD_ANYXML,$TypeName,$TypeNameSpace);\r
+ return $ArrayString;\r
+ }\r
+\r
+ public function End()\r
+ {\r
+ $this->log_message("*** someone kill me ***");\r
+ $this->kill_me=true;\r
+ }\r
+ public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C)\r
+ {\r
+ $this->user = $Configs['user'];\r
+ $this->password = $Configs['password'];\r
+ $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';\r
+ $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';\r
+ $my_add_function = isset($Configs['add_user_function']) ? $Configs['add_user_function'] : false;\r
+ $my_rem_function = isset($Configs['remove_user_function']) ? $Configs['remove_user_function'] : false;\r
+ $this->use_ping = isset($Configs['use_ping']) ? $Configs['use_ping'] : false;\r
+ $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;\r
+ $this->backup_file = isset($Configs['backup_file']) ? $Configs['backup_file'] : true;\r
+ $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;\r
+ $this->PhotoStickerFile=$Configs['PhotoSticker'];\r
+ if($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false)\r
+ {\r
+ foreach($this->Emotions as $EmotionFilePath)\r
+ $this->MsnObj($EmotionFilePath,$Type=2);\r
+ } \r
+ $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;\r
+ $this->timeout = $timeout;\r
+ // check support\r
+ if (!function_exists('curl_init')) throw new Exception("We need curl module!\n");\r
+ if (!function_exists('preg_match')) throw new Exception("We need pcre module!\n");\r
+ if (!function_exists('mhash')) throw new Exception("We need mhash module!\n");\r
+\r
+ if (!function_exists('mcrypt_cbc')) throw new Exception("We need mcrypt module!\n");\r
+ if (!function_exists('bcmod')) throw new Exception("We need bcmath module for $protocol!\n");\r
+\r
+ /*\r
+ http://msnpiki.msnfanatic.com/index.php/Client_ID\r
+ Client ID for MSN:\r
+ normal MSN 8.1 clientid is:\r
+ 01110110 01001100 11000000 00101100\r
+ = 0x764CC02C\r
+\r
+ we just use following:\r
+ * 0x04: Your client can send/receive Ink (GIF format)\r
+ * 0x08: Your client can send/recieve Ink (ISF format)\r
+ * 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
+ * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)\r
+ = 0x7000800C;\r
+ */\r
+ $this->clientid = $client_id;\r
+ $this->windows =(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');\r
+ $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));\r
+ }\r
+\r
+ private function get_passport_ticket($url = '')\r
+ {\r
+ $user = $this->user;\r
+ $password = htmlspecialchars($this->password);\r
+\r
+ if ($url === '')\r
+ $passport_url = $this->passport_url;\r
+ else\r
+ $passport_url = $url;\r
+\r
+ $XML = '<?xml version="1.0" encoding="UTF-8"?>\r
+<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"\r
+ xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"\r
+ xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"\r
+ xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"\r
+ xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"\r
+ xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"\r
+ xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">\r
+<Header>\r
+ <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">\r
+ <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>\r
+ <ps:BinaryVersion>4</ps:BinaryVersion>\r
+ <ps:UIVersion>1</ps:UIVersion>\r
+ <ps:Cookies></ps:Cookies>\r
+ <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>\r
+ </ps:AuthInfo>\r
+ <wsse:Security>\r
+ <wsse:UsernameToken Id="user">\r
+ <wsse:Username>'.$user.'</wsse:Username>\r
+ <wsse:Password>'.$password.'</wsse:Password>\r
+ </wsse:UsernameToken>\r
+ </wsse:Security>\r
+</Header>\r
+<Body>\r
+ <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">\r
+ <wst:RequestSecurityToken Id="RST0">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>http://Passport.NET/tb</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST1">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>messengerclear.live.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST2">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>messenger.msn.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST3">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>contacts.msn.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST4">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>messengersecure.live.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST5">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>spaces.live.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ <wst:RequestSecurityToken Id="RST6">\r
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r
+ <wsp:AppliesTo>\r
+ <wsa:EndpointReference>\r
+ <wsa:Address>storage.msn.com</wsa:Address>\r
+ </wsa:EndpointReference>\r
+ </wsp:AppliesTo>\r
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>\r
+ </wst:RequestSecurityToken>\r
+ </ps:RequestMultipleSecurityTokens>\r
+</Body>\r
+</Envelope>';\r
+\r
+ $this->debug_message("*** URL: $passport_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $passport_url);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ // sometimes, rediret to another URL\r
+ // MSNP15\r
+ //<faultcode>psf:Redirect</faultcode>\r
+ //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>\r
+ //<faultstring>Authentication Failure</faultstring>\r
+ if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {\r
+ $this->debug_message("*** Can't get passport ticket! http code = $http_code");\r
+ return false;\r
+ }\r
+ preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->debug_message("*** redirect, but can't get redirect URL!");\r
+ return false;\r
+ }\r
+ $redirect_url = $matches[1];\r
+ if ($redirect_url == $passport_url) {\r
+ $this->debug_message("*** redirect, but redirect to same URL!");\r
+ return false;\r
+ }\r
+ $this->debug_message("*** redirect to $redirect_url");\r
+ return $this->get_passport_ticket($redirect_url);\r
+ }\r
+\r
+ // sometimes, rediret to another URL, also return 200\r
+ // MSNP15\r
+ //<faultcode>psf:Redirect</faultcode>\r
+ //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>\r
+ //<faultstring>Authentication Failure</faultstring>\r
+ if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {\r
+ preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);\r
+ if (count($matches) != 0) {\r
+ $redirect_url = $matches[1];\r
+ if ($redirect_url == $passport_url) {\r
+ $this->debug_message("*** redirect, but redirect to same URL!");\r
+ return false;\r
+ }\r
+ $this->debug_message("*** redirect to $redirect_url");\r
+ return $this->get_passport_ticket($redirect_url);\r
+ }\r
+ }\r
+\r
+ // no Redurect faultcode or URL\r
+ // we should get the ticket here\r
+\r
+ // we need ticket and secret code\r
+ // RST1: messengerclear.live.com\r
+ // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>\r
+ // <wst:BinarySecret>binary secret</wst:BinarySecret>\r
+ // RST2: messenger.msn.com\r
+ // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>\r
+ // RST3: contacts.msn.com\r
+ // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>\r
+ // RST4: messengersecure.live.com\r
+ // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>\r
+ // RST5: spaces.live.com\r
+ // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>\r
+ // RST6: storage.msn.com\r
+ // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>\r
+ preg_match("#".\r
+ "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
+ "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".\r
+ "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
+ "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
+ "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
+ "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
+ "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".\r
+ "#",\r
+ $data, $matches);\r
+\r
+ // no ticket found!\r
+ if (count($matches) == 0) {\r
+ $this->debug_message("*** Can't get passport ticket!");\r
+ return false;\r
+ }\r
+\r
+ //$this->debug_message(var_export($matches, true));\r
+ // matches[0]: all data\r
+ // matches[1]: RST1 (messengerclear.live.com) ticket\r
+ // matches[2]: ...\r
+ // matches[3]: RST1 (messengerclear.live.com) binary secret\r
+ // matches[4]: ...\r
+ // matches[5]: RST2 (messenger.msn.com) ticket\r
+ // matches[6]: ...\r
+ // matches[7]: RST3 (contacts.msn.com) ticket\r
+ // matches[8]: ...\r
+ // matches[9]: RST4 (messengersecure.live.com) ticket\r
+ // matches[10]: ...\r
+ // matches[11]: RST5 (spaces.live.com) ticket\r
+ // matches[12]: ...\r
+ // matches[13]: RST6 (storage.live.com) ticket\r
+ // matches[14]: ...\r
+\r
+ // so\r
+ // ticket => $matches[1]\r
+ // secret => $matches[3]\r
+ // web_ticket => $matches[5]\r
+ // contact_ticket => $matches[7]\r
+ // oim_ticket => $matches[9]\r
+ // space_ticket => $matches[11]\r
+ // storage_ticket => $matches[13]\r
+\r
+ // yes, we get ticket\r
+ $aTickets = array(\r
+ 'ticket' => html_entity_decode($matches[1]),\r
+ 'secret' => html_entity_decode($matches[3]),\r
+ 'web_ticket' => html_entity_decode($matches[5]),\r
+ 'contact_ticket' => html_entity_decode($matches[7]),\r
+ 'oim_ticket' => html_entity_decode($matches[9]),\r
+ 'space_ticket' => html_entity_decode($matches[11]),\r
+ 'storage_ticket' => html_entity_decode($matches[13])\r
+ ); \r
+ $this->ticket=$aTickets;\r
+ $this->debug_message(var_export($aTickets, true));\r
+ $ABAuthHeaderArray=array(\r
+ 'ABAuthHeader'=>array(\r
+ ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
+ 'ManagedGroupRequest'=>false,\r
+ 'TicketToken'=>htmlspecialchars($this->ticket['contact_ticket']),\r
+ )\r
+ );\r
+ $this->ABAuthHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook","ABAuthHeader", $this->Array2SoapVar($ABAuthHeaderArray));\r
+ file_put_contents('/tmp/STTicket.txt',htmlspecialchars($this->ticket['storage_ticket']));\r
+ //$this->debug_message("StorageTicket:\n",htmlspecialchars($this->ticket['storage_ticket']));\r
+ return $aTickets;\r
+ }\r
+ private function UpdateContacts()\r
+ {\r
+ $ABApplicationHeaderArray=array(\r
+ 'ABApplicationHeader'=>array(\r
+ ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
+ 'ApplicationId'=>'CFE80F9D-180F-4399-82AB-413F33A1FA11',\r
+ 'IsMigration'=>false,\r
+ 'PartnerScenario'=>'ContactSave'\r
+ )\r
+ );\r
+ $ABApplicationHeader=new SoapHeader("http://www.msn.com/webservices/AddressBook",'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));\r
+ $ABFindAllArray=array(\r
+ 'ABFindAll'=>array(\r
+ ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
+ 'abId'=>'00000000-0000-0000-0000-000000000000',\r
+ 'abView'=>'Full',\r
+ 'lastChange'=>'0001-01-01T00:00:00.0000000-08:00',\r
+ )\r
+ );\r
+ $ABFindAll=new SoapParam($this->Array2SoapVar($ABFindAllArray),'ABFindAll');\r
+ $this->ABService->__setSoapHeaders(array($ABApplicationHeader,$this->ABAuthHeader));\r
+ $this->Contacts=array();\r
+ try\r
+ {\r
+ $this->debug_message("*** Update Contacts...");\r
+ $Result=$this->ABService->ABFindAll($ABFindAll);\r
+ $this->debug_message("*** Result:\n".print_r($Result,true)."\n".$this->ABService->__getLastResponse());\r
+ foreach($Result->ABFindAllResult->contacts->Contact as $Contact)\r
+ $this->Contacts[$Contact->contactInfo->passportName]=$Contact;\r
+ }\r
+ catch(Exception $e)\r
+ {\r
+ $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
+ }\r
+ }\r
+ protected function addContact($email, $network, $display = '', $sendADL = false)\r
+ {\r
+ if ($network != 1) return true;\r
+ if(isset($this->Contacts[$email])) return true;\r
+\r
+ $ABContactAddArray=array(\r
+ 'ABContactAdd'=>array(\r
+ ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
+ 'abId'=>'00000000-0000-0000-0000-000000000000',\r
+ 'contacts'=>array(\r
+ 'Contact'=>array(\r
+ ':'=>array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),\r
+ 'contactInfo'=>array(\r
+ 'contactType'=>'LivePending',\r
+ 'passportName'=>$email,\r
+ 'isMessengerUser'=>true,\r
+ 'MessengerMemberInfo'=>array(\r
+ 'DisplayName'=>$email\r
+ )\r
+ )\r
+ )\r
+ ),\r
+ 'options'=>array(\r
+ 'EnableAllowListManagement'=>true\r
+ )\r
+ )\r
+ );\r
+ $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');\r
+ try\r
+ {\r
+ $this->debug_message("*** Add Contacts $email...");\r
+ $this->ABService->ABContactAdd($ABContactAdd);\r
+ }\r
+ catch(Exception $e)\r
+ {\r
+ $this->debug_message("*** Add Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());\r
+ }\r
+ if ($sendADL && !feof($this->NSfp)) {\r
+ @list($u_name, $u_domain) = @explode('@', $email);\r
+ foreach (array('1', '2') as $l) {\r
+ $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';\r
+ $len = strlen($str);\r
+ // NS: >>> ADL {id} {size}\r
+ $this->ns_writeln("ADL $this->id $len");\r
+ $this->ns_writedata($str);\r
+ }\r
+ }\r
+ $this->UpdateContacts();\r
+ return true;\r
+\r
+\r
+ $ABContactAdd=new SoapParam($this->Array2SoapVar($ABContactAddArray),'ABContactAdd');\r
+\r
+ // add contact for WLM\r
+ $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
+ $displayName = htmlspecialchars($display);\r
+ $user = $email;\r
+\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>ContactSave</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <ABContactAdd xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <abId>00000000-0000-0000-0000-000000000000</abId>\r
+ <contacts>\r
+ <Contact xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <contactInfo>\r
+ <contactType>LivePending</contactType>\r
+ <passportName>'.$user.'</passportName>\r
+ <isMessengerUser>true</isMessengerUser>\r
+ <MessengerMemberInfo>\r
+ <DisplayName>'.$displayName.'</DisplayName>\r
+ </MessengerMemberInfo>\r
+ </contactInfo>\r
+ </Contact>\r
+ </contacts>\r
+ <options>\r
+ <EnableAllowListManagement>true</EnableAllowListManagement>\r
+ </options>\r
+ </ABContactAdd>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->addcontact_soap,\r
+ 'Content-Type: text/xml; charset=utf-8',\r
+ 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
+ );\r
+\r
+ $this->debug_message("*** URL: $this->addcontact_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->addcontact_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** can't add contact (network: $network) $email");\r
+ return false;\r
+ }\r
+ $faultcode = trim($matches[1]);\r
+ $faultstring = trim($matches[2]);\r
+ $this->log_message("*** can't add contact (network: $network) $email, error code: $faultcode, $faultstring");\r
+ return false;\r
+ }\r
+ $this->log_message("*** add contact (network: $network) $email");\r
+ if ($sendADL && !feof($this->NSfp)) {\r
+ @list($u_name, $u_domain) = @explode('@', $email);\r
+ foreach (array('1', '2') as $l) {\r
+ $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';\r
+ $len = strlen($str);\r
+ // NS: >>> ADL {id} {size}\r
+ $this->ns_writeln("ADL $this->id $len");\r
+ $this->ns_writedata($str);\r
+ }\r
+ }\r
+ $this->UpdateContacts();\r
+ return true;\r
+ }\r
+\r
+ function delMemberFromList($memberID, $email, $network, $list) {\r
+ if ($network != 1 && $network != 32) return true;\r
+ if ($memberID === false) return true;\r
+ $user = $email;\r
+ $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
+ if ($network == 1)\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceHandle>\r
+ <Id>0</Id>\r
+ <Type>Messenger</Type>\r
+ <ForeignId></ForeignId>\r
+ </serviceHandle>\r
+ <memberships>\r
+ <Membership>\r
+ <MemberRole>'.$list.'</MemberRole>\r
+ <Members>\r
+ <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+ <Type>Passport</Type>\r
+ <MembershipId>'.$memberID.'</MembershipId>\r
+ <State>Accepted</State>\r
+ </Member>\r
+ </Members>\r
+ </Membership>\r
+ </memberships>\r
+ </DeleteMember>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+ else\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceHandle>\r
+ <Id>0</Id>\r
+ <Type>Messenger</Type>\r
+ <ForeignId></ForeignId>\r
+ </serviceHandle>\r
+ <memberships>\r
+ <Membership>\r
+ <MemberRole>'.$list.'</MemberRole>\r
+ <Members>\r
+ <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+ <Type>Email</Type>\r
+ <MembershipId>'.$memberID.'</MembershipId>\r
+ <State>Accepted</State>\r
+ </Member>\r
+ </Members>\r
+ </Membership>\r
+ </memberships>\r
+ </DeleteMember>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->delmember_soap,\r
+ 'Content-Type: text/xml; charset=utf-8',\r
+ 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
+ );\r
+\r
+ $this->debug_message("*** URL: $this->delmember_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->delmember_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");\r
+ return false;\r
+ }\r
+ $faultcode = trim($matches[1]);\r
+ $faultstring = trim($matches[2]);\r
+ if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {\r
+ $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");\r
+ return false;\r
+ }\r
+ $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");\r
+ return true;\r
+ }\r
+ $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");\r
+ return true;\r
+ }\r
+\r
+ function addMemberToList($email, $network, $list) {\r
+ if ($network != 1 && $network != 32) return true;\r
+ $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
+ $user = $email;\r
+\r
+ if ($network == 1)\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceHandle>\r
+ <Id>0</Id>\r
+ <Type>Messenger</Type>\r
+ <ForeignId></ForeignId>\r
+ </serviceHandle>\r
+ <memberships>\r
+ <Membership>\r
+ <MemberRole>'.$list.'</MemberRole>\r
+ <Members>\r
+ <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+ <Type>Passport</Type>\r
+ <State>Accepted</State>\r
+ <PassportName>'.$user.'</PassportName>\r
+ </Member>\r
+ </Members>\r
+ </Membership>\r
+ </memberships>\r
+ </AddMember>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+ else\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <AddMember xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceHandle>\r
+ <Id>0</Id>\r
+ <Type>Messenger</Type>\r
+ <ForeignId></ForeignId>\r
+ </serviceHandle>\r
+ <memberships>\r
+ <Membership>\r
+ <MemberRole>'.$list.'</MemberRole>\r
+ <Members>\r
+ <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r
+ <Type>Email</Type>\r
+ <State>Accepted</State>\r
+ <Email>'.$user.'</Email>\r
+ <Annotations>\r
+ <Annotation>\r
+ <Name>MSN.IM.BuddyType</Name>\r
+ <Value>32:YAHOO</Value>\r
+ </Annotation>\r
+ </Annotations>\r
+ </Member>\r
+ </Members>\r
+ </Membership>\r
+ </memberships>\r
+ </AddMember>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->addmember_soap,\r
+ 'Content-Type: text/xml; charset=utf-8',\r
+ 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
+ );\r
+\r
+ $this->debug_message("*** URL: $this->addmember_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->addmember_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** can't add member (network: $network) $email to $list");\r
+ return false;\r
+ }\r
+ $faultcode = trim($matches[1]);\r
+ $faultstring = trim($matches[2]);\r
+ if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {\r
+ $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");\r
+ return false;\r
+ }\r
+ $this->log_message("*** add member (network: $network) $email to $list, already exist!");\r
+ return true;\r
+ }\r
+ $this->log_message("*** add member (network: $network) $email to $list");\r
+ return true;\r
+ }\r
+\r
+ function getMembershipList($returnData=false) {\r
+ $ticket = htmlspecialchars($this->ticket['contact_ticket']);\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">\r
+<soap:Header>\r
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>\r
+ <IsMigration>false</IsMigration>\r
+ <PartnerScenario>Initial</PartnerScenario>\r
+ </ABApplicationHeader>\r
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <ManagedGroupRequest>false</ManagedGroupRequest>\r
+ <TicketToken>'.$ticket.'</TicketToken>\r
+ </ABAuthHeader>\r
+</soap:Header>\r
+<soap:Body>\r
+ <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">\r
+ <serviceFilter>\r
+ <Types>\r
+ <ServiceType>Messenger</ServiceType>\r
+ <ServiceType>Invitation</ServiceType>\r
+ <ServiceType>SocialNetwork</ServiceType>\r
+ <ServiceType>Space</ServiceType>\r
+ <ServiceType>Profile</ServiceType>\r
+ </Types>\r
+ </serviceFilter>\r
+ </FindMembership>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->membership_soap,\r
+ 'Content-Type: text/xml; charset=utf-8',\r
+ 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'\r
+ );\r
+ $this->debug_message("*** URL: $this->membership_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->membership_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+ if(($http_code != 200)||(!$returnData)) return array();\r
+ $p = $data;\r
+ $aMemberships = array();\r
+ while (1) {\r
+ //$this->debug_message("search p = $p");\r
+ $start = strpos($p, '<Membership>');\r
+ $end = strpos($p, '</Membership>');\r
+ if ($start === false || $end === false || $start > $end) break;\r
+ //$this->debug_message("start = $start, end = $end");\r
+ $end += 13;\r
+ $sMembership = substr($p, $start, $end - $start);\r
+ $aMemberships[] = $sMembership;\r
+ //$this->debug_message("add sMembership = $sMembership");\r
+ $p = substr($p, $end);\r
+ }\r
+ //$this->debug_message("aMemberships = ".var_export($aMemberships, true));\r
+\r
+ $aContactList = array();\r
+ foreach ($aMemberships as $sMembership) {\r
+ //$this->debug_message("sMembership = $sMembership");\r
+ if (isset($matches)) unset($matches);\r
+ preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);\r
+ if (count($matches) == 0) continue;\r
+ $sMemberRole = $matches[1];\r
+ //$this->debug_message("MemberRole = $sMemberRole");\r
+ if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;\r
+ $p = $sMembership;\r
+ if (isset($aMembers)) unset($aMembers);\r
+ $aMembers = array();\r
+ while (1) {\r
+ //$this->debug_message("search p = $p");\r
+ $start = strpos($p, '<Member xsi:type="');\r
+ $end = strpos($p, '</Member>');\r
+ if ($start === false || $end === false || $start > $end) break;\r
+ //$this->debug_message("start = $start, end = $end");\r
+ $end += 9;\r
+ $sMember = substr($p, $start, $end - $start);\r
+ $aMembers[] = $sMember;\r
+ //$this->debug_message("add sMember = $sMember");\r
+ $p = substr($p, $end);\r
+ }\r
+ //$this->debug_message("aMembers = ".var_export($aMembers, true));\r
+ foreach ($aMembers as $sMember) {\r
+ //$this->debug_message("sMember = $sMember");\r
+ if (isset($matches)) unset($matches);\r
+ preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);\r
+ if (count($matches) == 0) continue;\r
+ $sMemberType = $matches[1];\r
+ //$this->debug_message("MemberType = $sMemberType");\r
+ $network = -1;\r
+ preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);\r
+ if (count($matches) == 0) continue;\r
+ $id = $matches[1];\r
+ if ($sMemberType == 'PassportMember') {\r
+ if (strpos($sMember, '<Type>Passport</Type>') === false) continue;\r
+ $network = 1;\r
+ preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);\r
+ }\r
+ else if ($sMemberType == 'EmailMember') {\r
+ if (strpos($sMember, '<Type>Email</Type>') === false) continue;\r
+ // Value is 32: or 32:YAHOO\r
+ preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);\r
+ if (count($matches) == 0) continue;\r
+ if ($matches[1] != 32) continue;\r
+ $network = 32;\r
+ preg_match('#<Email>(.*)</Email>#', $sMember, $matches);\r
+ }\r
+ if ($network == -1) continue;\r
+ if (count($matches) > 0) {\r
+ $email = $matches[1];\r
+ @list($u_name, $u_domain) = @explode('@', $email);\r
+ if ($u_domain == NULL) continue;\r
+ $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;\r
+ $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");\r
+ }\r
+ }\r
+ }\r
+ return $aContactList;\r
+ }\r
+\r
+ private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {\r
+ $this->id = 1;\r
+ if ($redirect_server === '') {\r
+ $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);\r
+ if (!$this->NSfp) {\r
+ $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";\r
+ return false;\r
+ }\r
+ }\r
+ else {\r
+ $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, 5);\r
+ if (!$this->NSfp) {\r
+ $this->error = "Can't connect to $redirect_server:$redirect_port, error => $errno, $errstr";\r
+ return false;\r
+ }\r
+ }\r
+\r
+ stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
+ $this->authed = false;\r
+ // MSNP9\r
+ // NS: >> VER {id} MSNP9 CVR0\r
+ // MSNP15\r
+ // NS: >>> VER {id} MSNP15 CVR0\r
+ $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
+\r
+ $start_tm = time();\r
+ while (!feof($this->NSfp))\r
+ {\r
+ $data = $this->ns_readln();\r
+ // no data?\r
+ if ($data === false) {\r
+ if ($this->timeout > 0) {\r
+ $now_tm = time();\r
+ $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;\r
+ if ($used_time > $this->timeout) {\r
+ // logout now\r
+ // NS: >>> OUT\r
+ $this->ns_writeln("OUT");\r
+ fclose($this->NSfp);\r
+ $this->error = 'Timeout, maybe protocol changed!';\r
+ $this->debug_message("*** $this->error");\r
+ return false;\r
+ }\r
+ }\r
+ continue;\r
+ }\r
+ $code = substr($data, 0, 3);\r
+ $start_tm = time();\r
+\r
+ switch ($code) {\r
+ case 'VER':\r
+ // MSNP9\r
+ // NS: <<< VER {id} MSNP9 CVR0\r
+ // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}\r
+ // MSNP15\r
+ // NS: <<< VER {id} MSNP15 CVR0\r
+ // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}\r
+ $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");\r
+ break;\r
+\r
+ case 'CVR':\r
+ // MSNP9\r
+ // NS: <<< CVR {id} {ver_list} {download_serve} ....\r
+ // NS: >>> USR {id} TWN I {user}\r
+ // MSNP15\r
+ // NS: <<< CVR {id} {ver_list} {download_serve} ....\r
+ // NS: >>> USR {id} SSO I {user}\r
+ $this->ns_writeln("USR $this->id $this->login_method I $user");\r
+ break;\r
+\r
+ case 'USR':\r
+ // already login for passport site, finish the login process now.\r
+ // NS: <<< USR {id} OK {user} {verify} 0\r
+ if ($this->authed) return true;\r
+ // max. 16 digits for password\r
+ if (strlen($password) > 16)\r
+ $password = substr($password, 0, 16);\r
+\r
+ $this->user = $user;\r
+ $this->password = $password;\r
+ // NS: <<< USR {id} SSO S {policy} {nonce}\r
+ @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);\r
+\r
+ $this->passport_policy = $policy;\r
+ $aTickets = $this->get_passport_ticket();\r
+ if (!$aTickets || !is_array($aTickets)) {\r
+ // logout now\r
+ // NS: >>> OUT\r
+ $this->ns_writeln("OUT");\r
+ fclose($this->NSfp);\r
+ $this->error = 'Passport authenticated fail!';\r
+ $this->debug_message("*** $this->error");\r
+ return false;\r
+ }\r
+\r
+ $ticket = $aTickets['ticket'];\r
+ $secret = $aTickets['secret'];\r
+ $this->ticket = $aTickets;\r
+ $login_code = $this->generateLoginBLOB($secret, $nonce);\r
+\r
+ // NS: >>> USR {id} SSO S {ticket} {login_code}\r
+ $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");\r
+ $this->authed = true;\r
+ break;\r
+\r
+ case 'XFR':\r
+ // main login server will redirect to anther NS after USR command\r
+ // MSNP9\r
+ // NS: <<< XFR {id} NS {server} 0 {server}\r
+ // MSNP15\r
+ // NS: <<< XFR {id} NS {server} U D\r
+ @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);\r
+ if($Type!='NS') break;\r
+ @list($ip, $port) = @explode(':', $server);\r
+ // this connection will close after XFR\r
+ fclose($this->NSfp);\r
+\r
+ $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
+ if (!$this->NSfp) {\r
+ $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";\r
+ $this->debug_message("*** $this->error");\r
+ return false;\r
+ }\r
+\r
+ stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
+ // MSNP9\r
+ // NS: >> VER {id} MSNP9 CVR0\r
+ // MSNP15\r
+ // NS: >>> VER {id} MSNP15 CVR0\r
+ $this->ns_writeln("VER $this->id $this->protocol CVR0");\r
+ break;\r
+\r
+ case 'GCF':\r
+ // return some policy data after 'USR {id} SSO I {user}' command\r
+ // NS: <<< GCF 0 {size}\r
+ @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);\r
+ // we don't need the data, just read it and drop\r
+ if (is_numeric($size) && $size > 0)\r
+ $this->ns_readdata($size);\r
+ break;\r
+\r
+ default:\r
+ // we'll quit if got any error\r
+ if (is_numeric($code)) {\r
+ // logout now\r
+ // NS: >>> OUT\r
+ $this->ns_writeln("OUT");\r
+ fclose($this->NSfp);\r
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
+ $this->debug_message("*** $this->error");\r
+ return false;\r
+ }\r
+ // unknown response from server, just ignore it\r
+ break;\r
+ }\r
+ }\r
+ // never goto here\r
+ }\r
+\r
+ function derive_key($key, $magic) {\r
+ $hash1 = mhash(MHASH_SHA1, $magic, $key);\r
+ $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);\r
+ $hash3 = mhash(MHASH_SHA1, $hash1, $key);\r
+ $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);\r
+ return $hash2.substr($hash4, 0, 4);\r
+ }\r
+\r
+ function generateLoginBLOB($key, $challenge) {\r
+ $key1 = base64_decode($key);\r
+ $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');\r
+ $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');\r
+\r
+ // get hash of challenge using key2\r
+ $hash = mhash(MHASH_SHA1, $challenge, $key2);\r
+\r
+ // get 8 bytes random data\r
+ $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);\r
+\r
+ $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);\r
+\r
+ $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);\r
+ $blob .= $iv;\r
+ $blob .= $hash;\r
+ $blob .= $cipher;\r
+\r
+ return base64_encode($blob);\r
+ }\r
+\r
+ function getOIM_maildata() {\r
+ preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);\r
+ if (count($matches) == 0) {\r
+ $this->debug_message('*** no web ticket?');\r
+ return false;\r
+ }\r
+ $t = htmlspecialchars($matches[1]);\r
+ $p = htmlspecialchars($matches[2]);\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
+<soap:Header>\r
+ <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+ <t>'.$t.'</t>\r
+ <p>'.$p.'</p>\r
+ </PassportCookie>\r
+</soap:Header>\r
+<soap:Body>\r
+ <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->oim_maildata_soap,\r
+ 'Content-Type: text/xml; charset=utf-8',\r
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+ );\r
+\r
+ $this->debug_message("*** URL: $this->oim_maildata_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ $this->debug_message("*** Can't get OIM maildata! http code: $http_code");\r
+ return false;\r
+ }\r
+\r
+ // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>\r
+ preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->debug_message("*** Can't get OIM maildata");\r
+ return '';\r
+ }\r
+ return $matches[2];\r
+ }\r
+\r
+ function getOIM_message($msgid) {\r
+ preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);\r
+ if (count($matches) == 0) {\r
+ $this->debug_message('*** no web ticket?');\r
+ return false;\r
+ }\r
+ $t = htmlspecialchars($matches[1]);\r
+ $p = htmlspecialchars($matches[2]);\r
+\r
+ // read OIM\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
+<soap:Header>\r
+ <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+ <t>'.$t.'</t>\r
+ <p>'.$p.'</p>\r
+ </PassportCookie>\r
+</soap:Header>\r
+<soap:Body>\r
+ <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+ <messageId>'.$msgid.'</messageId>\r
+ <alsoMarkAsRead>false</alsoMarkAsRead>\r
+ </GetMessage>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->oim_read_soap,\r
+ 'Content-Type: text/xml; charset=utf-8',\r
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+ );\r
+\r
+ $this->debug_message("*** URL: $this->oim_read_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200) {\r
+ $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");\r
+ return false;\r
+ }\r
+\r
+ // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?\r
+ // multi-lines?\r
+ $start = strpos($data, '<GetMessageResult>');\r
+ $end = strpos($data, '</GetMessageResult>');\r
+ if ($start === false || $end === false || $start > $end) {\r
+ $this->debug_message("*** Can't get OIM: $msgid");\r
+ return false;\r
+ }\r
+ $lines = substr($data, $start + 18, $end - $start);\r
+ $aLines = @explode("\n", $lines);\r
+ $header = true;\r
+ $ignore = false;\r
+ $sOIM = '';\r
+ foreach ($aLines as $line) {\r
+ $line = rtrim($line);\r
+ if ($header) {\r
+ if ($line === '') {\r
+ $header = false;\r
+ continue;\r
+ }\r
+ continue;\r
+ }\r
+ // stop at empty lines\r
+ if ($line === '') break;\r
+ $sOIM .= $line;\r
+ }\r
+ $sMsg = base64_decode($sOIM);\r
+ $this->debug_message("*** we get OIM ($msgid): $sMsg");\r
+\r
+ // delete OIM\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
+<soap:Header>\r
+ <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+ <t>'.$t.'</t>\r
+ <p>'.$p.'</p>\r
+ </PassportCookie>\r
+</soap:Header>\r
+<soap:Body>\r
+ <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">\r
+ <messageIds>\r
+ <messageId>'.$msgid.'</messageId>\r
+ </messageIds>\r
+ </DeleteMessages>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->oim_del_soap,\r
+ 'Content-Type: text/xml; charset=utf-8',\r
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+ );\r
+\r
+ $this->debug_message("*** URL: $this->oim_del_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code != 200)\r
+ $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");\r
+ else\r
+ $this->debug_message("*** OIM ($msgid) deleted");\r
+ return $sMsg;\r
+ }\r
+ private function NSLogout() {\r
+ if (is_resource($this->NSfp) && !feof($this->NSfp)) {\r
+ // logout now\r
+ // NS: >>> OUT\r
+ $this->ns_writeln("OUT");\r
+ fclose($this->NSfp);\r
+ $this->NSfp = false;\r
+ $this->log_message("*** logout now!");\r
+ }\r
+\r
+ }\r
+ private function NSRetryWait($Wait) {\r
+ $this->log_message("*** wait for $Wait seconds");\r
+ for($i=0;$i<$Wait;$i++) {\r
+ sleep(1);\r
+ if($this->kill_me) return false;\r
+ }\r
+ return true;\r
+ }\r
+ public function ProcessSendMessageFileQueue() {\r
+ $aFiles = glob(MSN_CLASS_SPOOL_DIR.DIRECTORY_SEPARATOR.'*.msn');\r
+ if (!is_array($aFiles)) return true;\r
+ clearstatcache();\r
+ foreach ($aFiles as $filename) {\r
+ $fp = fopen($filename, 'rt');\r
+ if (!$fp) continue;\r
+ $aTo = array();\r
+ $sMessage = '';\r
+ $buf = trim(fgets($fp));\r
+ if (substr($buf, 0, 3) == 'TO:') {\r
+ $aTo = @explode(',', str_replace(array("\r","\n","\t",' '),'',substr($buf, 3)));\r
+ while (!feof($fp)) $sMessage.=rtrim(fgets($fp))."\n";\r
+ }\r
+ fclose($fp);\r
+ if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '')\r
+ $this->log_message("!!! message format error? delete $filename");\r
+ else\r
+ {\r
+ foreach($aTo as $To)\r
+ {\r
+ @list($user, $domain, $network) = @explode('@', $To);\r
+ $MessageList[$network]["$user@$domain"]=$sMessage;\r
+ }\r
+ }\r
+ if($this->backup_file)\r
+ {\r
+ $backup_dir = MSN_CLASS_SPOOL_DIR.'/backup';\r
+ if (!file_exists($backup_dir)) @mkdir($backup_dir);\r
+ $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);\r
+ if (@rename($filename, $backup_name))\r
+ $this->log_message("*** move file to $backup_name");\r
+ }\r
+ else @unlink($filename);\r
+ }\r
+ foreach ($MessageList as $network => $Messages)\r
+ {\r
+ switch(trim($network))\r
+ {\r
+ case '':\r
+ case 1: //MSN\r
+ // okay, try to ask a switchboard (SB) for sending message\r
+ // NS: >>> XFR {id} SB\r
+ // $this->ns_writeln("XFR $this->id SB");\r
+ foreach($Messages as $User => $Message)\r
+ $this->MessageQueue[$User][]=$Message;\r
+ break;\r
+ case 'Offline': //MSN\r
+ //Send OIM\r
+ //FIXME: 修正Send OIM\r
+ foreach($Messages as $To => $Message)\r
+ {\r
+ $lockkey='';\r
+ for ($i = 0; $i < $this->oim_try; $i++)\r
+ {\r
+ if(($oim_result = $this->sendOIM($To, $Message, $lockkey))===true) break;\r
+ if (is_array($oim_result) && $oim_result['challenge'] !== false) {\r
+ // need challenge lockkey\r
+ $this->log_message("*** we need a new challenge code for ".$oim_result['challenge']);\r
+ $lockkey = $this->getChallenge($oim_result['challenge']);\r
+ continue;\r
+ }\r
+ if ($oim_result === false || $oim_result['auth_policy'] !== false)\r
+ {\r
+ if ($re_login)\r
+ {\r
+ $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");\r
+ break;\r
+ }\r
+ $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");\r
+ // maybe we need to re-login again\r
+ if(!$this->get_passport_ticket())\r
+ {\r
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+ break;\r
+ }\r
+ $this->log_message("**** get new ticket, try it again");\r
+ continue;\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ default: //Other\r
+ foreach($Messages as $To => $Message) {\r
+ $Message=$this->getMessage($Message, $network);\r
+ $len = strlen($Message);\r
+ $this->ns_writeln("UUM $this->id $To $network 1 $len");\r
+ $this->ns_writedata($Message);\r
+ $this->log_message("*** sent to $To (network: $network):\n$Message");\r
+ }\r
+ }\r
+ }\r
+ if(isset($this->MessageQueue[$User])&&(!isset($this->MessageQueue[$User]['XFRSent'])))\r
+ {\r
+ $this->MessageQueue[$User]['XFRSent']=false;\r
+ $this->MessageQueue[$User]['ReqTime']=false;\r
+ }\r
+ return true;\r
+ }\r
+ public function SignalFunction($signal)\r
+ {\r
+ switch($signal)\r
+ {\r
+ case SIGTRAP:\r
+ case SIGTERM:\r
+ case SIGHUP:\r
+ $this->End();\r
+ return;\r
+ case SIGCHLD:\r
+ $ChildPid=pcntl_wait($status,WUNTRACED);\r
+ if($ChildPid>0)\r
+ {\r
+ $this->log_message("*** Child Process End for ".$this->ChildProcess[$ChildPid]);\r
+ unset($this->ChildProcess[$ChildPid]);\r
+ }\r
+ return;\r
+ }\r
+ }\r
+\r
+ public function Run()\r
+ {\r
+ $this->log_message("*** startup ***");\r
+ if(!pcntl_signal(SIGCHLD,array($this,'SignalFunction'))) die("Signal SIGCHLD Error\n");\r
+ if(!pcntl_signal(SIGTERM,array($this,'SignalFunction'))) die("Signal SIGTERM Error\n");\r
+ if(!pcntl_signal(SIGTRAP,array($this,'SignalFunction'))) die("Signal SIGTRAP Error\n");\r
+ $process_file = false;\r
+ $sent = false;\r
+ $aADL = array();\r
+ $aContactList = array();\r
+ while (true)\r
+ {\r
+ if($this->kill_me)\r
+ {\r
+ $this->log_message("*** Okay, kill me now!");\r
+ return $this->NSLogout();\r
+ }\r
+ if (!is_resource($this->NSfp) || feof($this->NSfp))\r
+ {\r
+ $this->log_message("*** try to connect to MSN network");\r
+ if (!$this->connect($this->user, $this->password))\r
+ {\r
+ $this->log_message("!!! Can't connect to server: $this->error");\r
+ if(!$this->NSRetryWait($this->retry_wait)) continue;\r
+ }\r
+ $this->UpdateContacts();\r
+ $this->LastPing=time();\r
+ $this->log_message("*** connected, wait for command");\r
+ $start_tm = time();\r
+ $ping_tm = time();\r
+ stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
+ $aContactList = $this->getMembershipList(true);\r
+ if ($this->update_pending) {\r
+ if (is_array($aContactList)) {\r
+ $pending = 'Pending';\r
+ foreach ($aContactList as $u_domain => $aUserList) {\r
+ foreach ($aUserList as $u_name => $aNetworks) {\r
+ foreach ($aNetworks as $network => $aData) {\r
+ if (isset($aData[$pending])) {\r
+ // pending list\r
+ $cnt = 0;\r
+ foreach (array('Allow', 'Reverse') as $list) {\r
+ if (isset($aData[$list]))\r
+ $cnt++;\r
+ else {\r
+ if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
+ $aContactList[$u_domain][$u_name][$network][$list] = false;\r
+ $cnt++;\r
+ }\r
+ }\r
+ }\r
+ if ($cnt >= 2) {\r
+ $id = $aData[$pending];\r
+ // we can delete it from pending now\r
+ if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
+ unset($aContactList[$u_domain][$u_name][$network][$pending]);\r
+ }\r
+ }\r
+ else {\r
+ // sync list\r
+ foreach (array('Allow', 'Reverse') as $list) {\r
+ if (!isset($aData[$list])) {\r
+ if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
+ $aContactList[$u_domain][$u_name][$network][$list] = false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ $n = 0;\r
+ $sList = '';\r
+ $len = 0;\r
+ if (is_array($aContactList)) {\r
+ foreach ($aContactList as $u_domain => $aUserList) {\r
+ $str = '<d n="'.$u_domain.'">';\r
+ $len += strlen($str);\r
+ if ($len > 7400) {\r
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+ $n++;\r
+ $sList = '';\r
+ $len = strlen($str);\r
+ }\r
+ $sList .= $str;\r
+ foreach ($aUserList as $u_name => $aNetworks) {\r
+ foreach ($aNetworks as $network => $status) {\r
+ $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
+ $len += strlen($str);\r
+ // max: 7500, but <ml l="1"></d></ml> is 19,\r
+ // so we use 7475\r
+ if ($len > 7475) {\r
+ $sList .= '</d>';\r
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+ $n++;\r
+ $sList = '<d n="'.$u_domain.'">'.$str;\r
+ $len = strlen($sList);\r
+ }\r
+ else\r
+ $sList .= $str;\r
+ }\r
+ }\r
+ $sList .= '</d>';\r
+ }\r
+ }\r
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+ // NS: >>> BLP {id} BL\r
+ $this->ns_writeln("BLP $this->id BL");\r
+ foreach ($aADL as $str) {\r
+ $len = strlen($str);\r
+ // NS: >>> ADL {id} {size}\r
+ $this->ns_writeln("ADL $this->id $len");\r
+ $this->ns_writedata($str);\r
+ }\r
+ // NS: >>> PRP {id} MFN name\r
+ if ($this->alias == '') $this->alias = $user;\r
+ $aliasname = rawurlencode($this->alias);\r
+ $this->ns_writeln("PRP $this->id MFN $aliasname");\r
+ //設定個人大頭貼\r
+ //$MsnObj=$this->PhotoStckObj();\r
+ // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid"); \r
+ if($this->PhotoStickerFile!==false)\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+ // NS: >>> UUX {id} length\r
+ $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
+ $len = strlen($str);\r
+ $this->ns_writeln("UUX $this->id $len");\r
+ $this->ns_writedata($str); \r
+ }\r
+ $data = $this->ns_readln();\r
+ if($data===false)\r
+ {\r
+ //If No NS Message Process SendMessageFileQueue\r
+ if (time()-$this->LastPing > $this->ping_wait)\r
+ {\r
+ // NS: >>> PNG\r
+ $this->ns_writeln("PNG");\r
+ $this->LastPing = time();\r
+ }\r
+ if(count($this->ChildProcess)<$this->MAXChildProcess)\r
+ {\r
+ $Index=0;\r
+ foreach($this->MessageQueue as $User => $Message)\r
+ {\r
+ if(!trim($User)) continue;\r
+ if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;\r
+ if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))\r
+ {\r
+ $this->MessageQueue[$User]['XFRSent']=true;\r
+ $this->MessageQueue[$User]['ReqTime']=time();\r
+ $this->log_message("*** Request SB for $User");\r
+ $this->ns_writeln("XFR $this->id SB");\r
+ $Index++;\r
+ }\r
+ }\r
+ }\r
+ if($this->ProcessSendMessageFileQueue()) continue;\r
+ break;\r
+ }\r
+ switch (substr($data,0,3))\r
+ {\r
+ case 'SBS':\r
+ // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
+ // NS: <<< SBS 0 null\r
+ break;\r
+\r
+ case 'RFS':\r
+ // FIXME:\r
+ // NS: <<< RFS ???\r
+ // refresh ADL, so we re-send it again\r
+ if (is_array($aADL)) {\r
+ foreach ($aADL as $str) {\r
+ $len = strlen($str);\r
+ // NS: >>> ADL {id} {size}\r
+ $this->ns_writeln("ADL $this->id $len");\r
+ $this->ns_writedata($str);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case 'LST':\r
+ // NS: <<< LST {email} {alias} 11 0\r
+ @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\r
+ @list($u_name, $u_domain) = @explode('@', $email);\r
+ if (!isset($aContactList[$u_domain][$u_name][1])) {\r
+ $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
+ $this->log_message("*** add to our contact list: $u_name@$u_domain");\r
+ }\r
+ break;\r
+\r
+ case 'ADL':\r
+ // randomly, we get ADL command, someome add us to their contact list for MSNP15\r
+ // NS: <<< ADL 0 {size}\r
+ @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0)\r
+ {\r
+ $data = $this->ns_readdata($size);\r
+ preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
+ if (is_array($matches) && count($matches) > 0)\r
+ {\r
+ $u_domain = $matches[1];\r
+ $u_name = $matches[2];\r
+ $network = $matches[4];\r
+ if (isset($aContactList[$u_domain][$u_name][$network]))\r
+ $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
+ else\r
+ {\r
+ $re_login = false;\r
+ $cnt = 0;\r
+ foreach (array('Allow', 'Reverse') as $list)\r
+ {\r
+ if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
+ {\r
+ if ($re_login) {\r
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+ continue;\r
+ }\r
+ $aTickets = $this->get_passport_ticket();\r
+ if (!$aTickets || !is_array($aTickets)) {\r
+ // failed to login? ignore it\r
+ $this->log_message("*** can't re-login, something wrong here");\r
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+ continue;\r
+ }\r
+ $re_login = true;\r
+ $this->ticket = $aTickets;\r
+ $this->log_message("**** get new ticket, try it again");\r
+ if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
+ {\r
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+ continue;\r
+ }\r
+ }\r
+ $aContactList[$u_domain][$u_name][$network][$list] = false;\r
+ $cnt++;\r
+ }\r
+ $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
+ }\r
+ $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
+ $len = strlen($str);\r
+ }\r
+ else\r
+ $this->log_message("*** someone add us to their list: $data");\r
+ $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
+ }\r
+ break;\r
+\r
+ case 'RML':\r
+ // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
+ // NS: <<< RML 0 {size}\r
+ @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0)\r
+ {\r
+ $data = $this->ns_readdata($size);\r
+ preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
+ if (is_array($matches) && count($matches) > 0)\r
+ {\r
+ $u_domain = $matches[1];\r
+ $u_name = $matches[2];\r
+ $network = $matches[4];\r
+ if (isset($aContactList[$u_domain][$u_name][$network]))\r
+ {\r
+ $aData = $aContactList[$u_domain][$u_name][$network];\r
+ foreach ($aData as $list => $id)\r
+ $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
+ unset($aContactList[$u_domain][$u_name][$network]);\r
+ $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
+ }\r
+ else\r
+ $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
+ $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);\r
+ }\r
+ else\r
+ $this->log_message("*** someone remove us from their list: $data");\r
+ }\r
+ break;\r
+\r
+ case 'MSG':\r
+ // randomly, we get MSG notification from server\r
+ // NS: <<< MSG Hotmail Hotmail {size}\r
+ @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0) {\r
+ $data = $this->ns_readdata($size);\r
+ $aLines = @explode("\n", $data);\r
+ $header = true;\r
+ $ignore = false;\r
+ $maildata = '';\r
+ foreach ($aLines as $line) {\r
+ $line = rtrim($line);\r
+ if ($header) {\r
+ if ($line === '') {\r
+ $header = false;\r
+ continue;\r
+ }\r
+ if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
+ if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&\r
+ strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
+ // we just need text/x-msmsgsinitialmdatanotification\r
+ // or text/x-msmsgsoimnotification\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ }\r
+ continue;\r
+ }\r
+ if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
+ $maildata = trim(substr($line, 10));\r
+ break;\r
+ }\r
+ }\r
+ if ($ignore) {\r
+ $this->log_message("*** ingnore MSG for: $line");\r
+ break;\r
+ }\r
+ if ($maildata == '') {\r
+ $this->log_message("*** ingnore MSG not for OIM");\r
+ break;\r
+ }\r
+ $re_login = false;\r
+ if (strcasecmp($maildata, 'too-large') == 0) {\r
+ $this->log_message("*** large mail-data, need to get the data via SOAP");\r
+ $maildata = $this->getOIM_maildata();\r
+ if ($maildata === false) {\r
+ $this->log_message("*** can't get mail-data via SOAP");\r
+ // maybe we need to re-login again\r
+ $aTickets = $this->get_passport_ticket();\r
+ if (!$aTickets || !is_array($aTickets)) {\r
+ // failed to login? ignore it\r
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+ break;\r
+ }\r
+ $re_login = true;\r
+ $this->ticket = $aTickets;\r
+ $this->log_message("**** get new ticket, try it again");\r
+ $maildata = $this->getOIM_maildata();\r
+ if ($maildata === false) {\r
+ $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ // could be a lots of <M>...</M>, so we can't use preg_match here\r
+ $p = $maildata;\r
+ $aOIMs = array();\r
+ while (1) {\r
+ $start = strpos($p, '<M>');\r
+ $end = strpos($p, '</M>');\r
+ if ($start === false || $end === false || $start > $end) break;\r
+ $end += 4;\r
+ $sOIM = substr($p, $start, $end - $start);\r
+ $aOIMs[] = $sOIM;\r
+ $p = substr($p, $end);\r
+ }\r
+ if (count($aOIMs) == 0) {\r
+ $this->log_message("*** ingnore empty OIM");\r
+ break;\r
+ }\r
+ foreach ($aOIMs as $maildata) {\r
+ // T: 11 for MSN, 13 for Yahoo\r
+ // S: 6 for MSN, 7 for Yahoo\r
+ // RT: the datetime received by server\r
+ // RS: already read or not\r
+ // SZ: size of message\r
+ // E: sender\r
+ // I: msgid\r
+ // F: always 00000000-0000-0000-0000-000000000009\r
+ // N: sender alias\r
+ preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** ingnore OIM maildata without <T>type</T>");\r
+ continue;\r
+ }\r
+ $oim_type = $matches[1];\r
+ if ($oim_type = 13)\r
+ $network = 32;\r
+ else\r
+ $network = 1;\r
+ preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** ingnore OIM maildata without <E>sender</E>");\r
+ continue;\r
+ }\r
+ $oim_sender = $matches[1];\r
+ preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");\r
+ continue;\r
+ }\r
+ $oim_msgid = $matches[1];\r
+ preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
+ $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
+ preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
+ $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
+ $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
+ $sMsg = $this->getOIM_message($oim_msgid);\r
+ if ($sMsg === false) {\r
+ $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
+ if ($re_login) {\r
+ $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
+ continue;\r
+ }\r
+ $aTickets = $this->get_passport_ticket();\r
+ if (!$aTickets || !is_array($aTickets)) {\r
+ // failed to login? ignore it\r
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+ continue;\r
+ }\r
+ $re_login = true;\r
+ $this->ticket = $aTickets;\r
+ $this->log_message("**** get new ticket, try it again");\r
+ $sMsg = $this->getOIM_message($oim_msgid);\r
+ if ($sMsg === false) {\r
+ $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
+ continue;\r
+ }\r
+ }\r
+ $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
+\r
+ $this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case 'UBM':\r
+ // randomly, we get UBM, this is the message from other network, like Yahoo!\r
+ // NS: <<< UBM {email} $network $type {size}\r
+ @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0)\r
+ {\r
+ $data = $this->ns_readdata($size);\r
+ $aLines = @explode("\n", $data);\r
+ $header = true;\r
+ $ignore = false;\r
+ $sMsg = '';\r
+ foreach ($aLines as $line) {\r
+ $line = rtrim($line);\r
+ if ($header) {\r
+ if ($line === '') {\r
+ $header = false;\r
+ continue;\r
+ }\r
+ if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ continue;\r
+ }\r
+ $aSubLines = @explode("\r", $line);\r
+ foreach ($aSubLines as $str) {\r
+ if ($sMsg !== '')\r
+ $sMsg .= "\n";\r
+ $sMsg .= $str;\r
+ }\r
+ }\r
+ if($ignore)\r
+ {\r
+ $this->log_message("*** ingnore from $from_email: $line");\r
+ break;\r
+ }\r
+ $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
+ $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
+ }\r
+ break;\r
+\r
+ case 'UBX':\r
+ // randomly, we get UBX notification from server\r
+ // NS: <<< UBX email {network} {size}\r
+ @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);\r
+ // we don't need the notification data, so just ignore it\r
+ if (is_numeric($size) && $size > 0)\r
+ $this->ns_readdata($size);\r
+ break;\r
+\r
+ case 'CHL':\r
+ // randomly, we'll get challenge from server\r
+ // NS: <<< CHL 0 {code}\r
+ @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);\r
+ $fingerprint = $this->getChallenge($chl_code);\r
+ // NS: >>> QRY {id} {product_id} 32\r
+ // NS: >>> fingerprint\r
+ $this->ns_writeln("QRY $this->id $this->prod_id 32");\r
+ $this->ns_writedata($fingerprint);\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid"); \r
+ if($this->PhotoStickerFile!==false)\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+ break;\r
+ case 'CHG':\r
+ // NS: <<< CHG {id} {status} {code}\r
+ // ignore it\r
+ // change our status to online first\r
+ break;\r
+\r
+ case 'XFR':\r
+ // sometimes, NS will redirect to another NS\r
+ // MSNP9\r
+ // NS: <<< XFR {id} NS {server} 0 {server}\r
+ // MSNP15\r
+ // NS: <<< XFR {id} NS {server} U D\r
+ // for normal switchboard XFR\r
+ // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
+ @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);\r
+ @list($ip, $port) = @explode(':', $server);\r
+ if ($server_type != 'SB') {\r
+ // maybe exit?\r
+ // this connection will close after XFR\r
+ $this->NSLogout();\r
+ continue;\r
+ }\r
+ if(count($this->MessageQueue))\r
+ {\r
+ foreach($this->MessageQueue as $User => $Message)\r
+ {\r
+ //$this->ChildProcess[$ChildPid]\r
+ $this->log_message("*** XFR SB $User");\r
+ $pid=pcntl_fork();\r
+ if($pid)\r
+ {\r
+ //Parrent Process\r
+ $this->ChildProcess[$pid]=$User;\r
+ break;\r
+ }\r
+ elseif($pid==-1)\r
+ {\r
+ $this->log_message("*** Fork Error $User");\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ //Child Process\r
+ $this->log_message("*** Child Process Start for $User");\r
+ unset($Message['XFRSent']);\r
+ unset($Message['ReqTime']);\r
+ $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
+ if ($bSBresult === false)\r
+ {\r
+ // error for switchboard\r
+ $this->log_message("!!! error for sending message to ".$User);\r
+ }\r
+ die;\r
+ }\r
+ }\r
+ unset($this->MessageQueue[$User]);\r
+ }\r
+ /*\r
+ $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
+ if ($bSBresult === false) {\r
+ // error for switchboard\r
+ $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
+ $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
+ }*/\r
+ break;\r
+ case 'QNG':\r
+ // NS: <<< QNG {time}\r
+ @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);\r
+ if ($this->ping_wait == 0) $this->ping_wait = 50;\r
+ //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;\r
+ //Mod by Ricky Set Online\r
+ break;\r
+\r
+ case 'RNG':\r
+ if($this->PhotoStickerFile!==false)\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+ else\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
+ // someone is trying to talk to us\r
+ // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
+ $this->log_message("NS: <<< RNG $data");\r
+ @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
+ @list($sb_ip, $sb_port) = @explode(':', $server);\r
+ $this->log_message("*** RING from $email, $sb_ip:$sb_port");\r
+ $this->addContact($email,1,$email, true);\r
+ $pid=pcntl_fork();\r
+ if($pid)\r
+ {\r
+ //Parrent Process\r
+ $this->ChildProcess[$pid]='RNG';\r
+ break;\r
+ }\r
+ elseif($pid==-1)\r
+ {\r
+ $this->log_message("*** Fork Error $User");\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ //Child Process\r
+ $this->log_message("*** Ring Child Process Start for $User");\r
+ $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
+ die;\r
+ }\r
+ break;\r
+ case 'OUT':\r
+ // force logout from NS\r
+ // NS: <<< OUT xxx\r
+ fclose($this->NSfp);\r
+ $this->log_message("*** LOGOUT from NS");\r
+ break;\r
+\r
+ default:\r
+ $code = substr($data,0,3);\r
+ if (is_numeric($code)) {\r
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
+ $this->debug_message("*** NS: $this->error");\r
+\r
+ return $this->NsLogout();\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ return $this->NsLogout();\r
+ }\r
+\r
+ /*public function SendMessage($Message, $To)\r
+ {\r
+ $FileName = MSN_CLASS_SPOOL_DIR.'/'.strftime('%Y%m%d%H%M%S',time()).'_'.posix_getpid().'_sendMessage.msn';\r
+ if(!is_array($To))\r
+ $To=array($To);\r
+ $Receiver='';\r
+ foreach($To as $Email)\r
+ {\r
+ list($name,$host,$network)=explode('@',$Email);\r
+ $network=$network==''?1:$network;\r
+ if($network==1 && $this->SwitchBoardProcess && $this->SwitchBoardSessionUser=="$name@$host" )\r
+ {\r
+ $this->debug_message("*** SendMessage to $Receiver use SB message queue.");\r
+ array_push($this->SwitchBoardMessageQueue,$Message);\r
+ continue;\r
+ }\r
+ $Receiver.="$name@$host@$network,";\r
+ }\r
+ if($Receiver=='') return;\r
+ $Receiver=substr($Receiver,0,-1);\r
+ $this->debug_message("*** SendMessage to $Receiver use File queue.");\r
+ file_put_contents($FileName,"TO: $Receiver\n$Message\n");\r
+ }*/\r
+\r
+ function getChallenge($code)\r
+ {\r
+ // MSNP15\r
+ // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges\r
+ // Step 1: The MD5 Hash\r
+ $md5Hash = md5($code.$this->prod_key);\r
+ $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));\r
+ for ($i = 0; $i < 4; $i++) {\r
+ $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));\r
+ $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;\r
+ }\r
+\r
+ // Step 2: A new string\r
+ $chl_id = $code.$this->prod_id;\r
+ $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));\r
+\r
+ $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));\r
+ for ($i = 0; $i < count($aID); $i++) {\r
+ $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));\r
+ $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);\r
+ }\r
+\r
+ // Step 3: The 64 bit key\r
+ $magic_num = 0x0E79A9C1;\r
+ $str7f = 0x7FFFFFFF;\r
+ $high = 0;\r
+ $low = 0;\r
+ for ($i = 0; $i < count($aID); $i += 2) {\r
+ $temp = $aID[$i];\r
+ $temp = bcmod(bcmul($magic_num, $temp), $str7f);\r
+ $temp = bcadd($temp, $high);\r
+ $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);\r
+ $temp = bcmod($temp, $str7f);\r
+\r
+ $high = $aID[$i+1];\r
+ $high = bcmod(bcadd($high, $temp), $str7f);\r
+ $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);\r
+ $high = bcmod($high, $str7f);\r
+\r
+ $low = bcadd(bcadd($low, $high), $temp);\r
+ }\r
+\r
+ $high = bcmod(bcadd($high, $aMD5[1]), $str7f);\r
+ $low = bcmod(bcadd($low, $aMD5[3]), $str7f);\r
+\r
+ $new_high = bcmul($high & 0xFF, 0x1000000);\r
+ $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));\r
+ $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));\r
+ $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));\r
+ // we need integer here\r
+ $high = 0+$new_high;\r
+\r
+ $new_low = bcmul($low & 0xFF, 0x1000000);\r
+ $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));\r
+ $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));\r
+ $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));\r
+ // we need integer here\r
+ $low = 0+$new_low;\r
+\r
+ // we just use 32 bits integer, don't need the key, just high/low\r
+ // $key = bcadd(bcmul($high, 0x100000000), $low);\r
+\r
+ // Step 4: Using the key\r
+ $md5Hash = md5($code.$this->prod_key);\r
+ $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));\r
+\r
+ $hash = '';\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);\r
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);\r
+\r
+ return $hash;\r
+ }\r
+\r
+ private function getMessage($sMessage, $network = 1)\r
+ {\r
+ $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
+ $msg_header_len = strlen($msg_header);\r
+ if ($network == 1)\r
+ $maxlen = $this->max_msn_message_len - $msg_header_len;\r
+ else\r
+ $maxlen = $this->max_yahoo_message_len - $msg_header_len;\r
+ $sMessage=str_replace("\r", '', $sMessage);\r
+ $msg=substr($sMessage,0,$maxlen);\r
+ return $msg_header.$msg;\r
+ }\r
+ /**\r
+ *\r
+ * @param $Action 連線模式 'Active' => 主動傳送訊息,'Passive' => 接收訊息\r
+ * @param $Param\r
+ * @return boolean\r
+ */\r
+ private function DoSwitchBoard($Action,$Param)\r
+ {\r
+ $SessionEnd=false;\r
+ $Joined=false;\r
+ $id=1;\r
+ $LastActive=time();\r
+ stream_set_timeout($this->SBFp, $this->SBTimeout);\r
+ switch($Action)\r
+ {\r
+ case 'Active':\r
+ $cki_code=$Param['cki'];\r
+ $user=$Param['user'];\r
+ $this->SwitchBoardMessageQueue=$Param['Msg'];\r
+ // SB: >>> USR {id} {user} {cki}\r
+ $this->SB_writeln("USR $id $this->user $cki_code");\r
+ $id++;\r
+ $this->SwitchBoardSessionUser=$user;\r
+ break;\r
+ case 'Passive':\r
+ $ticket=$Param['ticket'];\r
+ $sid=$Param['sid'];\r
+ $user=$Param['user'];\r
+ // SB: >>> ANS {id} {user} {ticket} {session_id}\r
+ $this->SB_writeln("ANS $id $this->user $ticket $sid");\r
+ $id++;\r
+ $this->SwitchBoardSessionUser=$user;\r
+ break;\r
+ default:\r
+ return false;\r
+ }\r
+ while((!feof($this->SBFp))&&(!$SessionEnd))\r
+ {\r
+ $data = $this->SB_readln();\r
+ if($this->kill_me)\r
+ {\r
+ $this->log_message("*** SB Okay, kill me now!");\r
+ break;\r
+ }\r
+ if($data === false)\r
+ {\r
+ if(time()-$LastActive > $this->SBIdleTimeout)\r
+ {\r
+ $this->debug_message("*** SB Idle Timeout!");\r
+ break;\r
+ }\r
+ if(!$Joined) continue;\r
+ foreach($this->SwitchBoardMessageQueue as $Message)\r
+ {\r
+ if($Message=='') continue;\r
+ $aMessage = $this->getMessage($Message);\r
+ //CheckEmotion...\r
+ $MsnObjDefine=$this->GetMsnObjDefine($aMessage);\r
+ if($MsnObjDefine!=='')\r
+ {\r
+ $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";\r
+ $len = strlen($SendString);\r
+ $this->SB_writeln("MSG $id N $len");\r
+ $id++;\r
+ $this->SB_writedata($SendString);\r
+ $this->id++;\r
+ }\r
+ $len = strlen($aMessage);\r
+ $this->SB_writeln("MSG $id N $len");\r
+ $id++;\r
+ $this->SB_writedata($aMessage);\r
+ }\r
+ $this->SwitchBoardMessageQueue=array();\r
+ $LastActive=time();\r
+ continue;\r
+ }\r
+ $code = substr($data, 0, 3);\r
+ switch($code)\r
+ {\r
+ case 'IRO':\r
+ // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}\r
+ @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);\r
+ $this->log_message("*** $email join us");\r
+ $Joined=true;\r
+ break;\r
+ case 'BYE':\r
+ $this->log_message("*** Quit for BYE");\r
+ $SessionEnd=true;\r
+ break;\r
+ case 'USR':\r
+ // SB: <<< USR {id} OK {user} {alias}\r
+ // we don't need the data, just ignore it\r
+ // request user to join this switchboard\r
+ // SB: >>> CAL {id} {user}\r
+ $this->SB_writeln("CAL $id $user");\r
+ $id++;\r
+ break;\r
+ case 'CAL':\r
+ // SB: <<< CAL {id} RINGING {?}\r
+ // we don't need this, just ignore, and wait for other response\r
+ $this->id++;\r
+ break;\r
+ case 'JOI':\r
+ // SB: <<< JOI {user} {alias} {clientid?}\r
+ // someone join us\r
+ // we don't need the data, just ignore it\r
+ // no more user here\r
+ $Joined=true;\r
+ break;\r
+ case 'MSG':\r
+ // SB: <<< MSG {email} {alias} {len}\r
+ @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);\r
+ $len = trim($len);\r
+ $data = $this->SB_readdata($len);\r
+ $aLines = @explode("\n", $data);\r
+ $header = true;\r
+ $ignore = false;\r
+ $is_p2p = false;\r
+ $sMsg = '';\r
+ foreach ($aLines as $line)\r
+ {\r
+ $line = rtrim($line);\r
+ if ($header) {\r
+ if ($line === '') {\r
+ $header = false;\r
+ continue;\r
+ }\r
+ if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
+ // typing notification, just ignore\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ if (strncasecmp($line, 'Chunk:', 6) == 0) {\r
+ // we don't handle any split message, just ignore\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {\r
+ // p2p message, ignore it, but we need to send acknowledgement for it...\r
+ $is_p2p = true;\r
+ $p = strstr($data, "\n\n");\r
+ $sMsg = '';\r
+ if ($p === false) {\r
+ $p = strstr($data, "\r\n\r\n");\r
+ if ($p !== false)\r
+ $sMsg = substr($p, 4);\r
+ }\r
+ else\r
+ $sMsg = substr($p, 2);\r
+ break;\r
+ }\r
+ if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {\r
+ // ignore all application/x-... message\r
+ // for example:\r
+ // application/x-ms-ink => ink message\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {\r
+ // ignore all text/x-... message\r
+ // for example:\r
+ // text/x-msnmsgr-datacast => nudge, voice clip....\r
+ // text/x-mms-animemoticon => customized animemotion word\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ continue;\r
+ }\r
+ if ($sMsg !== '')\r
+ $sMsg .= "\n";\r
+ $sMsg .= $line;\r
+ }\r
+ if ($ignore)\r
+ {\r
+ $this->log_message("*** ingnore from $from_email: $line");\r
+ break;\r
+ }\r
+ if ($is_p2p)\r
+ {\r
+ // we will ignore any p2p message after sending acknowledgement\r
+ $ignore = true;\r
+ $len = strlen($sMsg);\r
+ $this->log_message("*** p2p message from $from_email, size $len");\r
+ // header = 48 bytes\r
+ // content >= 0 bytes\r
+ // footer = 4 bytes\r
+ // so it need to >= 52 bytes\r
+ /*if ($len < 52) {\r
+ $this->log_message("*** p2p: size error, less than 52!");\r
+ break;\r
+ }*/\r
+ $aDwords = @unpack("V12dword", $sMsg);\r
+ if (!is_array($aDwords)) {\r
+ $this->log_message("*** p2p: header unpack error!");\r
+ break;\r
+ }\r
+ $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));\r
+ $hdr_SessionID = $aDwords['dword1'];\r
+ $hdr_Identifier = $aDwords['dword2'];\r
+ $hdr_DataOffsetLow = $aDwords['dword3'];\r
+ $hdr_DataOffsetHigh = $aDwords['dword4'];\r
+ $hdr_TotalDataSizeLow = $aDwords['dword5'];\r
+ $hdr_TotalDataSizeHigh = $aDwords['dword6'];\r
+ $hdr_MessageLength = $aDwords['dword7'];\r
+ $hdr_Flag = $aDwords['dword8'];\r
+ $hdr_AckID = $aDwords['dword9'];\r
+ $hdr_AckUID = $aDwords['dword10'];\r
+ $hdr_AckSizeLow = $aDwords['dword11'];\r
+ $hdr_AckSizeHigh = $aDwords['dword12'];\r
+ $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");\r
+ $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");\r
+ $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");\r
+ $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");\r
+ $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");\r
+ $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");\r
+ $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");\r
+ $this->debug_message("*** p2p: header Flag = $hdr_Flag");\r
+ $this->debug_message("*** p2p: header AckID = $hdr_AckID");\r
+ $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");\r
+ $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");\r
+ $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");\r
+ if($hdr_Flag==2) {\r
+ //This is an ACK from SB ignore....\r
+ $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");\r
+ break;\r
+ }\r
+ $MsgBody=$this->linetoArray(substr($sMsg,48,-4));\r
+ $this->debug_message("*** p2p: body".print_r($MsgBody,true));\r
+ if(($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context'])))\r
+ {\r
+ while(true)\r
+ {\r
+ if($this->SB_readln()===false) break;\r
+ }\r
+ $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg,0,48)));\r
+ preg_match('/{([0-9A-F\-]*)}/i',$MsgBody['Via'],$Matches);\r
+ $BranchGUID=$Matches[1];\r
+ //it's an invite to send a display picture.\r
+ $new_id = ~$hdr_Identifier;\r
+ $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
+ $new_id,\r
+ 0, 0,\r
+ $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+ 0,\r
+ 2,\r
+ $hdr_Identifier,\r
+ $hdr_AckID,\r
+ $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);\r
+ $footer = pack("L", 0);\r
+ $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
+ $len = strlen($message);\r
+ $this->SB_writeln("MSG $id D $len");\r
+ $id++;\r
+ $this->SB_writedata($message);\r
+ $this->log_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");\r
+ $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message)); \r
+ $this->SB_readln();//Read ACK; \r
+ $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));\r
+ $new_id-=3;\r
+ //Send 200 OK message\r
+ $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);\r
+ $MessagePayload=\r
+ "MSNSLP/1.0 200 OK\r\n".\r
+ "To: <msnmsgr:".$from_email.">\r\n".\r
+ "From: <msnmsgr:".$this->user.">\r\n".\r
+ "Via: ".$MsgBody['Via']."\r\n".\r
+ "CSeq: ".($MsgBody['CSeq']+1)."\r\n".\r
+ "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
+ "Max-Forwards: 0\r\n".\r
+ "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".\r
+ "Content-Length: ".strlen($MessageContent)."\r\n\r\n".\r
+ $MessageContent;\r
+ $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
+ $hdr_TotalDataSizeHigh=0;\r
+ $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
+ $new_id,\r
+ 0, 0,\r
+ $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+ strlen($MessagePayload),\r
+ 0,\r
+ rand(),\r
+ 0,\r
+ 0,0);\r
+\r
+ $message =\r
+ "MIME-Version: 1.0\r\n".\r
+ "Content-Type: application/x-msnmsgrp2p\r\n".\r
+ "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
+ $this->SB_writeln("MSG $id D ".strlen($message));\r
+ $id++;\r
+ $this->SB_writedata($message);\r
+ $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));\r
+ $this->SB_readln();//Read ACK;\r
+ \r
+ $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));\r
+ //send Data preparation message\r
+ //send 4 null bytes as data\r
+ $hdr_TotalDataSizeLow=4;\r
+ $hdr_TotalDataSizeHigh=0;\r
+ $new_id++;\r
+ $hdr = pack("LLLLLLLLLLLL",\r
+ $MsgBody['SessionID'],\r
+ $new_id,\r
+ 0, 0,\r
+ $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+ $hdr_TotalDataSizeLow,\r
+ 0,\r
+ rand(),\r
+ 0,\r
+ 0,0);\r
+ $message =\r
+ "MIME-Version: 1.0\r\n".\r
+ "Content-Type: application/x-msnmsgrp2p\r\n".\r
+ "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L',0)."$footer";\r
+ $this->SB_writeln("MSG $id D ".strlen($message));\r
+ $id++;\r
+ $this->SB_writedata($message);\r
+ $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));\r
+ $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));\r
+ $this->SB_readln();//Read ACK;\r
+\r
+ //send Data Content..\r
+ $footer=pack('N',1);\r
+ $new_id++;\r
+ $FileSize=filesize($PictureFilePath);\r
+ if($hTitle=fopen($PictureFilePath,'rb'))\r
+ {\r
+ $Offset=0;\r
+ //$new_id++;\r
+ while(!feof($hTitle))\r
+ {\r
+ $FileContent=fread($hTitle,1024);\r
+ $FileContentSize=strlen($FileContent);\r
+ $hdr = pack("LLLLLLLLLLLL",\r
+ $MsgBody['SessionID'],\r
+ $new_id,\r
+ $Offset, 0,\r
+ $FileSize,0,\r
+ $FileContentSize,\r
+ 0x20,\r
+ rand(),\r
+ 0,\r
+ 0,0\r
+ );\r
+ $message =\r
+ "MIME-Version: 1.0\r\n".\r
+ "Content-Type: application/x-msnmsgrp2p\r\n".\r
+ "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";\r
+ $this->SB_writeln("MSG $id D ".strlen($message));\r
+ $id++;\r
+ $this->SB_writedata($message);\r
+ $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));\r
+ $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));\r
+ //$this->SB_readln();//Read ACK;\r
+ $Offset+=$FileContentSize;\r
+ }\r
+ }\r
+ //Send Bye\r
+ /*\r
+ $MessageContent="\r\n".pack("C", 0);\r
+ $MessagePayload=\r
+ "BYE MSNMSGR:MSNSLP/1.0\r\n".\r
+ "To: <msnmsgr:$from_email>\r\n".\r
+ "From: <msnmsgr:".$this->user.">\r\n".\r
+ "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n". \r
+ "CSeq: 0\r\n".\r
+ "Call-ID: ".$MsgBody['Call-ID']."\r\n".\r
+ "Max-Forwards: 0\r\n".\r
+ "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".\r
+ "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;\r
+ $footer=pack('N',0);\r
+ $hdr_TotalDataSizeLow=strlen($MessagePayload);\r
+ $hdr_TotalDataSizeHigh=0;\r
+ $new_id++;\r
+ $hdr = pack("LLLLLLLLLLLL", \r
+ 0,\r
+ $new_id,\r
+ 0, 0,\r
+ $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+ 0,\r
+ 0,\r
+ rand(),\r
+ 0,\r
+ 0,0);\r
+ $message =\r
+ "MIME-Version: 1.0\r\n".\r
+ "Content-Type: application/x-msnmsgrp2p\r\n".\r
+ "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";\r
+ $this->SB_writeln("MSG $id D ".strlen($message));\r
+ $id++;\r
+ $this->SB_writedata($message);\r
+ $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));\r
+ */\r
+ break;\r
+ }\r
+ //TODO:\r
+ //if ($hdr_Flag == 2) {\r
+ // just send ACK...\r
+ // $this->SB_writeln("ACK $id");\r
+ // break;\r
+ //}\r
+ if ($hdr_SessionID == 4) {\r
+ // ignore?\r
+ $this->debug_message("*** p2p: ignore flag 4");\r
+ break;\r
+ }\r
+ $finished = false;\r
+ if ($hdr_TotalDataSizeHigh == 0) {\r
+ // only 32 bites size\r
+ if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)\r
+ $finished = true;\r
+ }\r
+ else {\r
+ // we won't accept any file transfer\r
+ // so I think we won't get any message size need to use 64 bits\r
+ // 64 bits size here, can't count directly...\r
+ $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);\r
+ $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);\r
+ $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);\r
+ $now_size = bcadd($dataoffset, $messagelength);\r
+ if (bccomp($now_size, $totalsize) >= 0)\r
+ $finished = true;\r
+ }\r
+ if (!$finished) {\r
+ // ignore not finished split packet\r
+ $this->debug_message("*** p2p: ignore split packet, not finished");\r
+ break;\r
+ }\r
+ //$new_id = ~$hdr_Identifier;\r
+ /*\r
+ $new_id++;\r
+ $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,\r
+ $new_id,\r
+ 0, 0,\r
+ $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,\r
+ 0,\r
+ 2,\r
+ $hdr_Identifier,\r
+ $hdr_AckID,\r
+ $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);\r
+ $footer = pack("L", 0);\r
+ $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";\r
+ $len = strlen($message);\r
+ $this->SB_writeln("MSG $id D $len");\r
+ $id++;\r
+ $this->SB_writedata($message);\r
+ $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");\r
+ $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));\r
+ */\r
+ break;\r
+ }\r
+ $this->log_message("*** MSG from $from_email: $sMsg");\r
+ $this->ReceivedMessage($from_email,$sMsg,$network,false);\r
+ break;\r
+ case '217':\r
+ $this->log_message("*** User $user is offline. Try OIM.");\r
+ foreach($this->SwitchBoardMessageQueue as $Message)\r
+ $this->SendMessage($Message,"$user@Offline");\r
+ $SessionEnd=true;\r
+ break;\r
+ default:\r
+ if (is_numeric($code))\r
+ {\r
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
+ $this->debug_message("*** SB: $this->error");\r
+ $SessionEnd=true;\r
+ }\r
+ break;\r
+ }\r
+ $LastActive = time();\r
+ }\r
+ if (feof($this->SBFp))\r
+ {\r
+ // lost connection? error? try OIM later\r
+ @fclose($this->SBFp);\r
+ return false;\r
+ }\r
+ $this->SB_writeln("OUT");\r
+ @fclose($this->SBFp);\r
+ return true;\r
+ }\r
+ private function switchboard_control($ip, $port, $cki_code, $user, $Messages)\r
+ {\r
+ $this->SwitchBoardProcess=1;\r
+ $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");\r
+ $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
+ if (!$this->SBFp)\r
+ {\r
+ $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
+ return false;\r
+ }\r
+ return $this->DoSwitchBoard('Active',array('cki'=>$cki_code, 'user'=>$user,'Msg'=>$Messages));\r
+ }\r
+ private function switchboard_ring($ip, $port, $sid, $ticket,$user)\r
+ {\r
+ $this->SwitchBoardProcess=2;\r
+ $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");\r
+ $this->SBFp = @fsockopen($ip, $port, $errno, $errstr, 5);\r
+ if (!$this->SBFp)\r
+ {\r
+ $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");\r
+ return false;\r
+ }\r
+ return $this->DoSwitchBoard('Passive',array('sid'=>$sid,'user'=>$user,'ticket'=>$ticket));\r
+ }\r
+\r
+ private function sendOIM($to, $sMessage, $lockkey)\r
+ {\r
+ $XML = '<?xml version="1.0" encoding="utf-8"?>\r
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"\r
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\r
+<soap:Header>\r
+ <From memberName="'.$this->user.'"\r
+ friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="\r
+ xml:lang="zh-TW"\r
+ proxy="MSNMSGR"\r
+ xmlns="http://messenger.msn.com/ws/2004/09/oim/"\r
+ msnpVer="'.$this->protocol.'"\r
+ buildVer="'.$this->buildver.'"/>\r
+ <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
+ <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"\r
+ appid="'.$this->prod_id.'"\r
+ lockkey="'.$lockkey.'"\r
+ xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>\r
+ <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">\r
+ <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>\r
+ <MessageNumber>1</MessageNumber>\r
+ </Sequence>\r
+</soap:Header>\r
+<soap:Body>\r
+ <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>\r
+ <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: base64\r
+X-OIM-Message-Type: OfflineMessage\r
+X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}\r
+X-OIM-Sequence-Num: 1\r
+\r
+'.chunk_split(base64_encode($sMessage)).'\r
+ </Content>\r
+</soap:Body>\r
+</soap:Envelope>';\r
+\r
+ $header_array = array(\r
+ 'SOAPAction: '.$this->oim_send_soap,\r
+ 'Content-Type: text/xml',\r
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'\r
+ );\r
+\r
+ $this->debug_message("*** URL: $this->oim_send_url");\r
+ $this->debug_message("*** Sending SOAP:\n$XML");\r
+ $curl = curl_init();\r
+ curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);\r
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);\r
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\r
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);\r
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);\r
+ curl_setopt($curl, CURLOPT_POST, 1);\r
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);\r
+ $data = curl_exec($curl);\r
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r
+ curl_close($curl);\r
+ $this->debug_message("*** Get Result:\n$data");\r
+\r
+ if ($http_code == 200) {\r
+ $this->debug_message("*** OIM sent for $to");\r
+ return true;\r
+ }\r
+\r
+ $challenge = false;\r
+ $auth_policy = false;\r
+ // the lockkey is invalid, authenticated fail, we need challenge it again\r
+ // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>\r
+ preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);\r
+ if (count($matches) != 0) {\r
+ // yes, we get new LockKeyChallenge\r
+ $challenge = $matches[2];\r
+ $this->debug_message("*** OIM need new challenge ($challenge) for $to");\r
+ }\r
+ // auth policy error\r
+ // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>\r
+ preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);\r
+ if (count($matches) != 0) {\r
+ $auth_policy = $matches[2];\r
+ $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");\r
+ }\r
+ if ($auth_policy === false && $challenge === false) {\r
+ //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>\r
+ preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);\r
+ if (count($matches) == 0) {\r
+ // no error, we assume the OIM is sent\r
+ $this->debug_message("*** OIM sent for $to");\r
+ return true;\r
+ }\r
+ $err_code = $matches[2];\r
+ //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>\r
+ preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);\r
+ if (count($matches) > 0)\r
+ $err_msg = $matches[1];\r
+ else\r
+ $err_msg = '';\r
+ $this->debug_message("*** OIM failed for $to");\r
+ $this->debug_message("*** OIM Error code: $err_code");\r
+ $this->debug_message("*** OIM Error Message: $err_msg");\r
+ return false;\r
+ }\r
+ return array('challenge' => $challenge, 'auth_policy' => $auth_policy);\r
+ }\r
+\r
+ // read data for specified size\r
+ private function ns_readdata($size) {\r
+ $data = '';\r
+ $count = 0;\r
+ while (!feof($this->NSfp)) {\r
+ $buf = @fread($this->NSfp, $size - $count);\r
+ $data .= $buf;\r
+ $count += strlen($buf);\r
+ if ($count >= $size) break;\r
+ }\r
+ $this->debug_message("NS: data ($size/$count) <<<\n$data");\r
+ return $data;\r
+ }\r
+\r
+ // read one line\r
+ private function ns_readln() {\r
+ $data = @fgets($this->NSfp, 4096);\r
+ if ($data !== false) {\r
+ $data = trim($data);\r
+ $this->debug_message("NS: <<< $data");\r
+ }\r
+ return $data;\r
+ }\r
+\r
+ // write to server, append \r\n, also increase id\r
+ private function ns_writeln($data) {\r
+ @fwrite($this->NSfp, $data."\r\n");\r
+ $this->debug_message("NS: >>> $data");\r
+ $this->id++;\r
+ return;\r
+ }\r
+\r
+ // write data to server\r
+ private function ns_writedata($data) {\r
+ @fwrite($this->NSfp, $data);\r
+ $this->debug_message("NS: >>> $data");\r
+ return;\r
+ }\r
+\r
+ // read data for specified size for SB\r
+ private function sb_readdata($size) {\r
+ $data = '';\r
+ $count = 0;\r
+ while (!feof($this->SBFp)) {\r
+ $buf = @fread($this->SBFp, $size - $count);\r
+ $data .= $buf;\r
+ $count += strlen($buf);\r
+ if ($count >= $size) break;\r
+ }\r
+ $this->debug_message("SB: data ($size/$count) <<<\n$data");\r
+ return $data;\r
+ }\r
+\r
+ // read one line for SB\r
+ private function sb_readln() {\r
+ $data = @fgets($this->SBFp, 4096);\r
+ if ($data !== false) {\r
+ $data = trim($data);\r
+ $this->debug_message("SB: <<< $data");\r
+ }\r
+ return $data;\r
+ }\r
+\r
+ // write to server for SB, append \r\n, also increase id\r
+ // switchboard server only accept \r\n, it will lost connection if just \n only\r
+ private function sb_writeln($data) {\r
+ @fwrite($this->SBFp, $data."\r\n");\r
+ $this->debug_message("SB: >>> $data");\r
+ $this->id++;\r
+ return;\r
+ }\r
+\r
+ // write data to server\r
+ private function sb_writedata($data) {\r
+ @fwrite($this->SBFp, $data);\r
+ $this->debug_message("SB: >>> $data");\r
+ return;\r
+ }\r
+\r
+ // show debug information\r
+ function debug_message($str) {\r
+ if (!$this->debug) return;\r
+ if($this->debug===STDOUT) echo $str."\n";\r
+ /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';\r
+ $fp = fopen($fname, 'at');\r
+ if ($fp) {\r
+ fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");\r
+ fclose($fp);\r
+ return;\r
+ }*/\r
+ // still show debug information, if we can't open log_file\r
+ echo $str."\n";\r
+ return;\r
+ }\r
+\r
+ function dump_binary($str) {\r
+ $buf = '';\r
+ $a_str = '';\r
+ $h_str = '';\r
+ $len = strlen($str);\r
+ for ($i = 0; $i < $len; $i++) {\r
+ if (($i % 16) == 0) {\r
+ if ($buf !== '') {\r
+ $buf .= "$h_str $a_str\n";\r
+ }\r
+ $buf .= sprintf("%04X:", $i);\r
+ $a_str = '';\r
+ $h_str = '';\r
+ }\r
+ $ch = ord($str[$i]);\r
+ if ($ch < 32)\r
+ $a_str .= '.';\r
+ else\r
+ $a_str .= chr($ch);\r
+ $h_str .= sprintf(" %02X", $ch);\r
+ }\r
+ if ($h_str !== '')\r
+ $buf .= "$h_str $a_str\n";\r
+ return $buf;\r
+ }\r
+\r
+ // write log\r
+ function log_message($str) {\r
+ /*$fname = MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';\r
+ $fp = fopen($fname, 'at');\r
+ if ($fp) {\r
+ fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");\r
+ fclose($fp);\r
+ }*/\r
+ $this->debug_message($str);\r
+ return;\r
+ }\r
+ /**\r
+ *\r
+ * @param $FilePath 圖檔路徑\r
+ * @param $Type 檔案類型 3=>大頭貼,2表情圖案 \r
+ * @return array\r
+ */\r
+ private function MsnObj($FilePath,$Type=3)\r
+ {\r
+ if(!($FileSize=filesize($FilePath))) return '';\r
+ $Location=md5($FilePath);\r
+ $Friendly=md5($FilePath.$Type);\r
+ if(isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];\r
+ $sha1d=base64_encode(sha1(file_get_contents($FilePath),true));\r
+ $sha1c=base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));\r
+ $this->MsnObjArray[$Location]=$FilePath;\r
+ $MsnObj='<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';\r
+ $this->MsnObjMap[$Location]=$MsnObj;\r
+ $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");\r
+ return $MsnObj;\r
+ }\r
+ private function linetoArray($lines) {\r
+ $lines=str_replace("\r",'',$lines);\r
+ $lines=explode("\n",$lines);\r
+ foreach($lines as $line) {\r
+ if(!isset($line{3})) continue;\r
+ list($Key,$Val)=explode(':',$line);\r
+ $Data[trim($Key)]=trim($Val);\r
+ }\r
+ return $Data;\r
+ }\r
+ private function GetPictureFilePath($Context)\r
+ {\r
+ $MsnObj=base64_decode($Context);\r
+ if(preg_match('/location="(.*?)"/i',$MsnObj,$Match))\r
+ $location=$Match[1];\r
+ $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");\r
+ if($location&&(isset($this->MsnObjArray[$location])))\r
+ return $this->MsnObjArray[$location];\r
+ return false;\r
+ }\r
+ private function GetMsnObjDefine($Message)\r
+ {\r
+ $DefineString='';\r
+ if(is_array($this->Emotions))\r
+ foreach($this->Emotions as $Pattern => $FilePath)\r
+ {\r
+ if(strpos($Message,$Pattern)!==false)\r
+ $DefineString.="$Pattern\t".$this->MsnObj($FilePath,2)."\t";\r
+ }\r
+ return $DefineString;\r
+ }\r
+ /**\r
+ * Receive Message Overload Function\r
+ * @param $Sender\r
+ * @param $Message\r
+ * @param $Network 1 => msn , 32 =>yahoo\r
+ * @param $IsOIM\r
+ * @return unknown_type\r
+ */\r
+ protected function ReceivedMessage($Sender,$Message,$Network,$IsOIM=false){}\r
+ /**\r
+ * Remove Us From Member List Overload Function\r
+ * @param $User\r
+ * @param $Message\r
+ * @param $Network 1 => msn , 32 =>yahoo\r
+ * @return unknown_type\r
+ */\r
+ protected function RemoveUsFromMemberList($User,$Network){}\r
+ /**\r
+ * Add Us to Member List Overload Function\r
+ * @param $User\r
+ * @param $Message\r
+ * @param $Network 1 => msn , 32 =>yahoo\r
+ * @return unknown_type\r
+ */\r
+ protected function AddUsToMemberList($User,$Network){}\r
+ \r
+ public function signon() {\r
+ $this->log_message("*** try to connect to MSN network");\r
+ while(!$this->connect($this->user, $this->password))\r
+ {\r
+ $this->log_message("!!! Can't connect to server: $this->error");\r
+ if(!$this->NSRetryWait($this->retry_wait)) return;\r
+ }\r
+ $this->UpdateContacts();\r
+ $this->LastPing=time();\r
+ $this->log_message("*** connected, wait for command");\r
+ $start_tm = time();\r
+ $ping_tm = time();\r
+ stream_set_timeout($this->NSfp, $this->NSStreamTimeout);\r
+ $this->aContactList = $this->getMembershipList(true);\r
+ if ($this->update_pending) {\r
+ if (is_array($this->aContactList)) {\r
+ $pending = 'Pending';\r
+ foreach ($this->aContactList as $u_domain => $aUserList) {\r
+ foreach ($aUserList as $u_name => $aNetworks) {\r
+ foreach ($aNetworks as $network => $aData) {\r
+ if (isset($aData[$pending])) {\r
+ // pending list\r
+ $cnt = 0;\r
+ foreach (array('Allow', 'Reverse') as $list) {\r
+ if (isset($aData[$list]))\r
+ $cnt++;\r
+ else {\r
+ if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {\r
+ $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
+ $cnt++;\r
+ }\r
+ }\r
+ }\r
+ if ($cnt >= 2) {\r
+ $id = $aData[$pending];\r
+ // we can delete it from pending now\r
+ if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))\r
+ unset($this->aContactList[$u_domain][$u_name][$network][$pending]);\r
+ }\r
+ }\r
+ else {\r
+ // sync list\r
+ foreach (array('Allow', 'Reverse') as $list) {\r
+ if (!isset($aData[$list])) {\r
+ if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
+ $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ $n = 0;\r
+ $sList = '';\r
+ $len = 0;\r
+ if (is_array($this->aContactList)) {\r
+ foreach ($this->aContactList as $u_domain => $aUserList) {\r
+ $str = '<d n="'.$u_domain.'">';\r
+ $len += strlen($str);\r
+ if ($len > 7400) {\r
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+ $n++;\r
+ $sList = '';\r
+ $len = strlen($str);\r
+ }\r
+ $sList .= $str;\r
+ foreach ($aUserList as $u_name => $aNetworks) {\r
+ foreach ($aNetworks as $network => $status) {\r
+ $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';\r
+ $len += strlen($str);\r
+ // max: 7500, but <ml l="1"></d></ml> is 19,\r
+ // so we use 7475\r
+ if ($len > 7475) {\r
+ $sList .= '</d>';\r
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+ $n++;\r
+ $sList = '<d n="'.$u_domain.'">'.$str;\r
+ $len = strlen($sList);\r
+ }\r
+ else\r
+ $sList .= $str;\r
+ }\r
+ }\r
+ $sList .= '</d>';\r
+ }\r
+ }\r
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';\r
+ // NS: >>> BLP {id} BL\r
+ $this->ns_writeln("BLP $this->id BL");\r
+ foreach ($aADL as $str) {\r
+ $len = strlen($str);\r
+ // NS: >>> ADL {id} {size}\r
+ $this->ns_writeln("ADL $this->id $len");\r
+ $this->ns_writedata($str);\r
+ }\r
+ // NS: >>> PRP {id} MFN name\r
+ if ($this->alias == '') $this->alias = $user;\r
+ $aliasname = rawurlencode($this->alias);\r
+ $this->ns_writeln("PRP $this->id MFN $aliasname");\r
+ //設定個人大頭貼\r
+ //$MsnObj=$this->PhotoStckObj();\r
+ // NS: >>> CHG {id} {status} {clientid} {msnobj}\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
+ if($this->PhotoStickerFile!==false)\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+ // NS: >>> UUX {id} length\r
+ $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';\r
+ $len = strlen($str);\r
+ $this->ns_writeln("UUX $this->id $len");\r
+ $this->ns_writedata($str);\r
+ }\r
+ \r
+ public function NSreceive() {\r
+ $this->log_message("*** startup ***");\r
+ \r
+ $aADL = array();\r
+ \r
+ // Sign in again if not signed in or socket failed\r
+ if (!is_resource($this->NSfp) || feof($this->NSfp)) {\r
+ $this->signon();\r
+ }\r
+ \r
+ $data = $this->ns_readln();\r
+ /*if($data===false)\r
+ {\r
+ //If No NS Message Process SendMessageFileQueue\r
+ if (time()-$this->LastPing > $this->ping_wait)\r
+ {\r
+ // NS: >>> PNG\r
+ $this->ns_writeln("PNG");\r
+ $this->LastPing = time();\r
+ }\r
+ if(count($this->ChildProcess)<$this->MAXChildProcess)\r
+ {\r
+ $Index=0;\r
+ foreach($this->MessageQueue as $User => $Message)\r
+ {\r
+ if(!trim($User)) continue;\r
+ if($Inxdex>=$this->MAXChildProcess-count($this->ChildProcess)) break;\r
+ if((!$Message['XFRSent'])||($Message['XFRSent']&&(time()-$this->MessageQueue[$User]['ReqTime']>$this->ReqSBXFRTimeout)))\r
+ {\r
+ $this->MessageQueue[$User]['XFRSent']=true;\r
+ $this->MessageQueue[$User]['ReqTime']=time();\r
+ $this->log_message("*** Request SB for $User");\r
+ $this->ns_writeln("XFR $this->id SB");\r
+ $Index++;\r
+ }\r
+ }\r
+ }\r
+ if($this->ProcessSendMessageFileQueue()) continue;\r
+ break;\r
+ }*/\r
+ switch (substr($data,0,3))\r
+ {\r
+ case 'SBS':\r
+ // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us\r
+ // NS: <<< SBS 0 null\r
+ break;\r
+\r
+ case 'RFS':\r
+ // FIXME:\r
+ // NS: <<< RFS ???\r
+ // refresh ADL, so we re-send it again\r
+ if (is_array($aADL)) {\r
+ foreach ($aADL as $str) {\r
+ $len = strlen($str);\r
+ // NS: >>> ADL {id} {size}\r
+ $this->ns_writeln("ADL $this->id $len");\r
+ $this->ns_writedata($str);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case 'LST':\r
+ // NS: <<< LST {email} {alias} 11 0\r
+ @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);\r
+ @list($u_name, $u_domain) = @explode('@', $email);\r
+ if (!isset($this->aContactList[$u_domain][$u_name][1])) {\r
+ $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';\r
+ $this->log_message("*** add to our contact list: $u_name@$u_domain");\r
+ }\r
+ break;\r
+\r
+ case 'ADL':\r
+ // randomly, we get ADL command, someome add us to their contact list for MSNP15\r
+ // NS: <<< ADL 0 {size}\r
+ @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0)\r
+ {\r
+ $data = $this->ns_readdata($size);\r
+ preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
+ if (is_array($matches) && count($matches) > 0)\r
+ {\r
+ $u_domain = $matches[1];\r
+ $u_name = $matches[2];\r
+ $network = $matches[4];\r
+ if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
+ $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");\r
+ else\r
+ {\r
+ $re_login = false;\r
+ $cnt = 0;\r
+ foreach (array('Allow', 'Reverse') as $list)\r
+ {\r
+ if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
+ {\r
+ if ($re_login) {\r
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+ continue;\r
+ }\r
+ $aTickets = $this->get_passport_ticket();\r
+ if (!$aTickets || !is_array($aTickets)) {\r
+ // failed to login? ignore it\r
+ $this->log_message("*** can't re-login, something wrong here");\r
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+ continue;\r
+ }\r
+ $re_login = true;\r
+ $this->ticket = $aTickets;\r
+ $this->log_message("**** get new ticket, try it again");\r
+ if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list))\r
+ {\r
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");\r
+ continue;\r
+ }\r
+ }\r
+ $this->aContactList[$u_domain][$u_name][$network][$list] = false;\r
+ $cnt++;\r
+ }\r
+ $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");\r
+ }\r
+ $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';\r
+ $len = strlen($str);\r
+ }\r
+ else\r
+ $this->log_message("*** someone add us to their list: $data");\r
+ $this->AddUsToMemberList($u_name.'@'.$u_domain, $network);\r
+ }\r
+ break;\r
+\r
+ case 'RML':\r
+ // randomly, we get RML command, someome remove us to their contact list for MSNP15\r
+ // NS: <<< RML 0 {size}\r
+ @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0)\r
+ {\r
+ $data = $this->ns_readdata($size);\r
+ preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);\r
+ if (is_array($matches) && count($matches) > 0)\r
+ {\r
+ $u_domain = $matches[1];\r
+ $u_name = $matches[2];\r
+ $network = $matches[4];\r
+ if (isset($this->aContactList[$u_domain][$u_name][$network]))\r
+ {\r
+ $aData = $this->aContactList[$u_domain][$u_name][$network];\r
+ foreach ($aData as $list => $id)\r
+ $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);\r
+ unset($this->aContactList[$u_domain][$u_name][$network]);\r
+ $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");\r
+ }\r
+ else\r
+ $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");\r
+ $this->RemoveUsFromMemberList($u_name.'@'.$u_domain, $network);\r
+ }\r
+ else\r
+ $this->log_message("*** someone remove us from their list: $data");\r
+ }\r
+ break;\r
+\r
+ case 'MSG':\r
+ // randomly, we get MSG notification from server\r
+ // NS: <<< MSG Hotmail Hotmail {size}\r
+ @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0) {\r
+ $data = $this->ns_readdata($size);\r
+ $aLines = @explode("\n", $data);\r
+ $header = true;\r
+ $ignore = false;\r
+ $maildata = '';\r
+ foreach ($aLines as $line) {\r
+ $line = rtrim($line);\r
+ if ($header) {\r
+ if ($line === '') {\r
+ $header = false;\r
+ continue;\r
+ }\r
+ if (strncasecmp($line, 'Content-Type:', 13) == 0) {\r
+ if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&\r
+ strpos($line, 'text/x-msmsgsoimnotification') === false) {\r
+ // we just need text/x-msmsgsinitialmdatanotification\r
+ // or text/x-msmsgsoimnotification\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ }\r
+ continue;\r
+ }\r
+ if (strncasecmp($line, 'Mail-Data:', 10) == 0) {\r
+ $maildata = trim(substr($line, 10));\r
+ break;\r
+ }\r
+ }\r
+ if ($ignore) {\r
+ $this->log_message("*** ingnore MSG for: $line");\r
+ break;\r
+ }\r
+ if ($maildata == '') {\r
+ $this->log_message("*** ingnore MSG not for OIM");\r
+ break;\r
+ }\r
+ $re_login = false;\r
+ if (strcasecmp($maildata, 'too-large') == 0) {\r
+ $this->log_message("*** large mail-data, need to get the data via SOAP");\r
+ $maildata = $this->getOIM_maildata();\r
+ if ($maildata === false) {\r
+ $this->log_message("*** can't get mail-data via SOAP");\r
+ // maybe we need to re-login again\r
+ $aTickets = $this->get_passport_ticket();\r
+ if (!$aTickets || !is_array($aTickets)) {\r
+ // failed to login? ignore it\r
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+ break;\r
+ }\r
+ $re_login = true;\r
+ $this->ticket = $aTickets;\r
+ $this->log_message("**** get new ticket, try it again");\r
+ $maildata = $this->getOIM_maildata();\r
+ if ($maildata === false) {\r
+ $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ // could be a lots of <M>...</M>, so we can't use preg_match here\r
+ $p = $maildata;\r
+ $aOIMs = array();\r
+ while (1) {\r
+ $start = strpos($p, '<M>');\r
+ $end = strpos($p, '</M>');\r
+ if ($start === false || $end === false || $start > $end) break;\r
+ $end += 4;\r
+ $sOIM = substr($p, $start, $end - $start);\r
+ $aOIMs[] = $sOIM;\r
+ $p = substr($p, $end);\r
+ }\r
+ if (count($aOIMs) == 0) {\r
+ $this->log_message("*** ingnore empty OIM");\r
+ break;\r
+ }\r
+ foreach ($aOIMs as $maildata) {\r
+ // T: 11 for MSN, 13 for Yahoo\r
+ // S: 6 for MSN, 7 for Yahoo\r
+ // RT: the datetime received by server\r
+ // RS: already read or not\r
+ // SZ: size of message\r
+ // E: sender\r
+ // I: msgid\r
+ // F: always 00000000-0000-0000-0000-000000000009\r
+ // N: sender alias\r
+ preg_match('#<T>(.*)</T>#', $maildata, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** ingnore OIM maildata without <T>type</T>");\r
+ continue;\r
+ }\r
+ $oim_type = $matches[1];\r
+ if ($oim_type = 13)\r
+ $network = 32;\r
+ else\r
+ $network = 1;\r
+ preg_match('#<E>(.*)</E>#', $maildata, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** ingnore OIM maildata without <E>sender</E>");\r
+ continue;\r
+ }\r
+ $oim_sender = $matches[1];\r
+ preg_match('#<I>(.*)</I>#', $maildata, $matches);\r
+ if (count($matches) == 0) {\r
+ $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");\r
+ continue;\r
+ }\r
+ $oim_msgid = $matches[1];\r
+ preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);\r
+ $oim_size = (count($matches) == 0) ? 0 : $matches[1];\r
+ preg_match('#<RT>(.*)</RT>#', $maildata, $matches);\r
+ $oim_time = (count($matches) == 0) ? 0 : $matches[1];\r
+ $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");\r
+ $sMsg = $this->getOIM_message($oim_msgid);\r
+ if ($sMsg === false) {\r
+ $this->log_message("*** can't get OIM, msgid = $oim_msgid");\r
+ if ($re_login) {\r
+ $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
+ continue;\r
+ }\r
+ $aTickets = $this->get_passport_ticket();\r
+ if (!$aTickets || !is_array($aTickets)) {\r
+ // failed to login? ignore it\r
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");\r
+ continue;\r
+ }\r
+ $re_login = true;\r
+ $this->ticket = $aTickets;\r
+ $this->log_message("**** get new ticket, try it again");\r
+ $sMsg = $this->getOIM_message($oim_msgid);\r
+ if ($sMsg === false) {\r
+ $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");\r
+ continue;\r
+ }\r
+ }\r
+ $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");\r
+\r
+ //$this->ReceivedMessage($oim_sender,$sMsg,$network,true);\r
+ $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));\r
+ }\r
+ }\r
+ break;\r
+\r
+ case 'UBM':\r
+ // randomly, we get UBM, this is the message from other network, like Yahoo!\r
+ // NS: <<< UBM {email} $network $type {size}\r
+ @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);\r
+ if (is_numeric($size) && $size > 0)\r
+ {\r
+ $data = $this->ns_readdata($size);\r
+ $aLines = @explode("\n", $data);\r
+ $header = true;\r
+ $ignore = false;\r
+ $sMsg = '';\r
+ foreach ($aLines as $line) {\r
+ $line = rtrim($line);\r
+ if ($header) {\r
+ if ($line === '') {\r
+ $header = false;\r
+ continue;\r
+ }\r
+ if (strncasecmp($line, 'TypingUser:', 11) == 0) {\r
+ $ignore = true;\r
+ break;\r
+ }\r
+ continue;\r
+ }\r
+ $aSubLines = @explode("\r", $line);\r
+ foreach ($aSubLines as $str) {\r
+ if ($sMsg !== '')\r
+ $sMsg .= "\n";\r
+ $sMsg .= $str;\r
+ }\r
+ }\r
+ if($ignore)\r
+ {\r
+ $this->log_message("*** ingnore from $from_email: $line");\r
+ break;\r
+ }\r
+ $this->log_message("*** MSG from $from_email (network: $network): $sMsg");\r
+ //$this->ReceivedMessage($from_email,$sMsg,$network,false);\r
+ $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));\r
+ }\r
+ break;\r
+\r
+ case 'UBX':\r
+ // randomly, we get UBX notification from server\r
+ // NS: <<< UBX email {network} {size}\r
+ @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);\r
+ // we don't need the notification data, so just ignore it\r
+ if (is_numeric($size) && $size > 0)\r
+ $this->ns_readdata($size);\r
+ break;\r
+\r
+ case 'CHL':\r
+ // randomly, we'll get challenge from server\r
+ // NS: <<< CHL 0 {code}\r
+ @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);\r
+ $fingerprint = $this->getChallenge($chl_code);\r
+ // NS: >>> QRY {id} {product_id} 32\r
+ // NS: >>> fingerprint\r
+ $this->ns_writeln("QRY $this->id $this->prod_id 32");\r
+ $this->ns_writedata($fingerprint);\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid"); \r
+ if($this->PhotoStickerFile!==false)\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+ break;\r
+ case 'CHG':\r
+ // NS: <<< CHG {id} {status} {code}\r
+ // ignore it\r
+ // change our status to online first\r
+ break;\r
+\r
+ case 'XFR':\r
+ // sometimes, NS will redirect to another NS\r
+ // MSNP9\r
+ // NS: <<< XFR {id} NS {server} 0 {server}\r
+ // MSNP15\r
+ // NS: <<< XFR {id} NS {server} U D\r
+ // for normal switchboard XFR\r
+ // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0\r
+ @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);\r
+ @list($ip, $port) = @explode(':', $server);\r
+ if ($server_type != 'SB') {\r
+ // maybe exit?\r
+ // this connection will close after XFR\r
+ $this->NSLogout();\r
+ continue;\r
+ }\r
+ if(count($this->MessageQueue))\r
+ {\r
+ foreach($this->MessageQueue as $User => $Message)\r
+ {\r
+ //$this->ChildProcess[$ChildPid]\r
+ $this->log_message("*** XFR SB $User");\r
+ $pid=pcntl_fork();\r
+ if($pid)\r
+ {\r
+ //Parrent Process\r
+ $this->ChildProcess[$pid]=$User;\r
+ break;\r
+ }\r
+ elseif($pid==-1)\r
+ {\r
+ $this->log_message("*** Fork Error $User");\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ //Child Process\r
+ $this->log_message("*** Child Process Start for $User");\r
+ unset($Message['XFRSent']);\r
+ unset($Message['ReqTime']);\r
+ $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);\r
+ if ($bSBresult === false)\r
+ {\r
+ // error for switchboard\r
+ $this->log_message("!!! error for sending message to ".$User);\r
+ }\r
+ die;\r
+ }\r
+ }\r
+ unset($this->MessageQueue[$User]);\r
+ }\r
+ /*\r
+ $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);\r
+ if ($bSBresult === false) {\r
+ // error for switchboard\r
+ $this->log_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);\r
+ $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];\r
+ }*/\r
+ break;\r
+ case 'QNG':\r
+ // NS: <<< QNG {time}\r
+ @list(/* QNG */, $this->ping_wait) = @explode(' ', $data);\r
+ if ($this->ping_wait == 0) $this->ping_wait = 50;\r
+ //if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;\r
+ //Mod by Ricky Set Online\r
+ break;\r
+\r
+ case 'RNG':\r
+ if($this->PhotoStickerFile!==false)\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));\r
+ else\r
+ $this->ns_writeln("CHG $this->id NLN $this->clientid");\r
+ // someone is trying to talk to us\r
+ // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0\r
+ $this->log_message("NS: <<< RNG $data");\r
+ @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);\r
+ @list($sb_ip, $sb_port) = @explode(':', $server);\r
+ $this->log_message("*** RING from $email, $sb_ip:$sb_port");\r
+ $this->addContact($email,1,$email, true);\r
+ $pid=pcntl_fork();\r
+ if($pid)\r
+ {\r
+ //Parrent Process\r
+ $this->ChildProcess[$pid]='RNG';\r
+ break;\r
+ }\r
+ elseif($pid==-1)\r
+ {\r
+ $this->log_message("*** Fork Error $User");\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ //Child Process\r
+ $this->log_message("*** Ring Child Process Start for $User");\r
+ $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket,$email);\r
+ die;\r
+ }\r
+ break;\r
+ case 'OUT':\r
+ // force logout from NS\r
+ // NS: <<< OUT xxx\r
+ $this->log_message("*** LOGOUT from NS");\r
+ return $this->NsLogout();\r
+\r
+ default:\r
+ $code = substr($data,0,3);\r
+ if (is_numeric($code)) {\r
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";\r
+ $this->debug_message("*** NS: $this->error");\r
+\r
+ return $this->NsLogout();\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ \r
+ public function SendMessage($Message, $To) {\r
+ if(!is_array($To))\r
+ $To=array($To);\r
+ $Receiver='';\r
+ foreach($To as $Email)\r
+ {\r
+ list($name,$host,$network)=explode('@',$Email);\r
+ $network=$network==''?1:$network;\r
+ if($network==1 && isset($this->switchBoardSessions[$Email]) ) {\r
+ $this->debug_message("*** SendMessage to $Receiver use SB message queue.");\r
+ array_push($this->SwitchBoardMessageQueue,$Message);\r
+ continue;\r
+ }\r
+ $Receiver.="$name@$host@$network,";\r
+ }\r
+ if($Receiver=='') return;\r
+ $Receiver=substr($Receiver,0,-1);\r
+ $this->debug_message("*** SendMessage to $Receiver use File queue.");\r
+ file_put_contents($FileName,"TO: $Receiver\n$Message\n");\r
+ }\r
+ \r
+ public function getNSSocket() {\r
+ return $this->NSfp;\r
+ }\r
+ \r
+ public function getSBSocket() {\r
+ return $this->SBfp;\r
+ }\r
+ \r
+ public function getSockets() {\r
+ return array($this->NSfp, $this->SBfp);\r
+ }\r
+ \r
+ /**\r
+ * Calls User Handler\r
+ *\r
+ * Calls registered handler for a specific event.\r
+ * \r
+ * @param String $event Command (event) name (Rvous etc)\r
+ * @param String $data Raw message from server\r
+ * @see registerHandler\r
+ * @return void\r
+ */\r
+ private function callHandler($event, $data) {\r
+ if (isset($this->myEventHandlers[$event])) {\r
+ call_user_func($this->myEventHandlers[$event], $data);\r
+ } else {\r
+ $this->noHandler($data);\r
+ }\r
+ }\r
+ \r
+ /** \r
+ * Registers a user handler\r
+ * \r
+ * Handler List\r
+ * IMIn\r
+ *\r
+ * @param String $event Event name\r
+ * @param String $handler User function to call\r
+ * @see callHandler\r
+ * @return boolean Returns true if successful\r
+ */\r
+ public function registerHandler($event, $handler) {\r
+ if (is_callable($handler)) {\r
+ $this->myEventHandlers[$event] = $handler;\r
+ return true;\r
+ } else {\r
+ return false;\r
+ }\r
+ }\r
+}\r